Qual é a maneira correta de atualizar o estado, é um objeto aninhado, em React with Hooks?
export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})
Como alguém usaria setExampleState
para atualizar exampleState
para a
(anexando um campo)?
const a = {
masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}
}
b
(Alterando valores)?
const b = {masterField: {
fieldOne: "e",
fieldTwo: {
fieldTwoOne: "f"
fieldTwoTwo: "g"
}
}
})
javascript
reactjs
react-hooks
isaacsultan
fonte
fonte
Respostas:
Você pode passar um novo valor assim
setExampleState({...exampleState, masterField2: { fieldOne: "c", fieldTwo: { fieldTwoOne: "d", fieldTwoTwo: "e" } }, }})
fonte
masterField
vez demasterField2
setExampleState({...exampleState, masterField:
{// novos valores}setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Se alguém estiver procurando por useState (), atualize os ganchos para o objeto
- 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' }));
fonte
Geralmente, você deve estar atento a objetos profundamente aninhados no estado React. Para evitar um comportamento inesperado, o estado deve ser atualizado de forma imutável. Quando você tem objetos profundos, acaba clonando-os profundamente para a imutabilidade, o que pode ser bastante caro no React. Por quê?
Depois de clonar profundamente o estado, o React irá recalcular e renderizar novamente tudo o que depende das variáveis, mesmo que elas não tenham mudado!
Portanto, antes de tentar resolver seu problema, pense em como você pode nivelar o estado primeiro. Assim que fizer isso, você encontrará belas ferramentas que o ajudarão a lidar com grandes estados, como useReducer ().
Caso você tenha pensado sobre isso, mas ainda esteja convencido de que precisa usar uma árvore de estados profundamente aninhada, você ainda pode usar useState () com bibliotecas como immutable.js e Immutability-helper . Eles simplificam a atualização ou clonagem de objetos profundos sem ter que se preocupar com a mutabilidade.
fonte
Obrigado Philip isso me ajudou - meu caso de uso foi eu tinha um formulário com muitos campos de entrada, então mantive o estado inicial como objeto e não fui capaz de atualizar o estado do objeto. O post acima me ajudou :)
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} />
fonte
Estou atrasado para a festa .. :)
A resposta @aseferov funciona muito bem quando a intenção é entrar novamente em toda a estrutura do objeto. No entanto, se a meta / objetivo é atualizar um valor de campo específico em um objeto, acredito que a abordagem abaixo é melhor.
situação:
const [infoData, setInfoData] = useState({ major: { name: "John Doe", age: "24", sex: "M", }, minor:{ id: 4, collegeRegion: "south", } });
Atualizar um registro específico exigirá fazer um recall para o estado anterior
prevState
Aqui:
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", } }));
possivelmente
setInfoData((prevState) => ({ ...prevState, major: { ...prevState.major, name: "Tan Long", }, minor: { ...prevState.minor, collegeRegion: "northEast" }));
Espero que isso ajude qualquer pessoa que esteja tentando resolver um problema semelhante.
fonte
()
:: Para responder de forma simples; é porquesetInfoData
é de alta ordem por natureza, ou seja, pode assumir outra função como argumento, cortesia do poder fornecido pelo Gancho: useState. Espero que este artigo forneça mais clareza sobre funções de ordem superior: 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> ); }
fonte
Acho que a melhor solução é o Immer . Ele permite que você atualize o objeto como se estivesse modificando campos diretamente (masterField.fieldOne.fieldx = 'abc'). Mas isso não mudará o objeto real, é claro. Ele coleta todas as atualizações em um objeto de rascunho e fornece um objeto final no final que pode ser usado para substituir o objeto original.
fonte
Deixo-vos uma função utilitária para atualizar objetos inmutavelmente
/** * 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 }; };
Então você pode usá-lo assim
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 }
Caso contrário, você tem mais utilitários aqui: https://es.reactjs.org/docs/update.html
fonte
Inicialmente, usei object em useState, mas depois mudei para o gancho useReducer para casos complexos. Senti uma melhora no desempenho ao refatorar o código.
useReducer React docs
Já implementei esse gancho para meu próprio uso:
/** * 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); } }, ]; }
mais ganchos relacionados .
fonte
, faça como este exemplo:
primeiro estado de criação dos objetos:
const [isSelected, setSelection] = useState({ id_1: false }, { id_2: false }, { id_3: false });
em seguida, altere o valor deles:
// 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 })}
fonte