Skip to main content

故障排除

¥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 传递给你的 stateaction 是很诱人的。不要这样做!

¥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.

还要留意需要深度复制的嵌套状态对象。_.extendObject.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.