2016-12-07 33 views
8

Mam listę zadań i chcę ustawić stan tego elementu w tablicy, aby "zakończyć", jeśli użytkownik kliknie "ukończ".Zaktualizuj pojedynczą wartość w tablicy pozycji | reagx redux

Oto moje działanie:

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

Oto mój reduktor:

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

Problem mam jest nowe państwo nie prowadzi już tekst związany z tego elementu na todo wybrana pozycja i identyfikator już tam nie są. Czy to dlatego, że nadpisuję stan i ignoruję poprzednie właściwości? Moje ładowanie obiektów obiektów wygląda następująco:

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

Jak widać, wszystko, co chcę zrobić, to ustawić ukończoną wartość na wartość true.

Odpowiedz

29

Musisz zmienić tablicę todos, aby zaktualizować odpowiedni artykuł. Array.map to najprostszy sposób na zrobienie tego:

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

Istnieją biblioteki, które pomogą ci w tej głębokiej aktualizacji stanu. Możesz znaleźć listę takich bibliotek tutaj: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

Osobiście używam ImmutableJS (https://facebook.github.io/immutable-js/), która rozwiązuje problem z jego updateIn i setIn metod (które są bardziej efektywne niż normalnych obiektów i tablic dla dużych obiektów z dużą ilością klawiszy i dla tablic, ale wolniej dla małych).

+0

dzięki za pomoc TomW i dziękuję za odniesienia do bibliotek w zakresie aktualizacji stanu głębokiego. Świetna pomoc! – Filth

+0

Widzę, że używasz pojedynczej linii, jeśli stwierdzenie jest trudne do odczytania. Wiem, że reduktory bombowe, gdy są instrukcje if. Czy jesteś w stanie wyjaśnić, co tu się dzieje i być może inny sposób napisania tej logiki? – Filth

+0

Redukuje "bombę", gdy nie zwrócisz stanu z każdej gałęzi - instrukcje "jeśli" są w porządku, wystarczy, że upewnisz się, że "zwrócisz stan"; po znaku 'if'. Jeśli spojrzysz na obsługę TOGGLE_TODO tutaj: http://redux.js.org/docs/basics/Reducers.html#handling-more-actions zobaczysz bardzo podobny przykład do mojego, który używa 'if ' instrukcja zamiast '?:' - '?:' ma niewielką przewagę nad instrukcją if, ponieważ zmusza cię do zwracania wartości z każdej gałęzi. – TomW

1

Nowe państwo nie ma już tekst powiązany z tej pozycji todo na wybranego elementu i identyfikator nie jest już tam, Czy to dlatego, że jestem nadpisania stanu i ignorując wcześniejsze właściwości?

Tak, ponieważ podczas każdej aktualizacji jesteś przypisując nową tablicę z tylko jeden klucz completed, a tablica nie zawiera żadnych poprzednich wartości. Więc po aktualizacji tablica nie będzie miała poprzednich danych. Właśnie dlatego tekst i identyfikatory nie są dostępne po aktualizacji.

rozwiązania:

1- Zastosowanie array.map znaleźć odpowiednie elementem następnie aktualizować wartość, na przykład:

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

2- Zastosowanie array.findIndex znaleźć indeks danego obiektu następnie zaktualizuj to, tak:

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

Sprawdź ten fragment będzie można uzyskać lepsze wyobrażenie o pomyłkę robisz:

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

0

Jednym z przełomowych zasad projektowania w React jest „Nie mutować stanu."Jeśli chcesz zmienić dane w tablicy, chcesz utworzyć nową tablicę ze zmienionymi wartościami:

Na przykład mam tablicę wyników w stanie. Początkowo po prostu ustawiam wartości na 0 dla każdego indeksu w moim konstruktora.

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

Później chcę zaktualizować wartości w tablicy. Ale ja nie zmieniając go w obiekcie państwowej. z ES6, możemy użyć operatora rozprzestrzeniać. Metoda wycinka tablic zwraca nową tablicę, nie zmieni istniejącej tablicy.

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

Kiedy ja wan t zaktualizować elementu w tablicy, wzywam updateArray i ustawić stan za jednym zamachem:

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