web-dev-qa-db-fra.com

Mettre à jour une valeur unique dans le tableau d'éléments | réagir de nouveau

J'ai une liste de tâches et je souhaite définir l'état de cet élément dans le tableau sur "terminé" si l'utilisateur clique sur "terminer".

Voici mon action:

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

Voici mon réducteur:

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

Le problème que je rencontre est que le nouvel état n'a plus le texte associé à cet élément de tâche sur l'élément sélectionné et que l'ID n'est plus là. Est-ce parce que j'écrase l'état et que j'ignore les propriétés précédentes? La charge de mon objet objet ressemble à ceci:

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

Comme vous pouvez le voir, tout ce que je veux faire est de définir la valeur terminée sur true.

13
Filth

Vous devez transformer votre tableau de todos pour mettre à jour l'élément approprié. Array.map est le moyen le plus simple de le faire:

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
        ) 
    };

Il existe des bibliothèques pour vous aider avec ce type de mise à jour d'état profond. Vous pouvez trouver une liste de ces bibliothèques ici: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

Personnellement, j'utilise ImmutableJS ( https://facebook.github.io/immutable-js/ ) qui résout le problème avec ses méthodes updateIn et setIn (qui sont plus efficaces que les objets normaux et les tableaux pour les grands objets avec beaucoup de clés et pour les tableaux, mais plus lents pour les petits).

46
TomW

Le nouvel état n'a plus le texte associé à cet élément de tâche sur l'élément sélectionné et l'ID n'est plus là, est-ce parce que j'écrase l'état et ignore les propriétés précédentes?

Oui, car à chaque mise à jour, vous assignez un nouveau tableau avec une seule touche completed, et ce tableau ne contient aucun précédent valeurs. Ainsi, après la mise à jour, le tableau n'aura aucune donnée précédente. C'est pourquoi le texte et les identifiants ne sont pas là après la mise à jour.

Solutions:

1 - Utilisez array.map pour trouver l'élément correct puis mettez à jour la valeur, comme ceci:

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

2- Utilisez array.findIndex pour trouver l'index de cet objet particulier puis mettez-le à jour, comme ceci:

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}

Vérifiez cet extrait, vous aurez une meilleure idée de l'erreur que vous faites:

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

L'un des principes de conception séminaux de React est "Don't mutate state". Si vous souhaitez modifier des données dans un tableau, vous souhaitez créer un nouveau tableau avec la ou les valeurs modifiées. .

Par exemple, j'ai un tableau de résultats en état. Au départ, je mets simplement des valeurs à 0 pour chaque index de mon constructeur.

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         
};

Plus tard, je veux mettre à jour une valeur dans le tableau. Mais je ne le change pas dans l'objet d'état. Avec ES6, nous pouvons utiliser l'opérateur d'étalement. La méthode de tranche de tableau renvoie un nouveau tableau, elle ne changera pas le tableau existant.

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

Lorsque je veux mettre à jour un élément du tableau, j'appelle updateArray et je définis l'état en une seule fois:

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