跳至主要内容

故障排除

这是一个分享常见问题及其解决方案的地方。示例使用 React,但即使您使用其他工具,也应该能从中受益。

当我分派操作时什么也没发生

有时,您尝试分派操作,但您的视图没有更新。为什么会这样?这可能有多种原因。

永远不要修改 Reducer 参数

修改 Redux 传递给你的 stateaction 很诱人,但不要这样做!

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 对象。_.extendObject.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 频道上提问,或者 创建一个问题

如果你找到了解决方法,请 编辑此文档,作为对遇到相同问题的下一位用户的帮助。