Когда вы думаете о useEffect
как о функции, которая запускается при использовании «побочного эффекта» (изменение состояния) в качестве componentDidUpdate
, вам необходимо следить за изменениями в состояниях error
и msg
.
Вместо
// Similar to componentDidMount and componentDidUpdate
//By running an empty array [] as a second argument,
//we’re letting React know that the useEffect function doesn’t
//depend on any values from props or state.
useEffect(() => {
const { error } = props;
if (error !== form.error) {
//check for register error
if (error.id === "REGISTER_FAIL") {
setValues({ msg: error.msg});
} else {
setValues({ msg: null });
}
}
}, []);
Вам нужно сделать
useEffect(() => {
const { error } = props;
if (error !== form.error) {
//check for register error
if (error.id === "REGISTER_FAIL") {
- setValues(({ msg: error.msg});
+ setValues({ ...form, msg: error.msg});
} else {
- setValues({ msg: null });
+ setValues({ ...form, msg: null });
}
}
}, [error, form.msg]);
Причина, по которой я распространяю ...form
как setValues({ ...form, msg: error.msg.msg })
, заключается в том, что функция обновления (2-й аргумент), возвращаемая React.useState
, не обновляет другие свойства в состоянии.
Так что setValues({ msg: null });
превратится form
с
{
name: 'previous name',
password: 'previous password',
email: 'previous email',
msg: 'previous message...'
}
в {msg: null}
удаление name, email, password
свойств.
Поскольку поведение между React.useState
в обновителе ("aliased" как setState
в React документации ) и setState
отличается, кто-то в итоге создал use-legay-state NPM пакет , который вы, вероятно, не должны использовать без крайней необходимости.
примечание
Не имеющий отношения к вашему вопросу, вы также хотели бы
1. разделить name/email/password/msg
на отдельные состояния
2. "или" используйте useReducer hook.
Изменение реализации для случая 1
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [message, setMessage] = useState('');
Если вы хотите продолжать использовать объект form
, вы можете следовать соглашению об именах, изменив setValues
на setForm
или const [formValues, setFormValues] = useState({...})
.
И вы можете обновить каждое поле, как,
<TextField
autoComplete="name"
name="name"
variant="outlined"
required
fullWidth
id="name"
label="Full Name"
autoFocus
value={name}
onChange={e => setName(e.target.value)}
/>
Вы можете использовать useCallback для onChange
как onChange={useCallback(e => setName(e.target.value), [name])}
здесь, но не обязательно, если у вас нет проблем с производительностью. Начните с простого изначально ?
Кроме того, ваш useEffect
зависит только от msg
.
useEffect(() => {
const { error } = props;
if (error !== form.error) {
//check for register error
if (error.id === "REGISTER_FAIL") {
setMessage(error.msg);
} else {
setMessage(null);
}
}
}, [error, message]);
Здесь я изменил setValues
на setMessage
и массив зависимостей с [error, form]
на [error, message]
.
Изменение реализации для случая 2
Если вы хотите использовать useReducer
, вам кажется, что вам нужно больше работать, так как вы изменяете поле индивидуальной формы, которое изменяется отдельно. useReducer
работает лучше, когда вы обновляете связанный набор состояний, которые должны изменяться вместе. Но в вашем случае каждое состояние (password/name
и т. Д.) Изменяется независимо от пользовательского ввода, поэтому было бы легче приступить к описанному выше случаю № 1.