故障排除
这是一个分享常见问题及其解决方案的地方。示例使用 React,但即使您使用其他工具,也应该能从中受益。
当我分派操作时什么也没发生
有时,您尝试分派操作,但您的视图没有更新。为什么会这样?这可能有多种原因。
永远不要修改 Reducer 参数
修改 Redux 传递给你的 state
或 action
很诱人,但不要这样做!
Redux 假设你永远不会修改它在 reducer 中传递给你的对象。**每次你都必须返回新的 state 对象。**即使你没有使用像 Immer 这样的库,你也需要完全避免修改。
不可变性让 react-redux 可以高效地订阅你 state 的细粒度更新。它还支持像 redux-devtools 这样的时间旅行等功能,从而带来极佳的开发体验。
例如,以下 reducer 是错误的,因为它修改了 state
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// Wrong! This mutates state
state.push({
text: action.text,
completed: false
})
return state
case 'COMPLETE_TODO':
// Wrong! This mutates state[action.index].
state[action.index].completed = true
return state
default:
return state
}
}
它需要像这样重写
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
}
}
虽然代码更多,但这正是使 Redux 可预测且高效的关键。如果你想减少代码量,可以使用像 React.addons.update
这样的辅助工具,用简洁的语法编写不可变的转换。
// Before:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
// After
return update(state, {
[action.index]: {
completed: {
$set: true
}
}
})
最后,要更新对象,你需要像 Underscore 中的 _.extend
这样的工具,或者更好的,一个 Object.assign
polyfill。
确保你正确地使用 Object.assign
。例如,不要从你的 reducer 中返回类似 Object.assign(state, newData)
的东西,而是返回 Object.assign({}, state, newData)
。这样你就不会覆盖之前的 state
。
你也可以使用对象展开运算符提案来获得更简洁的语法
// 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
})
请注意,实验性语言特性可能会发生变化。
还要注意需要深度复制的嵌套 state 对象。_.extend
和 Object.assign
都对 state 进行浅拷贝。有关如何处理嵌套 state 对象的建议,请参阅 更新嵌套对象。
不要忘记调用 dispatch(action)
如果你定义了一个 action creator,调用它并不会自动分派 action。例如,以下代码将不会执行任何操作
TodoActions.js
export function addTodo(text) {
return { type: 'ADD_TODO', text }
}
AddTodo.js
import React, { Component } from 'react'
import { addTodo } from './TodoActions'
class AddTodo extends Component {
handleClick() {
// Won't work!
addTodo('Fix the issue')
}
render() {
return <button onClick={() => this.handleClick()}>Add</button>
}
}
它不起作用是因为你的 action creator 只是一个返回 action 的函数。你需要自己去 dispatch 它。我们不能在定义期间将你的 action creator 绑定到特定的 Store 实例,因为在服务器上渲染的应用程序需要为每个请求创建一个单独的 Redux store。
解决方法是在 store 实例上调用 dispatch()
方法。
handleClick() {
// Works! (but you need to grab store somehow)
store.dispatch(addTodo('Fix the issue'))
}
如果你在组件层次结构的深处,手动传递 store 会很麻烦。这就是为什么 react-redux 允许你使用一个 connect
高阶组件,它除了让你订阅 Redux store 之外,还会将 dispatch
注入到你的组件的 props 中。
修复后的代码如下
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.
export default connect()(AddTodo)
如果你想的话,你可以手动将 dispatch
传递给其他组件。
确保 mapStateToProps 正确
你可能正在正确地 dispatch 一个 action 并应用你的 reducer,但相应的 state 并没有被正确地转换为 props。
其他问题
在 #redux Reactiflux Discord 频道上提问,或者 创建一个问题。
如果你找到了解决方法,请 编辑此文档,作为对遇到相同问题的下一位用户的帮助。