Jaki jest poprawny sposób aktualizowania stanu, czy obiekt zagnieżdżony w React with Hooks?
export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})
Jak można użyć setExampleState
aktualizacji exampleState
do a
(dołączania pola)?
const a = {
masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}
}
b
(Zmiana wartości)?
const b = {masterField: {
fieldOne: "e",
fieldTwo: {
fieldTwoOne: "f"
fieldTwoTwo: "g"
}
}
})
javascript
reactjs
react-hooks
isaacsultan
źródło
źródło
Odpowiedzi:
W ten sposób możesz przekazać nową wartość
setExampleState({...exampleState, masterField2: { fieldOne: "c", fieldTwo: { fieldTwoOne: "d", fieldTwoTwo: "e" } }, }})
źródło
masterField
zamiastmasterField2
setExampleState({...exampleState, masterField:
{// nowe wartości}setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Jeśli ktoś szuka haków useState (), zaktualizuj obiekt
- 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' }));
źródło
Generalnie należy uważać na głęboko zagnieżdżone obiekty w stanie React. Aby uniknąć nieoczekiwanego zachowania, stan powinien być niezmiennie aktualizowany. Kiedy masz głębokie obiekty, ostatecznie klonujesz je w celu uzyskania niezmienności, co w Reakcie może być dość kosztowne. Czemu?
Gdy głęboko sklonujesz stan, React przeliczy i ponownie wyrenderuje wszystko, co zależy od zmiennych, nawet jeśli się nie zmieniły!
Zanim więc spróbujesz rozwiązać swój problem, zastanów się, jak możesz najpierw spłaszczyć stan. Jak tylko to zrobisz, znajdziesz piękne narzędzia, które pomogą radzić sobie z dużymi stanami, takie jak useReducer ().
Jeśli zastanawiałeś się nad tym, ale nadal jesteś przekonany, że musisz użyć głęboko zagnieżdżonego drzewa stanu, możesz nadal używać useState () z bibliotekami takimi jak immutable.js i Immutability-helper . Ułatwiają aktualizację lub klonowanie głębokich obiektów bez martwienia się o zmienność.
źródło
Dzięki Filipowi to mi pomogło - moim przypadkiem użycia było to, że miałem formularz z wieloma polami wejściowymi, więc zachowałem stan początkowy jako obiekt i nie mogłem zaktualizować stanu obiektu. Powyższy post mi pomógł :)
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} />
źródło
Spóźniłem się na imprezę .. :)
Odpowiedź @aseferov działa bardzo dobrze, gdy intencją jest ponowne wejście do całej struktury obiektu. Jeśli jednak celem / celem jest aktualizacja określonej wartości pola w obiekcie, uważam, że poniższe podejście jest lepsze.
sytuacja:
const [infoData, setInfoData] = useState({ major: { name: "John Doe", age: "24", sex: "M", }, minor:{ id: 4, collegeRegion: "south", } });
Aktualizacja konkretnego rekordu będzie wymagała przywrócenia do poprzedniego stanu
prevState
Tutaj:
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", } }));
być może
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", }, minor: { ...prevState.minor, collegeRegion: "northEast" }));
Mam nadzieję, że pomoże to każdemu, kto próbuje rozwiązać podobny problem.
źródło
()
:: Aby odpowiedzieć na to proste;setInfoData
Dzieje się tak dlatego, że jest z natury wysokiego rzędu, tj. może przyjąć inną funkcję jako argument, dzięki możliwości dostarczonej przez Hook: useState. Mam nadzieję, że ten artykuł zapewni większą przejrzystość funkcji wyższego rzędu: sitepoint.com/higher-order-functions-javascriptfunction 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} /> {items.title}</label> </div> ) })} </div> ); }
źródło
Myślę, że najlepszym rozwiązaniem jest Immer . Pozwala na aktualizację obiektu tak, jak bezpośrednio modyfikujesz pola (masterField.fieldOne.fieldx = 'abc'). Ale oczywiście nie zmieni to rzeczywistego obiektu. Zbiera wszystkie aktualizacje obiektu roboczego i na końcu daje ostateczny obiekt, którego można użyć do zastąpienia oryginalnego obiektu.
źródło
Zostawiam ci funkcję narzędzia do nieodwracalnej aktualizacji obiektów
/** * 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 }; };
Więc możesz tego używać w ten sposób
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 }
Jeśli nie, masz więcej narzędzi tutaj: https://es.reactjs.org/docs/update.html
źródło
Początkowo użyłem object w useState, ale potem przeszedłem do haka useReducer dla złożonych przypadków. Odczułem poprawę wydajności, kiedy refaktoryzowałem kod.
useReducer React docs
Zaimplementowałem już taki hak na własny użytek:
/** * 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); } }, ]; }
więcej powiązanych haków .
źródło
, zrób to jak w tym przykładzie:
stan pierwszego stworzenia obiektów:
const [isSelected, setSelection] = useState({ id_1: false }, { id_2: false }, { id_3: false });
następnie zmień wartość jednego z nich:
// if the id_1 is false make it true or return it false. onValueChange={() => isSelected.id_1 == false ? setSelection({ ...isSelected, id_1: true }) : setSelection({ ...isSelected, id_1: false })}
źródło