このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →
トラブルシューティング
ここではよく発生する問題とその解決策を共有します。
例ではReactを使用していますが、他のフレームワークを使っている場合でも参考になるでしょう。
アクションをディスパッチしても何も起こらない
アクションをディスパッチしているのにビューが更新されないことがあります。これにはいくつかの原因が考えられます。
リデューサーの引数を変更しない
Reduxから渡されるstateやactionを変更したくなるかもしれませんが、これは絶対に避けてください!
Reduxはリデューサー内で渡されるオブジェクトが変更されないことを前提としています。毎回必ず新しいstateオブジェクトを返す必要があります。 Immerのようなライブラリを使わない場合でも、変更は完全に避けなければなりません。
不変性(Immutability)は、react-reduxが状態の細かい更新を効率的に監視できるようにするためです。またredux-devtoolsによるタイムトラベル機能など、優れた開発者体験を実現します。
例えば次のリデューサーは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のポリフィルを使用します。
Object.assignは正しく使用してください。リデューサーから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
})
実験的な言語機能は変更される可能性があることに注意してください。
また、深くコピーする必要があるネストされた状態オブジェクトにも注意が必要です。_.extendとObject.assignはどちらも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>
}
}
これは、アクションクリエーターが単にアクションを返す関数だからです。実際にディスパッチするのはあなたの役割です。サーバーサイドレンダリングを行うアプリではリクエストごとに別々のReduxストアが必要なため、定義時に特定のストアインスタンスにアクションクリエーターをバインドすることはできません。
修正方法はstoreインスタンスのdispatch()メソッドを呼び出すことです:
handleClick() {
// Works! (but you need to grab store somehow)
store.dispatch(addTodo('Fix the issue'))
}
コンポーネント階層の深い場所では、ストアを手動で渡すのは面倒です。そのためreact-reduxでは、Reduxストアへの購読に加えて、コンポーネントのpropsにdispatchを注入する高階コンポーネントconnectを使用できます。
修正後のコードは次のようになります:
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 が正しいことを確認する
アクションを正しくディスパッチし、リデューサーを適用しているにもかかわらず、対応する状態が正しく props に変換されていない可能性があります。
その他の問題
#redux Reactiflux Discord チャンネルで質問するか、issue を作成してください。
問題を解決した場合は、同じ問題に遭遇した次の人のために、このドキュメントを編集してください。