Reagiere auf Hooks useState () mit Object

103

Was ist die richtige Methode zum Aktualisieren des Status, ist ein verschachteltes Objekt in Reagieren mit Hooks?

export Example = () => {
  const [exampleState, setExampleState] = useState(
  {masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b"
           fieldTwoTwo: "c"
           }
        }
   })

Wie würde man verwenden , setExampleStateum Updates exampleStatezu a(ein Feld anhängen)?

const a = {
masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }
}

b (Werte ändern)?

const b = {masterField: {
        fieldOne: "e",
        fieldTwo: {
           fieldTwoOne: "f"
           fieldTwoTwo: "g"
           }
        }
   })

isaacsultan
quelle
Sie meinen, dem vorhandenen Objekt einen neuen Objektschlüsselwert hinzuzufügen?
Einfach Code
@ Justcode Für das erste Beispiel ja, für das zweite Beispiel nur das vorhandene Objekt
ändern

Antworten:

109

Sie können einen neuen Wert wie diesen übergeben

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }})
aseferov
quelle
2
Großartig, das beantwortet Teil a! Kennen Sie die Best Practice für Teil b?
Isaacacultan
1
Ändern auch gleich gerade vorbei masterFieldstatt masterField2 setExampleState({...exampleState, masterField:{// neue Werte}
aseferov
3
Achten Sie beim Verwenden des Spread-Operators auf flaches Kopieren. Siehe zum Beispiel: stackoverflow.com/questions/43638938/…
João Marcos Gris
7
Wenn Sie denselben Status mit dem Dispatcher verwenden, sollten Sie eine Funktion verwenden. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Michael Harley
43

Wenn jemand nach useState () sucht, aktualisiert Hooks das Objekt

- Through Input

        const [state, setState] = useState({ fName: "", lName: "" });
        const handleChange = e => {
            const { name, value } = e.target;
            setState(prevState => ({
                ...prevState,
                [name]: value
            }));
        };

        <input
            value={state.fName}
            type="text"
            onChange={handleChange}
            name="fName"
        />
        <input
            value={state.lName}
            type="text"
            onChange={handleChange}
            name="lName"
        />
   ***************************

 - Through onSubmit or button click
    
        setState(prevState => ({
            ...prevState,
            fName: 'your updated value here'
         }));
Maheshvirus
quelle
3
Genau das, wonach ich gesucht habe. Genial!
StackedActor
41

Im Allgemeinen sollten Sie auf tief verschachtelte Objekte im Status "Reagieren" achten. Um unerwartetes Verhalten zu vermeiden, sollte der Status unveränderlich aktualisiert werden. Wenn Sie tiefe Objekte haben, klonen Sie diese tief, um Unveränderlichkeit zu erreichen, was in React sehr teuer sein kann. Warum?

Sobald Sie den Status tief geklont haben, berechnet React alles neu und rendert es neu, was von den Variablen abhängt, auch wenn sie sich nicht geändert haben!

Überlegen Sie sich also zunächst, wie Sie den Status reduzieren können, bevor Sie versuchen, Ihr Problem zu lösen. Sobald Sie dies tun, finden Sie schöne Tools, die Ihnen beim Umgang mit großen Zuständen helfen, wie z. B. useReducer ().

Für den Fall , haben Sie darüber nach , sind aber nach wie vor davon überzeugt , benötigen Sie einen tief verschachtelten Zustand Baum zu verwenden, haben Sie immer useState () mit Bibliotheken wie können immutable.js und Unveränderlichkeit-Helfer . Sie machen es einfach, tiefe Objekte zu aktualisieren oder zu klonen, ohne sich um die Veränderlichkeit sorgen zu müssen.

Ilyas Assainov
quelle
Sie können auch Hookstate (Haftungsausschluss: Ich bin ein Autor) verwenden, um komplexe (lokale und globale) Statusdaten zu verwalten, ohne sie dann gründlich zu klonen und ohne sich um unnötige Updates zu kümmern - Hookstate übernimmt dies für Sie.
Andrew
7

Danke Philip, das hat mir geholfen. Mein Anwendungsfall war, dass ich ein Formular mit vielen Eingabefeldern hatte, sodass ich den Anfangszustand als Objekt beibehalten und den Objektstatus nicht aktualisieren konnte. Der obige Beitrag hat mir geholfen :)

const [projectGroupDetails, setProjectGroupDetails] = useState({
    "projectGroupId": "",
    "projectGroup": "DDD",
    "project-id": "",
    "appd-ui": "",
    "appd-node": ""    
});

const inputGroupChangeHandler = (event) => {
    setProjectGroupDetails((prevState) => ({
       ...prevState,
       [event.target.id]: event.target.value
    }));
}

<Input 
    id="projectGroupId" 
    labelText="Project Group Id" 
    value={projectGroupDetails.projectGroupId} 
    onChange={inputGroupChangeHandler} 
/>


Kavitha Vikas
quelle
6

Ich bin zu spät zur Party .. :)

Die Antwort von @aseferov funktioniert sehr gut, wenn die gesamte Objektstruktur erneut eingegeben werden soll. Wenn das Ziel jedoch darin besteht, einen bestimmten Feldwert in einem Objekt zu aktualisieren, ist der folgende Ansatz meiner Meinung nach besser.

Lage:

const [infoData, setInfoData] = useState({
    major: {
      name: "John Doe",
      age: "24",
      sex: "M",
    },

    minor:{
      id: 4,
      collegeRegion: "south",

    }

  });

Für die Aktualisierung eines bestimmten Datensatzes muss der vorherige Status wiederhergestellt werden prevState

Hier:

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      }
    }));

vielleicht

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      },
      minor: {
        ...prevState.minor,
        collegeRegion: "northEast"

    }));

Ich hoffe, dies hilft jedem, der versucht, ein ähnliches Problem zu lösen.

Olamigoke Philip
quelle
Ich habe eine Frage. Warum müssen wir die Funktion in Klammern setzen? Ich bin mir nicht sicher, was unter der Haube los ist. Würdest du zufällig wissen oder wo ich mehr darüber lesen kann?
Xenon
1
@MichaelRamage Warum setzen wir die Funktion in Klammern :: ()Um dies einfach zu beantworten; setInfoData Dies liegt daran, dass es von Natur aus von hoher Ordnung ist, dh es kann dank der vom Hook bereitgestellten Leistung eine andere Funktion als Argument übernehmen: useState. Ich hoffe, dieser Artikel bietet mehr Klarheit über Funktionen höherer Ordnung: sitepoint.com/higher-order-functions-javascript
Olamigoke Philip
1
Sehr hilfreich, insbesondere für den Fall, dass Sie versuchen, eine Eigenschaft zu aktualisieren, die mehrere Ebenen tief verschachtelt ist, dh: first.second.third - die anderen Beispiele beziehen sich auf first.second, aber nicht first.second.third
Craig Presti - MSFT
3
function App() {

  const [todos, setTodos] = useState([
    { id: 1, title: "Selectus aut autem", completed: false },
    { id: 2, title: "Luis ut nam facilis et officia qui", completed: false },
    { id: 3, title: "Fugiat veniam minus", completed: false },
    { id: 4, title: "Aet porro tempora", completed: true },
    { id: 5, title: "Laboriosam mollitia et enim quasi", completed: false }
  ]);

  const changeInput = (e) => {todos.map(items => items.id === parseInt(e.target.value) && (items.completed = e.target.checked));
 setTodos([...todos], todos);}
  return (
    <div className="container">
      {todos.map(items => {
        return (
          <div key={items.id}>
            <label>
<input type="checkbox" 
onChange={changeInput} 
value={items.id} 
checked={items.completed} />&nbsp; {items.title}</label>
          </div>
        )
      })}
    </div>
  );
}
vikas kumar
quelle
1

Ich denke, die beste Lösung ist Immer . Sie können das Objekt so aktualisieren, als würden Sie Felder direkt ändern (masterField.fieldOne.fieldx = 'abc'). Aber das eigentliche Objekt wird sich natürlich nicht ändern. Es sammelt alle Aktualisierungen eines Entwurfsobjekts und gibt Ihnen am Ende ein endgültiges Objekt, mit dem Sie das ursprüngliche Objekt ersetzen können.

Deniz
quelle
0

Ich überlasse Ihnen eine Utility-Funktion, um Objekte unveränderlich zu aktualisieren

/**
 * Inmutable update object
 * @param  {Object} oldObject     Object to update
 * @param  {Object} updatedValues Object with new values
 * @return {Object}               New Object with updated values
 */
export const updateObject = (oldObject, updatedValues) => {
  return {
    ...oldObject,
    ...updatedValues
  };
};

Sie können es also so verwenden

const MyComponent = props => {

  const [orderForm, setOrderForm] = useState({
    specialities: {
      elementType: "select",
      elementConfig: {
        options: [],
        label: "Specialities"
      },
      touched: false
    }
  });


// I want to update the options list, to fill a select element

  // ---------- Update with fetched elements ---------- //

  const updateSpecialitiesData = data => {
    // Inmutably update elementConfig object. i.e label field is not modified
    const updatedOptions = updateObject(
      orderForm[formElementKey]["elementConfig"],
      {
        options: data
      }
    );
    // Inmutably update the relevant element.
    const updatedFormElement = updateObject(orderForm[formElementKey], {
      touched: true,
      elementConfig: updatedOptions
    });
    // Inmutably update the relevant element in the state.
    const orderFormUpdated = updateObject(orderForm, {
      [formElementKey]: updatedFormElement
    });
    setOrderForm(orderFormUpdated);
  };

  useEffect(() => {
      // some code to fetch data
      updateSpecialitiesData.current("specialities",fetchedData);
  }, [updateSpecialitiesData]);

// More component code
}

Wenn nicht, haben Sie hier weitere Dienstprogramme: https://es.reactjs.org/docs/update.html

Gonzalo
quelle
0

Anfangs habe ich ein Objekt in useState verwendet, dann bin ich für komplexe Fälle zum useReducer-Hook übergegangen. Ich spürte eine Leistungsverbesserung, als ich den Code überarbeitete.

useReducer ist useState normalerweise vorzuziehen, wenn Sie eine komplexe Statuslogik haben, die mehrere Unterwerte umfasst, oder wenn der nächste Status vom vorherigen abhängt.

useReducer React-Dokumente

Ich habe einen solchen Hook bereits für meinen eigenen Gebrauch implementiert:

/**
 * Same as useObjectState but uses useReducer instead of useState
 *  (better performance for complex cases)
 * @param {*} PropsWithDefaultValues object with all needed props 
 * and their initial value
 * @returns [state, setProp] state - the state object, setProp - dispatch 
 * changes one (given prop name & prop value) or multiple props (given an 
 * object { prop: value, ...}) in object state
 */
export function useObjectReducer(PropsWithDefaultValues) {
  const [state, dispatch] = useReducer(reducer, PropsWithDefaultValues);

  //newFieldsVal={[field_name]: [field_value], ...}
  function reducer(state, newFieldsVal) {
    return { ...state, ...newFieldsVal };
  }

  return [
    state,
    (newFieldsVal, newVal) => {
      if (typeof newVal !== "undefined") {
        const tmp = {};
        tmp[newFieldsVal] = newVal;
        dispatch(tmp);
      } else {
        dispatch(newFieldsVal);
      }
    },
  ];
}

mehr verwandte Haken .

eyalyoli
quelle