如何使用 Redux 只更新组件的一个实例?

Posted

技术标签:

【中文标题】如何使用 Redux 只更新组件的一个实例?【英文标题】:How can I use Redux to only update one instance of a component? 【发布时间】:2021-01-16 14:37:25 【问题描述】:

我正在尝试使用 Redux 来更新我的卡片组件以在点击时禁用和更改颜色。 Redux 可以很好地调度操作,但它会更新所有卡片,而不仅仅是被点击的卡片。每张卡片都有一个与之关联的对象,其中包含单词和值。该值是我想在单击时更改颜色的className

组件

const Card = ( wordObj, updateClass, isDisabled, cardClass ) => 
    
    const showColor = (e) => 
        updateClass(wordObj);
        console.log(cardClass)
    ;
    return (
        <button
            onClick=(e) => showColor()
            disabled=isDisabled
            className=cardClass>
            wordObj.word
        </button>
    );
;


const mapStateToProps = (state) => (
    cardClass: state.game.cardClass,
);
export default connect(mapStateToProps,  updateClass )(Card);

动作

export const updateClass = (obj) => (dispatch) => 
    console.log(obj)
    dispatch(
        type: UPDATE_CARD,
        payload: obj,
    );
;

减速器

const initialState = 
    words: [],
    cardClass: 'card',
    isDisabled: false,
;

export default function (state = initialState, action) 
    const  type, payload  = action;
    switch (type) 
        case SET_WORDS: 
            return 
                ...state,
                words: payload,
            ;
           

        case UPDATE_CARD:
            return 
                ...state,
                isDisabled: true,
                cardClass: ['card', payload.value].join(' '),
            ;

        default:
            return state;
    
```

【问题讨论】:

【参考方案1】:

您的所有卡片组件都在使用状态中的相同 cardClass 字段。当你在这一行修改它时:

cardClass: ['card', payload.value].join(' ')

所有使用此字段的卡片都会更新其类别。 isDisable 字段也是如此。

您需要为您所在州的每张卡片创建一个对象。这是我的实现(未经测试):

const initialState = 
    cards: []
;

export default function (state = initialState, action) 
    const  type, payload  = action;
    switch (type) 
        // create a card object for each word
        case SET_WORDS: 
            return 
                ...state,
                cards: payload.map(word => 
                    return  word: word, cardClass: "card", isDisabled: false 
                )
            ;
           

        case UPDATE_CARD:
            // here i'm using the wordObj.word passed as payload
            // to identify the card (i recommend to use an id field) 
            const cardIndex = state.cards.findIndex(card => card.word === payload.word);
            // get the current card
            const card = state.cards[cardIndex];
            // create an updated card object obeying the immutability principle
            const updatedCard =  ...card, isDisabled: true, cardClass: ['card', payload.value].join(' '), 
            return 
                ...state,
               cards: [
                   ...state.cards.slice(0, cardIndex), // cards before
                   updatedCard,
                   ...state.cards.slice(cardIndex + 1) // cards after
               ]
                
            ;

        default:
            return state;
    

【讨论】:

【参考方案2】:

您的mapStateToProps 选择了一个string,但是任何updateClass 上的字符串都发生了变化,这会导致您的所有卡片都更新,因为选择state.game.cardClass 会产生不同的值,这会触发新的渲染连接组件。

也许你想要的是识别选择的东西,即每张卡的 id,并在 mapStateToProps 中使用该 id 选择以避免读取更改,因为现在发生的情况如下:

卡 A[className="card A"] == 调度后 ==> mapStateToProps => [className="card B"] Card B[className="card A"] => dispatch('B') => mapStateToProps => [className="card B"]

B 正在更新 A 和 B 的状态,这就是发生 extra 渲染的原因

【讨论】:

以上是关于如何使用 Redux 只更新组件的一个实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React hooks 和 redux 中只获取一次且不再获取数据

如何使用从子组件传递给组件的反应变量更新 redux 状态

如何在 redux 状态更新时自动刷新组件

在 React 中,如何在所有组件中拥有一个自定义钩子实例?

在 react js 功能组件中调度操作后如何立即在 redux store 中使用更新的值

如何响应 redux 中的状态变化并派发另一个动作?