Troubleshooting
Sometimes, you are trying to dispatch an action, but your view does not update. Why does this happen? There may be several reasons for this.
Never mutate reducer arguments
It is tempting to modify the or action
passed to you by Redux. Don't do this!
Redux assumes that you never mutate the objects it gives to you in the reducer. Every single time, you must return the new state object. Even if you don't use a library like Immutable, you need to completely avoid mutation.
Immutability is what lets efficiently subscribe to fine-grained updates of your state. It also enables great developer experience features such as time travel with redux-devtools.
For example, a reducer like this is wrong because it mutates the state:
It needs to be rewritten like this:
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// Return a new array
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
// Return a new array
return state.map((todo, index) => {
if (index === action.index) {
// Copy the object before mutating
return Object.assign({}, todo, {
completed: true
}
return todo
})
default:
return state
}
}
Finally, to update objects, you'll need something like from Underscore, or better, an polyfill.
Make sure that you use Object.assign
correctly. For example, instead of returning something like Object.assign(state, newData)
from your reducers, return Object.assign({}, state, newData)
. This way you don't override the previous state
.
You can also enable the object spread operator proposal for a more succinct syntax:
// Before:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
// After:
return state.map((todo, index) => {
if (index === action.index) {
return { ...todo, completed: true }
}
return todo
})
Note that experimental language features are subject to change.
Also keep an eye out for nested state objects that need to be deeply copied. Both _.extend
and Object.assign
make a shallow copy of the state. See for suggestions on how to deal with nested state objects.
Don't forget to call dispatch(action)
If you define an action creator, calling it will not automatically dispatch the action. For example, this code will do nothing:
TodoActions.js
AddTodo.js
import React, { Component } from 'react'
import { addTodo } from './TodoActions'
class AddTodo extends Component {
// Won't work!
addTodo('Fix the issue')
}
render() {
return <button onClick={() => this.handleClick()}>Add</button>
}
}
The fix is to call method on the store instance:
If you're somewhere deep in the component hierarchy, it is cumbersome to pass the store down manually. This is why lets you use a connect
higher-order component that will, apart from subscribing you to a Redux store, inject dispatch
into your component's props.
The fixed code looks like this:
AddTodo.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addTodo } from './TodoActions'
class AddTodo extends Component {
handleClick() {
// Works!
this.props.dispatch(addTodo('Fix the issue'))
}
render() {
return <button onClick={() => this.handleClick()}>Add</button>
}
}
// In addition to the state, `connect` puts `dispatch` in our props.
You can then pass down to other components manually, if you want to.
Make sure mapStateToProps is correct
It's possible you're correctly dispatching an action and applying your reducer but the corresponding state is not being correctly translated into props.
Ask around on the #redux Discord channel, or create an issue.If you figure it out, as a courtesy to the next person having the same problem.