简体   繁体   中英

How can I use Redux to only update one instance of a component?

I'm trying to use Redux to update my Card Component to disable and change colors on click. Redux dispatches the action fine, but it updates all Cards not just the one that was clicked. Each Card has an object associated with it that hold the word and a value. The value is the className I want to use to change the color when clicked

Component

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);

Action

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

Reducer

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;
    }
}```

Your mapStateToProps selects a string , but said string changes on any updateClass and that causes all your cards to update, because the selection of state.game.cardClass produces a different value, which triggers a new render for the connected component.

Maybe what you want, is something that identifies the selection, ie an id for each card, and select with that id in the mapStateToProps to avoid reading the change, because what's happening right now is the following:

Card A[className="card A"] == after dispatch ==> mapStateToProps => [className="card B"]
Card B[className="card A"] => dispatch('B') => mapStateToProps => [className="card B"]

B is updating the state of both A and B, and that's why the extra render occurs

All of your card components are consuming the same cardClass field in the state. When you modify it in this line:

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

All cards that are consuming this field have their classes updated. The same occurs to the isDisable field.

You need to create one object for each card in your state. Here is my implementation (was not tested):

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM