So aktualisieren Sie einen einzelnen Wert innerhalb eines bestimmten Array-Elements in Redux

105

Ich habe ein Problem, bei dem das erneute Rendern des Status Probleme mit der Benutzeroberfläche verursacht, und es wurde vorgeschlagen, nur einen bestimmten Wert in meinem Reduzierer zu aktualisieren, um das erneute Rendern auf einer Seite zu reduzieren.

Dies ist ein Beispiel für meinen Zustand

{
 name: "some name",
 subtitle: "some subtitle",
 contents: [
   {title: "some title", text: "some text"},
   {title: "some other title", text: "some other text"}
 ]
}

und ich aktualisiere es gerade so

case 'SOME_ACTION':
   return { ...state, contents: action.payload }

Wo action.payloadist ein ganzes Array mit neuen Werten. Aber jetzt muss ich nur noch den Text des zweiten Elements im Inhaltsarray aktualisieren, und so etwas funktioniert nicht

case 'SOME_ACTION':
   return { ...state, contents[1].text: action.payload }

Wo action.payloadist jetzt ein Text, den ich zum Aktualisieren brauche.

Ilja
quelle

Antworten:

57

Sie können die React Immutability-Helfer verwenden

import update from 'react-addons-update';

// ...    

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      1: {
        text: {$set: action.payload}
      }
    }
  });

Obwohl ich mir vorstellen würde, dass Sie wahrscheinlich mehr davon machen würden?

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      [action.id]: {
        text: {$set: action.payload}
      }
    }
  });
Clarkie
quelle
Muss ich diese Helfer tatsächlich einbeziehen, damit sie nicht in meinem Reduzierer reagieren?
Ilja
8
Obwohl das Paket verschoben wurde, kolodny/immutability-helperist es jetzt yarn add immutability-helperundimport update from 'immutability-helper';
SacWebDeveloper
Mein Fall ist genau der gleiche, aber wenn ich das Objekt in einem der Elemente im Array aktualisiere, wird der aktualisierte Wert nicht in den componentWillReceiveProps wiedergegeben. Ich verwende Inhalte direkt in meinen mapStateToProps. Müssen wir in mapStateToProps etwas anderes verwenden, damit sie wiedergegeben werden?
Srishti Gupta
171

Sie können verwenden map. Hier ist eine Beispielimplementierung:

case 'SOME_ACTION':
   return { 
       ...state, 
       contents: state.contents.map(
           (content, i) => i === 1 ? {...content, text: action.payload}
                                   : content
       )
    }
Fatih Erikli
quelle
So mache ich es auch. Ich bin mir nur nicht sicher, ob es ein guter Ansatz ist oder nicht, da Map ein neues Array zurückgibt. Irgendwelche Gedanken?
Thiago C. S Ventura
@ Ventura Ja, es ist gut, weil es eine neue Referenz für das Array und ein einfaches neues Objekt für das zu aktualisierende Objekt erstellt (und nur das eine), genau das, was Sie wollen
ivcandela
3
Ich denke, das ist der beste Ansatz
Jesus Gomez
12
Ich mache das auch oft, aber es scheint relativ teuer zu sein, alle Elemente zu durchlaufen, anstatt den erforderlichen Index zu aktualisieren.
Ben Creasy
schöne ! Ich liebe es !
Nate Ben
21

Sie müssen nicht alles in einer Zeile erledigen:

case 'SOME_ACTION':
  const newState = { ...state };
  newState.contents = 
    [
      newState.contents[0],
      {title: newState.contnets[1].title, text: action.payload}
    ];
  return newState
Yuya
quelle
1
Du hast recht, ich habe eine schnelle Lösung gefunden. Übrigens, ist das Array eine feste Länge? und ist der Index, den Sie ändern möchten, ebenfalls festgelegt? Der derzeitige Ansatz ist nur mit einem kleinen Array und einem festen Index realisierbar.
Yuya
2
Wickeln Sie Ihren Fallkörper in {} ein, da const einen Blockbereich hat.
Benny Powers
12

Sehr spät zur Party, aber hier ist eine generische Lösung, die mit jedem Indexwert funktioniert.

  1. Sie erstellen und verbreiten ein neues Array vom alten Array bis zu dem, das indexSie ändern möchten.

  2. Fügen Sie die gewünschten Daten hinzu.

  3. Erstellen und verbreiten Sie ein neues Array von dem Array, das indexSie ändern möchten, bis zum Ende des Arrays

let index=1;// probabbly action.payload.id
case 'SOME_ACTION':
   return { 
       ...state, 
       contents: [
          ...state.contents.slice(0,index),
          {title: "some other title", text: "some other text"},
         ...state.contents.slice(index+1)
         ]
    }

Aktualisieren:

Ich habe ein kleines Modul erstellt, um den Code zu vereinfachen. Sie müssen also nur eine Funktion aufrufen:

case 'SOME_ACTION':
   return {
       ...state,
       contents: insertIntoArray(state.contents,index, {title: "some title", text: "some text"})
    }

Weitere Beispiele finden Sie im Repository

Funktionssignatur:

insertIntoArray(originalArray,insertionIndex,newData)
Ivan V.
quelle
3

Ich glaube, wenn Sie diese Art von Operationen für Ihren Redux-Status benötigen, ist der Spread-Operator Ihr Freund, und dieses Prinzip gilt für alle Kinder.

Stellen wir uns vor, dies sei Ihr Zustand:

const state = {
    houses: {
        gryffindor: {
          points: 15
        },
        ravenclaw: {
          points: 18
        },
        hufflepuff: {
          points: 7
        },
        slytherin: {
          points: 5
        }
    }
}

Und Sie möchten Ravenclaw 3 Punkte hinzufügen

const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }

Mit dem Spread-Operator können Sie nur den neuen Status aktualisieren und alles andere intakt lassen.

Beispiel aus diesem erstaunlichen Artikel , finden Sie fast jede mögliche Option mit tollen Beispielen.

Luis Rodriguez
quelle
3

In meinem Fall habe ich so etwas gemacht, basierend auf Luis 'Antwort:

...State object...
userInfo = {
name: '...',
...
}

...Reducer's code...
case CHANGED_INFO:
return {
  ...state,
  userInfo: {
    ...state.userInfo,
    // I'm sending the arguments like this: changeInfo({ id: e.target.id, value: e.target.value }) and use them as below in reducer!
    [action.data.id]: action.data.value,
  },
};
Erfan226
quelle
0

So habe ich es für eines meiner Projekte gemacht:

const markdownSaveActionCreator = (newMarkdownLocation, newMarkdownToSave) => ({
  type: MARKDOWN_SAVE,
  saveLocation: newMarkdownLocation,
  savedMarkdownInLocation: newMarkdownToSave  
});

const markdownSaveReducer = (state = MARKDOWN_SAVED_ARRAY_DEFAULT, action) => {
  let objTemp = {
    saveLocation: action.saveLocation, 
    savedMarkdownInLocation: action.savedMarkdownInLocation
  };

  switch(action.type) {
    case MARKDOWN_SAVE:
      return( 
        state.map(i => {
          if (i.saveLocation === objTemp.saveLocation) {
            return Object.assign({}, i, objTemp);
          }
          return i;
        })
      );
    default:
      return state;
  }
};
TazExprez
quelle