react setstate não funciona corretamente em loops

Aug 31 2020

estou recebendo uma resposta de validação do meu back-end como uma matriz como esta

 [
        { param: "secondName", msg: "second name is required" },
        { param: "password", msg: "password is required" }
 ]

e eu tenho um estado no meu componente de reação como este

  const [errs, setErrs] = useState({
    firstName: null,
    secondName: null,
    password: null,
    email: null,
  })

O objetivo é alterar no meu estado apenas os campos mencionados no meu response.paramsformulário de envio e deixar o resto como estão null. isso é o que eu tentei:

const submitFoo = () => {
    console.log(localErrs) //all props are set to null (default)
    res.forEach((single) => {
        setLocalErrs({
            ...localErrs,
            [single.param]: single.msg
        });
    });
    console.log(localErrs);//only password is set to the `response.msg`, instead of `password` AND `secondName`
};

mas o problema é que ele está apenas mudando o último item em meu "estado de erro"; a saída é:

{
    first: null,
    second: null,
    password: 'password is required',
    email: null,
}

ps: tentei com vanilla js percorrendo a matriz e definindo os props do Errs obj diretamente para o response.msg e funcionou. então proplem deve estar com react setstate

Respostas

3 AwaisRafiqChaudhry Aug 31 2020 at 00:25

Tente usar a abordagem Fat Arrow para definir o estado da seguinte maneira:

setLocalErrs(localErrs => ({ ...localErrs, [single.param]: single.msg }));

Ele evita a perda de estado caso a função setter seja chamada duas vezes ao mesmo tempo devido a diferentes chamadas assíncronas, etc.

1 BhojendraRauniyar Aug 31 2020 at 00:34

Evite atualizar o estado em um loop . Atualizar estado como:

let errObj = {}
res.forEach((single) => {
  errObj[single.param] = single.msg
})
setLocalErrs({
  ...localErrs,
  ...errObj
})

A propósito, você pode simplesmente usar:

setLocalErrs(errObj)

Uma vez que errObjtem todos os valores de estados atualizados.