這里會列出常見的問題和對應的解決方案。雖然使用 React 做示例,即使你使用要其它庫仍然會有幫助。
有時,你 dispatch action 后,view 卻沒有更新。這是為什么呢?可能有下面幾種原因。
如果你想修改 Redux 給你傳入的 state
或 action
,請住手!
Redux 假定你永遠不會修改 reducer 里傳入的對象。任何時候,你都應該返回一個新的 state 對象。即使你沒有使用 Immutable 這樣的庫,也要保證做到不修改對象。
不變性(Immutability)可以讓 react-redux 高效的監(jiān)聽 state 的細粗度更新。它也讓 redux-devtools 能提供“時間旅行”這類強大特性。
例如,下面的 reducer 就是錯誤的,因為它改變了 state:
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// 錯誤!這會改變 state.actions。
state.actions.push({
text: action.text,
completed: false
});
case 'COMPLETE_TODO':
// 錯誤!這會改變 state.actions[action.index].
state.actions[action.index].completed = true;
}
return state
}
應該重寫成這樣:
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// 返回新數(shù)組
return [...state, {
text: action.text,
completed: false
}];
case 'COMPLETE_TODO':
// 返回新數(shù)組
return [
...state.slice(0, action.index),
// 修改之前復制數(shù)組
Object.assign({}, state[action.index], {
completed: true
}),
...state.slice(action.index + 1)
];
default:
return state;
}
}
雖然需要寫更多代碼,但是讓 Redux 變得可具有可預測性和高效。如果你想減少代碼量,你可以用一些輔助方法類似 React.addons.update
來讓這樣的不可變轉(zhuǎn)換操作變得更簡單:
// 修改前
return [
...state.slice(0, action.index),
Object.assign({}, state[action.index], {
completed: true
}),
...state.slice(action.index + 1)
]
// 修改后
return update(state, {
[action.index]: {
completed: {
$set: true
}
}
});
最后,如果需要更新 object,你需要使用 Underscore 提供的 _.extend
方法,或者更好的,使用 Object.assign
的 polyfill
要注意 Object.assign
的使用方法。例如,在 reducer 里不要這樣使用 Object.assign(state, newData)
,應該用 Object.assign({}, state, newData)
。這樣它才不會覆蓋以前的 state
。
你也可以通過使用 Babel 階段 1 模式來開啟 ES7 對象的 spread 操作:
// 修改前:
return [
...state.slice(0, action.index),
Object.assign({}, state[action.index], {
completed: true
}),
...state.slice(action.index + 1)
]
// 修改后:
return [
...state.slice(0, action.index),
{ ...state[action.index], completed: true },
...state.slice(action.index + 1)
]
注意還在實驗階段的特性注定經(jīng)常改變,最好不要在大的項目里過多依賴它們。
dispatch(action)
如果你定義了一個 action 創(chuàng)建函數(shù),調(diào)用它并不會自動 dispatch 這個 action。比如,下面的代碼什么也不會做:
TodoActions.js
export function addTodo(text) {
return { type: 'ADD_TODO', text };
}
AddTodo.js
import { Component } from 'react';
import { addTodo } from './TodoActions';
class AddTodo extends Component {
handleClick() {
// 不起作用!
addTodo('Fix the issue');
}
render() {
return (
<button onClick={() => this.handleClick()}>
Add
</button>
);
}
}
它不起作用是因為你的 action 創(chuàng)建函數(shù)只是一個返回 action 的函數(shù)而已。你需要手動 dispatch 它。我們不能在定義時把 action 創(chuàng)建函數(shù)綁定到指定的 Store 上,因為應用在服務端渲染時需要為每個請求都對應一個獨立的 Redux store。
解法是調(diào)用 store 實例上的 dispatch()
方法。
handleClick() {
// 生效?。ǖ阈枰纫阅撤N方式拿到 store)
store.dispatch(addTodo('Fix the issue'));
}
如果組件的層級非常深,把 store 一層層傳下去很麻煩。因此 react-redux 提供了 connect
這個 高階組件,它除了可以幫你監(jiān)聽 Redux store,還會把 dispatch
注入到組件的 props 中。
修復后的代碼是這樣的:
import { Component } from 'react';
import { connect } from 'react-redux';
import { addTodo } from './TodoActions';
class AddTodo extends Component {
handleClick() {
// 生效!
this.props.dispatch(addTodo('Fix the issue'));
}
render() {
return (
<button onClick={() => this.handleClick()}>
Add
</button>
);
}
}
// 除了 state,`connect` 還把 `dispatch` 放到 props 里。
export default connect(AddTodo, state => ({}))
如果你想的話也可以把 dispatch
手動傳給其它組件。
在 Slack Reactiflux 里的 redux 頻道里提問,或者提交一個 issue。如果問題終于解決了,請把解法寫到文檔里,以便別人遇到同樣問題時參考。
更多建議: