web-dev-qa-db-ja.com

アイテム配列の単一の値を更新する|リデュースに反応する

ToDoリストがあり、ユーザーが「完了」をクリックした場合、配列内のそのアイテムの状態を「完了」に設定したい。

私のアクションは次のとおりです。

export function completeTodo(id) {
    return {
        type: "COMPLETE_TASK",
        completed: true,
        id
    }
}

私のレデューサーは次のとおりです。

case "COMPLETE_TASK": {
             return {...state,
                todos: [{
                    completed: action.completed
                }]
             }
        }

私が抱えている問題は、新しい状態では選択されたアイテムにそのtodoアイテムのテキストが関連付けられておらず、IDがそこにないということです。これは、状態を上書きし、以前のプロパティを無視しているためですか?オブジェクトアイテムのオンロードは次のようになります。

Objecttodos: Array[1]
    0: Object
        completed: false
        id: 0
        text: "Initial todo"
    __proto__: Object
    length: 1
    __proto__: Array[0]
    __proto__: Object

ご覧のとおり、完了した値をtrueに設定するだけです。

13
Filth

適切なアイテムを更新するには、todos配列を変換する必要があります。 Array.mapは、これを行う最も簡単な方法です。

case "COMPLETE_TASK":
    return {
        ...state,
        todos: state.todos.map(todo => todo.id === action.id ?
            // transform the one with a matching id
            { ...todo, completed: action.completed } : 
            // otherwise return original todo
            todo
        ) 
    };

このような状態の詳細な更新を支援するライブラリがあります。そのようなライブラリのリストは、ここで見つけることができます: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

個人的には、ImmutableJS( https://facebook.github.io/immutable-js/ )を使用します。これにより、updateInメソッドとsetInメソッド(多くのキーを持つ大きなオブジェクトや配列では通常のオブジェクトや配列よりも効率的ですが、小さなオブジェクトでは遅いです)。

46
TomW

新しい状態には、選択されたアイテムにそのtodoアイテムに関連付けられたテキストがなくなり、IDはもう存在しません。これは、状態を上書きして以前のプロパティを無視しているためですか?

はい、各更新中にonly one key completedで新しい配列を割り当てており、その配列には以前の値が含まれていないためです。したがって、更新後の配列には以前のデータがありません。それが、更新後にテキストとIDが存在しない理由です。

ソリューション:

1-array.map を使用して正しい要素を見つけ、値を更新します。例:

case "COMPLETE_TASK":
    return {
        ...state,
        todos: state.todos.map(todo => 
            todo.id === action.id ? { ...todo, completed: action.completed } : todo
        ) 
    };

2- array.findIndex を使用して、特定のオブジェクトのインデックスを検索し、次のように更新します。

case "COMPLETE_TASK":
    let index = state.todos.findIndex(todo => todo.id === action.id);
    let todos = [...state.todos];
    todos[index] = {...todos[index], completed: action.completed};
    return {...state, todos}

このスニペットをチェックすると、あなたがしている間違いについてより良いアイデアを得ることができます:

let state = {
  a: 1,
  arr: [
    {text:1, id:1, completed: true},
    {text:2, id:2, completed: false}
  ]
}

console.log('old values', JSON.stringify(state));

// updating the values

let newState = {
   ...state,
   arr: [{completed: true}]
}

console.log('new state = ', newState);
3
Mayank Shukla

Reactの独創的な設計原則の1つは「状態を変更しないでください。」配列内のデータを変更する場合は、変更された値で新しい配列を作成します。 。

たとえば、状態の結果の配列があります。最初は、コンストラクターの各インデックスの値を0に設定しています。

this.state = {           
       index:0,
        question: this.props.test[0].questions[0],
        results:[[0,0],[1,0],[2,0],[3,0],[4,0],[5,0]],
        complete: false         
};

後で、配列の値を更新します。ただし、状態オブジェクトでは変更していません。 ES6では、スプレッド演算子を使用できます。配列スライスメソッドは新しい配列を返し、既存の配列を変更しません。

updateArray = (list, index,score) => {
   // updates the results array without mutating it
      return [
        ...list.slice(0, index),
        list[index][1] = score,
       ...list.slice(index + 1)
     ];
};

配列内のアイテムを更新する場合は、updateArrayを呼び出して、状態を一度に設定します。

this.setState({
        index:newIndex, 
        question:this.props.test[0].questions[newIndex],
        results:this.updateArray(this.state.results, index, score)
});
2
Charles Owen