MapStateToProps not being called after changing STATE in reducer

446 views Asked by At

I have theses files :

numPadHandler (my reducer), configureStore (my store), TestRedux (my component, trying to use the store) and my app file.

reducer :

    // store/reducers/numpadReducers/numpadHandler
    
    const initialState = { valueNumPad: "1" }
    
    function numpadHandler(state = initialState, action) {
        let nextState;
        
        switch(action.type) {
            case 'ADD_DIGIT':
                nextState = state;
                nextState.valueNumPad = nextState.valueNumPad + action.value;
    
                console.log("reducer new state : " + state.valueNumPad); 
                return nextState;
    
            default:
                return state;
        }
    }
    
    export default numpadHandler;

store :

    // store/configureStore

    import { createStore } from 'redux';
    import numpadHandler from './reducers/numpadReducers/numpadHandler';
    
    export default createStore(numpadHandler);

component :


    // components/TestRedux
    
    import React, { useState } from "react";
    import { connect } from 'react-redux';
    import Button from './Button';
    
    const TestRedux = (props) => {
        const handleClick = () => {
            console.log("props value numpad : " + props.valueNumPad);
    
            const action = { type: "ADD_DIGIT", value: 5 }
            props.dispatch(action);
        }
        
        return (
            <div>
                <Button onClick={handleClick} />
            </div>
        );
    }
    
    const mapStateToProps = (state) => {
        console.log("state : " + state.valueNumPad);
        return state
    }
    
    export default connect(mapStateToProps)(TestRedux);

app :

//...
render() {
        return (
            <Provider store={ Store }>
                <TestRedux />
            </Provider>
       );
}

I can't figure why when i click on the button, the reducer's state is changing but the function mapStateToProps isn't called again.

There is my console output after page load :

state : 1

After one click :

props value numpad : 1
reducer new state : 15

After two click :

props value numpad : 1
reducer new state : 155

Complete console output after 2 clicks :

state : 1
props value numpad : 1
reducer new state : 15
props value numpad : 1
reducer new state : 155

So you can see the action is happening and the reducer state is updating, but there is not update in component side !

Can you explain me why ?

RESOLVED : Answer based on TLadd response :

My reducer code was wrong, this is the good way to code :


    case 'ADD_DIGIT':
      const nextState = { 
        ...state, 
        valueNumPad: state.valueNumPad + action.value
      };
      return nextState

1

There are 1 answers

0
TLadd On

The problem is in the reducer:

case 'ADD_DIGIT':
  nextState = state;
  nextState.valueNumPad = nextState.valueNumPad + action.value;
  console.log("reducer new state : " + state.valueNumPad); 
  return nextState;

Redux works off the assumption that all state changes are handled immutably. This choice allows it to use shallow equality checks when deciding which parts of your app need to re-render in response to an action being dispatched. In the above reducer case, you assign state to nextState and assign the new value directly to nextState.valueNumPad. Even though nextState is a new reference, it still refers to the original state as well and the result is a mutation of the original state.

The correct would to do this would be:

case 'ADD_DIGIT':
  const nextState = { 
    ...state, 
    valueNumPad: state.valueNumPad + action.value
  };
  return nextState

In this case, I'm making a copy of state using the spread notation ...state and overwriting the valueNumPad value in that copy. The returned nextState is a new object and I did not mutate the old state object at all.