Vyhledávání na webu

javascript - React Hooks useState() s objektem


Jaký je správný způsob aktualizace stavu v zanořeném objektu v Reactu pomocí Hooků?

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

Jak by někdo použil setExampleState k aktualizaci exampleState na a (přidáním pole)?

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

b (Měnící se hodnoty)?

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

Odpověď č. 1

Novou hodnotu můžete předat takto:

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
   })

Odpověď č. 2

Pokud někdo hledá aktualizaci useState() hooků pro objekt

Prostřednictvím vstupu

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"
/>

Prostřednictvím onSubmit nebo kliknutím na tlačítko

setState(prevState => ({
    ...prevState,
    fName: 'your updated value here'
}));

Odpověď č. 3

Obecně byste si měli dávat pozor na hluboce vnorené objekty ve stavu Reactu. Abyste se vyhnuli neočekávanému chování, měl by být stav aktualizován neměnným způsobem. Když máte hluboké objekty, nakonec je musíte hluboce klonovat pro neměnnost, což může být v Reactu dost nákladné. Proč?

Jakmile provedete hlubokou kopii stavu, React přepočítá a znovu vykreslí vše, co závisí na proměnných, i když se nezměnily!

Takže než se pokusíte vyřešit svůj problém, nejdříve přemýšlejte, jak můžete stav zjednodušit. Jakmile to uděláte, najdete užitečné nástroje, které vám pomohou pracovat s velkými stavy, například useReducer().

Pokud jste o tom přemýšleli, ale stále jste přesvědčeni, že potřebujete používat hluboce zanořený stavový strom, stále můžete použít useState() spolu s knihovnami jako immutable.js a Immutability-helper. Tyto knihovny usnadňují aktualizaci nebo klonování hlubokých objektů, aniž byste se museli obávat změnitelnosti.

Odpověď č. 4

Jsem pozdě na párty.. :)

@aseferov odpověď funguje velmi dobře, když je úmyslem znovu zadat celou strukturu objektu. Nicméně, pokud je cílem aktualizovat konkrétní hodnotu pole v objektu, domnívám se, že níže uvedený přístup je lepší.

situace:

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

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

    }

  });

Aktualizace konkrétního záznamu bude vyžadovat volání předchozího stavu prevState

Zde :

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

snad

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

    }));

Doufám, že to pomůže každému, kdo se snaží vyřešit podobný problém.

Odpověď č. 5

Je možné použít hook useReducer pro správu složitého stavu místo useState. K tomu nejprve inicializujte stav a aktualizační funkci takto:

const initialState = { name: "Bob", occupation: "builder" };
const [state, updateState] = useReducer(
  (state, updates) => ({ ...state, ...updates }),
  initialState
);

A pak můžete aktualizovat stav tím, že předáte pouze částečné aktualizace, jako je toto:

updateState({ occupation: "postman" })

Odpověď č. 6

V roce 2022

Pokud hledáte stejnou funkcionalitu jako this.setState (pochází z třídních komponent) v funkcionálních komponentách, pak je toto odpověď, která vám hodně pomůže.

Například

Máte stav jako níže a chcete aktualizovat pouze konkrétní pole z celého stavu, pak musíte pokaždé použít destrukturalizaci objektu a někdy to může být otravné.

const [state, setState] = useState({first: 1, second: 2});

// results will be state = {first: 3} instead of {first: 3, second: 2}
setState({first: 3})

// To resolve that you need to use object destructing every time
// results will be state = {first: 3, second: 2}
setState(prev => ({...prev, first: 3}))

Abych to vyřešil, přišel jsem s přístupem useReducer. Prosím, podívejte se na useReducer.

const stateReducer = (state, action) => ({
  ...state,
  ...(typeof action === 'function' ? action(state) : action),
});
const [state, setState] = useReducer(stateReducer, {first: 1, second: 2});

// results will be state = {first: 3, second: 2}
setState({first: 3})

// you can also access the previous state callback if you want
// results will remain same, state = {first: 3, second: 2}
setState(prev => ({...prev, first: 3}))

Můžete ten stateReducer uložit do souborů utils a importovat ho v každém souboru, pokud chcete.

Tady je vlastní hook, pokud chceš.

import React from 'react';

export const stateReducer = (state, action) => ({
  ...state,
  ...(typeof action === 'function' ? action(state) : action),
});

const useReducer = (initial, lazyInitializer = null) => {
  const [state, setState] = React.useReducer(stateReducer, initial, init =>
    lazyInitializer ? lazyInitializer(init) : init
  );

  return [state, setState];
};

export default useReducer;

Typescript

import React, { Dispatch } from "react";

type SetStateAction<S> = S | ((prev: S) => S);

type STATE<R> = [R, Dispatch<SetStateAction<Partial<R>>>];

const stateReducer = (state, action) => ({
  ...state,
  ...(typeof action === "function" ? action(state) : action),
});

const useReducer = <S>(initial, lazyInitializer = null): STATE<S> => {
  const [state, setState] = React.useReducer(stateReducer, initial, (init) =>
    lazyInitializer ? lazyInitializer(init) : init,
  );

  return [state, setState];
};

export default useReducer;

Odpověď č. 7

Díky Philip, to mi pomohlo - můj případ použití byl, že jsem měl formulář s mnoha vstupními poli, takže jsem udržoval počáteční stav jako objekt a nebyl jsem schopen aktualizovat stav objektu. Výše uvedený příspěvek mi pomohl :)

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


Související články