跳至主内容
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

故障排除

这里汇集了常见问题及其解决方案。虽然示例使用了 React,但即使您使用其他框架,这些内容仍然具有参考价值。

派发 action 后没有任何反应

有时您尝试派发 action,但视图并未更新。为什么会这样?可能有多种原因导致此问题。

永远不要直接修改 reducer 参数

您可能会想直接修改 Redux 传递的 stateaction。切勿这样做!

Redux 要求您永远不要在 reducer 中直接修改传入的对象。每次都必须返回全新的 state 对象。 即使不使用 Immer 这类库,也必须完全避免直接修改。

不可变性(Immutability)是 react-redux 高效订阅细粒度状态更新的基础,同时也支持 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 对象,请参考更新嵌套对象

不要忘记调用 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 的函数,实际派发 action 需要您自行完成。我们无法在定义时将 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 正确无误

可能您已正确派发 action 并应用了 reducer,但对应状态未能正确映射到 props。

其他问题

请在 #redux Reactiflux Discord 频道中咨询,或创建 issue

若您已解决问题,请编辑本文档以便帮助后续遇到相同问题的开发者。