跳到主要內容

疑難排解

這裡是分享常見問題及其解決方案的地方。範例使用 React,但如果您使用其他東西,您仍然會覺得它們很有用。

在我發送動作時沒有任何事發生

有時,您嘗試發送動作,但您的檢視不會更新。為什麼會這樣?這可能有幾個原因。

絕不變異還原器引數

修改 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)

如果您定義了一個動作建立器,呼叫它不會自動傳送動作。例如,這段程式碼不會執行任何動作

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 頻道中詢問,或 建立問題

如果您找出原因,請 編輯此文件,以方便遇到相同問題的其他人。