疑難排解
這裡是分享常見問題及其解決方案的地方。範例使用 React,但如果您使用其他東西,您仍然會覺得它們很有用。
在我發送動作時沒有任何事發生
有時,您嘗試發送動作,但您的檢視不會更新。為什麼會這樣?這可能有幾個原因。
絕不變異還原器引數
修改 Redux 傳遞給您的 state
或 action
會令人心動。請不要這麼做!
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 物件。_.extend
和 Object.assign
都會對 state 進行淺層複製。請參閱 更新巢狀物件,以取得處理巢狀 state 物件的建議。
別忘了呼叫 dispatch(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>
}
}
它不起作用,因為您的動作建立器只是一個傳回動作的函式。實際傳送動作的責任在您身上。我們無法在定義期間將您的動作建立器繫結到特定的 Store 實例,因為在伺服器上呈現的應用程式需要每個要求都有獨立的 Redux store。
解決方法是在 store 實例上呼叫 dispatch()
方法
handleClick() {
// Works! (but you need to grab store somehow)
store.dispatch(addTodo('Fix the issue'))
}
如果您在元件階層的深處,手動傳遞儲存庫會很麻煩。這就是 react-redux 讓您可以使用 connect
高階元件 的原因,除了讓您訂閱 Redux 儲存庫之外,還會將 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 正確
您可能會正確地發送動作並套用您的 reducer,但對應的狀態並未正確轉換為 props。
其他事項無法運作
在 #redux Reactiflux Discord 頻道中詢問,或 建立問題。
如果您找出原因,請 編輯此文件,以方便遇到相同問題的其他人。