Каков правильный способ обновления состояния вложенного объекта в React with Hooks?
export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})
Как бы один использовать setExampleState
для обновления exampleState
до a
(добавления поля)?
const a = {
masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}
}
b
(Изменение значений)?
const b = {masterField: {
fieldOne: "e",
fieldTwo: {
fieldTwoOne: "f"
fieldTwoTwo: "g"
}
}
})
javascript
reactjs
react-hooks
Исааксултан
источник
источник
Ответы:
Вы можете передать новое значение следующим образом
setExampleState({...exampleState, masterField2: { fieldOne: "c", fieldTwo: { fieldTwoOne: "d", fieldTwoTwo: "e" } }, }})
источник
masterField
вместоmasterField2
setExampleState({...exampleState, masterField:
{// новых значений}setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Как правило, вы должны следить за глубоко вложенными объектами в состоянии React. Чтобы избежать неожиданного поведения, состояние должно обновляться постоянно. Когда у вас есть глубокие объекты, вы в конечном итоге их глубокое клонирование для неизменности, что может быть довольно дорогостоящим в React. Зачем?
После того, как вы глубоко клонируете состояние, React пересчитает и повторно отрендерит все, что зависит от переменных, даже если они не изменились!
Итак, прежде чем пытаться решить вашу проблему, подумайте, как вы можете сначала сгладить состояние. Как только вы это сделаете, вы найдете прекрасные инструменты, которые помогут справиться с большими состояниями, такие как useReducer ().
Если вы подумали об этом, но все же уверены, что вам нужно использовать глубоко вложенное дерево состояний, вы все равно можете использовать useState () с такими библиотеками, как immutable.js и Immutability-helper . Они упрощают обновление или клонирование глубоких объектов, не беспокоясь об изменчивости.
источник
Если кто-то ищет обновление хуков useState () для объекта
- 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' }));
источник
Спасибо, Филип, это помогло мне - в моем случае у меня была форма с множеством полей ввода, поэтому я сохранил исходное состояние как объект, и я не смог обновить состояние объекта. Приведенный выше пост помог мне :)
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} />
источник
Я опаздываю на вечеринку .. :)
@aseferov ответ работает очень хорошо, когда намерение состоит в том, чтобы повторно ввести всю структуру объекта. Однако, если цель / цель - обновить конкретное значение поля в объекте, я считаю, что приведенный ниже подход лучше.
ситуация:
const [infoData, setInfoData] = useState({ major: { name: "John Doe", age: "24", sex: "M", }, minor:{ id: 4, collegeRegion: "south", } });
Обновление конкретной записи потребует возврата к предыдущему состоянию
prevState
Вот:
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", } }));
возможно
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", }, minor: { ...prevState.minor, collegeRegion: "northEast" }));
Надеюсь, это поможет любому, кто пытается решить аналогичную проблему.
источник
()
:: Чтобы просто ответить на этот вопрос; это потому, чтоsetInfoData
по своей природе он имеет высокий порядок, т.е. он может принимать другую функцию в качестве аргумента, благодаря мощности, предоставляемой Hook: useState. Я надеюсь, что эта статья внесет больше ясности в функции высшего порядка: 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> ); }
источник
Думаю, лучшее решение - Immer . Это позволяет вам обновлять объект, как если бы вы напрямую изменяли поля (masterField.fieldOne.fieldx = 'abc'). Но реального объекта это, конечно, не изменит. Он собирает все обновления для чернового объекта и дает вам окончательный объект в конце, который вы можете использовать для замены исходного объекта.
источник
Я оставляю вам служебную функцию для неизменного обновления объектов
/** * 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 }; };
Так что вы можете использовать это так
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 }
Если нет, здесь есть другие утилиты: https://es.reactjs.org/docs/update.html
источник
Изначально я использовал объект в useState, но затем перешел на хук useReducer для сложных случаев. Я почувствовал улучшение производительности при рефакторинге кода.
useReducer React документы
Я уже реализовал такой хук для себя:
/** * 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); } }, ]; }
больше связанных крючков .
источник