Search code examples
reactjsreduxmapstatetopropsmapdispatchtoprops

React & Redux: State of component is not updating even with mapStateToProps


I posted an answer below, but if someone can explain why this is necessary you'll get the bounty, I went through a redux tutorial and feel like I didn't learn about mapDispatchToProps, only mapStateToProps. If you can explain at a deeper level what exactly mapStateToProps and mapDispatchToProps are doing and how they are different I'll give you the bounty.


Minimum Reproducible Example


I have a mapStateToProps function that looks like

const mapStateToProps = state => {
  return {
    firstName: state.firstName,
    middleName: state.middleName,
    lastName: state.lastName,
  }
}

const ReduxTabForm = connect(mapStateToProps)(MyTab)

In my MyTab component I have a button that is supposed to be inactive if these 2 field do not have anything entered in, but the state of whether or not the button is disabled does not change

function App() {
  const {firstName, lastName} = store.getState().formData

  const isDisabled = () => {
    const {firstName, lastName} = store.getState().form
    const requiredFields = [firstName, lastName]
    alert(requiredFields)
    for(let i = 0; i < requiredFields.length; i=i+1){
      if (!requiredFields[i]){
        return true
      }
    }
    return false
  }

  return (

    <div className="App">
        <div className='bg-light rounded'>
          <div className='px-sm-5 pt-0 px-4 flexCenterCol mx-5'>

            <div>
                <input 
                    type='text'
                    className="form-control"
                    value={store.getState().formData['firstName']}
                    placeholder="First Name"
                    onChange={(e) => {
                        store.dispatch(setFormData({'firstName': e.target.value}))
                    }}
               ></input>
               <input 
                    type='text'
                    className="form-control"
                    value={store.getState().formData['lastName']}
                    placeholder="Last Name"
                    onChange={(e) => {
                        store.dispatch(setFormData({'lastName': e.target.value}))
                    }}
               ></input>

            </div>
            <button
                type="submit"
                disabled={isDisabled()}
            >
                Button
            </button>
        </div>
    </div>
  </div>
 )
}

That alert statement executes on page refresh, but does not execute any time after that when I enter data in. I have checked that the redux state updating and it is. The button will not update though, and the isDisabled function will not run more than once


Solution

  • I looked at your reducer code and it looks like this:

    ...
    const reducer = combineReducers({
      formData: formReducer,
    })
    
    export default reducer
    

    Which means your redux state structure is like this:

    state = {
      formData: {
        firstName: <value>,
        middleName: <value>,
        lastName: <value>,
      }
    }
    

    Solution

    So, to make your component re-render when the redux state is changed, you need to subscribe to the correct state variables in your mapStateToProps function. Change it to this and will work:

    const mapStateToProps = state => {
      return {
        firstName: state.formData.firstName, // Note that I added "formData"
        middleName: state.formData.middleName,
        lastName: state.formData.lastName
      }
    }
    

    Couple of side notes:

    1. It's a better to use the props instead of directly accessing the redux store.
    2. For debugging, console.log is preferred over alert. React DevTools and Redux DevTools are even better.