Search code examples
reactjsredux

React-redux: Why is functional component rendering after updating redux state?


Why does the component dispatching an action for updating react-redux state rerenders after store state changes? Shouldn't the rerender occur only when props or component state changes?

const BoxContainer=memo(({props})=>{

    
    const dispatch = useDispatch();

    //Setting action with dispatch with useCallBack hook for safe side

    const callAction =useCallback(
        (payload) => {
            dispatch(payload)
        },
        [],
    );
    const structure = useSelector(state => ({...state.FormState}));
    var form = generateForm(structure,callAction);
    
    return(   
        <React.Fragment>
                {form}
        </React.Fragment>

    );
});

Reducer code is given below

export const FormState = (state = FormStructureState ,action) =>
{   
    switch(action.type){
        case 'UPDATE':
            return {
                ...state,
                Structure:action.Payload
            }
        case 'MOVEITEM':
              var sourceRow = action.sourceRow;
              var sourceCol = action.sourceCol;
              var destRow = action.destRow;
              var destCol = action.destCol;
              var destinationContent = state.Structure.row[destRow][destCol].content;
              state.Structure.row[destRow][destCol].content = state.Structure.row[sourceRow][sourceCol].content;
              state.Structure.row[sourceRow][sourceCol].content = destinationContent;
              return {...state}
        case 'SETTEMPLATE':
              var sourceRow = action.sourceRow;
              var sourceCol = action.sourceCol;
              state.Structure.row[sourceRow][sourceCol].content.template = action.template;
        default:
            return {...state}

    }

    
}

Solution

  • Because your selector is unconditionally returning a new object every time:

    useSelector(state => ({...state.FormState}));
    

    useSelector will force a re-render if the return value is a new reference. Don't do that.

    Also, as a side note, your current reducer logic has a number of issues. In particular, these lines are mutating the existing state:

    state.Structure.row[destRow][destCol].content = state.Structure.row[sourceRow][sourceCol].content;
    

    Don't do that.

    There's also some stylistic issues:

    • Avoid using var
    • Use destructuring syntax to pull out variables, like const {sourceRow, sourceCol} = action

    I'd strongly encourage you to switch to using our official Redux Toolkit package, which will catch accidental mutations by default, and also allow you to write simpler reducer logic.