У меня был компонент класса с именем <BasicForm>
, с помощью которого я создавал формы. Он обрабатывает проверку и все формы state
. Он предоставляет все необходимые функции (onChange
, onSubmit
и т. Д.) Для входов (отображается как children
из BasicForm
) через контекст React.
Работает так, как задумано. Проблема в том, что теперь, когда я конвертирую его в React Hooks, у меня возникают сомнения при попытке воспроизвести следующее поведение, которое я делал, когда это был класс:
class BasicForm extends React.Component {
...other code...
touchAllInputsValidateAndSubmit() {
// CREATE DEEP COPY OF THE STATE'S INPUTS OBJECT
let inputs = {};
for (let inputName in this.state.inputs) {
inputs = Object.assign(inputs, {[inputName]:{...this.state.inputs[inputName]}});
}
// TOUCH ALL INPUTS
for (let inputName in inputs) {
inputs[inputName].touched = true;
}
// UPDATE STATE AND CALL VALIDATION
this.setState({
inputs
}, () => this.validateAllFields()); // <---- SECOND CALLBACK ARGUMENT
}
... more code ...
}
Когда пользователь нажимает кнопку отправки, BasicForm
должен «коснуться» всех входов и только потом вызывать validateAllFields()
, потому что ошибки проверки будут отображаться только при касании ввода. Так что, если пользователь не коснулся ни одной, BasicForm
необходимо убедиться, что он "прикоснулся" к каждому входу перед вызовом функции validateAllFields()
.
И когда я использовал классы, я использовал второй аргумент обратного вызова в функции setState()
, как видно из приведенного выше кода. И это обеспечило вызов validateAllField()
только после обновления состояния (того, которое касается всех полей).
Но когда я пытаюсь использовать этот второй параметр обратного вызова с хуками состояния useState()
, я получаю эту ошибку:
const [inputs, setInputs] = useState({});
... some other code ...
setInputs(auxInputs, () => console.log('Inputs updated!'));
Предупреждение: обновления состояния из хуков useState () и useReducer ()
не поддерживает второй аргумент обратного вызова. Выполнить побочный эффект
после рендеринга объявите его в теле компонента с помощью useEffect ().
Итак, согласно сообщению об ошибке выше, я пытаюсь сделать это с помощью useEffect()
hook. Но это немного смущает меня, потому что, насколько я знаю, useEffect()
основан не на обновлениях состояний, а на выполнении рендеринга. Он выполняется после каждого рендера. И я знаю, что React может поставить в очередь некоторые обновления состояния перед повторным рендерингом, поэтому я чувствую, что не могу полностью контролировать, когда мой хук useEffect()
будет выполнен так, как я это делал, когда использовал классы и setState()
Второй аргумент обратного вызова.
То, что я до сих пор получил (кажется, работает):
function BasicForm(props) {
const [inputs, setInputs] = useState({});
const [submitted, setSubmitted] = useState(false);
... other code ...
function touchAllInputsValidateAndSubmit() {
const shouldSubmit = true;
// CREATE DEEP COPY OF THE STATE'S INPUTS OBJECT
let auxInputs = {};
for (let inputName in inputs) {
auxInputs = Object.assign(auxInputs, {[inputName]:{...inputs[inputName]}});
}
// TOUCH ALL INPUTS
for (let inputName in auxInputs) {
auxInputs[inputName].touched = true;
}
// UPDATE STATE
setInputs(auxInputs);
setSubmitted(true);
}
// EFFECT HOOK TO CALL VALIDATE ALL WHEN SUBMITTED = 'TRUE'
useEffect(() => {
if (submitted) {
validateAllFields();
}
setSubmitted(false);
});
... some more code ...
}
Я использую хук useEffect()
для вызова функции validateAllFields()
. И так как useEffect()
выполняется на каждом рендере , мне нужен был способ узнать, когда вызывать validateAllFields()
, так как я не хочу этого на каждом рендере. Таким образом, я создал переменную состояния submitted
, чтобы знать, когда мне нужен этот эффект.
Это хорошее решение? Какие еще возможные решения вы могли бы подумать? Это просто странно.
Представьте, что validateAllFields()
- это функция, которая НЕ МОЖЕТ быть вызвана дважды без каких-либо обстоятельств. Как мне узнать, что при следующем рендеринге мое submitted
состояние будет уже «ложным» на 100% уверенным?
Могу ли я рассчитывать на то, что React выполнит каждое обновление состояния в очереди перед следующим рендерингом? Это гарантировано?