故障排除
¥Troubleshooting
这是一个分享常见问题和解决方案的地方。这些示例使用 React,但如果你使用其他东西,你应该仍然会发现它们很有用。
¥This is a place to share common problems and solutions to them. The examples use React, but you should still find them useful if you use something else.
当我发送一个动作时什么也没有发生
¥Nothing happens when I dispatch an action
有时,你尝试分派操作,但你的视图没有更新。为什么会出现这种情况?这可能有几个原因。
¥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.
永远不要改变 reducer 参数
¥Never mutate reducer arguments
修改 Redux 传递给你的 state
或 action
是很诱人的。不要这样做!
¥It is tempting to modify the state
or action
passed to you by Redux. Don't do this!
Redux 假设你永远不会改变它在 reducer 中提供给你的对象。每次,你都必须返回新的状态对象。即使你不使用像 伊梅尔 这样的库,你也需要完全避免突变。
¥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 Immer, you need to completely avoid mutation.
不可变性使 react-redux 能够有效地订阅你状态的细粒度更新。它还支持出色的开发者体验功能,例如 redux-devtools 的时间旅行。
¥Immutability is what lets react-redux efficiently subscribe to fine-grained updates of your state. It also enables great developer experience features such as time travel with redux-devtools.
例如,像这样的 reducer 是错误的,因为它改变了状态:
¥For example, a reducer like this is wrong because it mutates the 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
}
}
它需要像这样重写:
¥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
}
}
这是更多的代码,但这正是 Redux 变得可预测且高效的原因。如果你想要更少的代码,你可以使用像 React.addons.update
这样的助手来用简洁的语法编写不可变的转换:
¥It's more code, but it's exactly what makes Redux predictable and efficient. If you want to have less code, you can use a helper like React.addons.update
to write immutable transformations with a terse syntax:
// 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。
¥Finally, to update objects, you'll need something like _.extend
from Underscore, or better, an Object.assign
polyfill.
确保正确使用 Object.assign
。例如,不要从你的 reducer 返回诸如 Object.assign(state, newData)
之类的内容,而是返回 Object.assign({}, state, newData)
。这样你就不会覆盖之前的 state
。
¥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 use 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.
还要留意需要深度复制的嵌套状态对象。_.extend
和 Object.assign
都制作状态的浅表副本。有关如何处理嵌套状态对象的建议,请参阅 更新嵌套对象。
¥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 Updating Nested Objects for suggestions on how to deal with nested state objects.
别忘了调用 dispatch(action)
¥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
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>
}
}
它不起作用,因为你的动作创建者只是一个返回动作的函数。实际发送它取决于你。我们无法在定义期间将你的操作创建者绑定到特定的 Store 实例,因为在服务器上渲染的应用需要为每个请求提供单独的 Redux 存储。
¥It doesn't work because your action creator is just a function that returns an action. It is up to you to actually dispatch it. We can't bind your action creators to a particular Store instance during the definition because apps that render on the server need a separate Redux store for every request.
修复方法是在 store 实例上调用 dispatch()
方法:
¥The fix is to call dispatch()
method on the store instance:
handleClick() {
// Works! (but you need to grab store somehow)
store.dispatch(addTodo('Fix the issue'))
}
如果你位于组件层次结构的深处,则手动传递存储会很麻烦。这就是为什么 react-redux 允许你使用 connect
高阶分量,除了为你订阅 Redux 存储之外,还将 dispatch
注入组件的 props 中。
¥If you're somewhere deep in the component hierarchy, it is cumbersome to pass the store down manually. This is why react-redux 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.
export default connect()(AddTodo)
如果需要,你可以手动将 dispatch
传递给其他组件。
¥You can then pass dispatch
down to other components manually, if you want to.
确保 mapStateToProps 正确
¥Make sure mapStateToProps is correct
你可能正确地调度了一个动作并应用了你的 reducer,但相应的状态没有被正确地转换为属性。
¥It's possible you're correctly dispatching an action and applying your reducer but the corresponding state is not being correctly translated into props.
其他东西不起作用
¥Something else doesn't work
在 #redux 反应流 Discord 通道或 创建一个问题 上询问。
¥Ask around on the #redux Reactiflux Discord channel, or create an issue.
如果你弄清楚了,编辑本文档 作为对下一个遇到同样问题的人的礼貌。
¥If you figure it out, edit this document as a courtesy to the next person having the same problem.