#javascript #reactjs #validation #react-hooks
#javascript #reactjs #проверка #реагирующие хуки
Вопрос:
Возможно, это неправильный вопрос. Но у меня возникла проблема с хуком проверки пользовательской формы, который не считывает входные значения, когда они обновляются с помощью http-запроса.
Вот CodeSandbox, если вы хотите его опробовать.
Это пользовательский хук useValidatedForm
const [
profileFormData,
profileFormValidation,
validateProfileForm,
getProfileFormData
] = useValidatedForm(
profileFormInitialState,
profileFormValidationDefinitions
);
У меня есть состояние для profileFormInitialState
const [profileFormInitialState, setProfileFormInitialState] = useState({
firstName: ""
});
Я обновляю это состояние в http-запросе (фиктивный http-запрос на данный момент)
useEffect(() => {
const fn = "first name";
setProfileFormInitialState({
firstName: fn
});
setContent({ title: "My Personal Details" });
setLoading({ status: false });
}, []);
Это моя форма, которая отображается в DOM. Входное значение устанавливается через profileFormInitialState
состояние.
const form = (
<>
<FormControl
key={"profileForm"}
submit={profileFormSubmit}
form={profileFormData}
validation={profileFormValidation}
>
<InputControl
autoComplete="off"
type="text"
name="firstName"
placeholder={profileFormInitialState.firstName}
value={profileFormInitialState.firstName}
onInput={e =>
setProfileFormInitialState({ firstName: e.target.value })
}
onChange={e => e.preventDefault()}
label="First Name*"
columns="2"
position="left"
>
<ErrorMsg map="required" msg="First Name is required" />
</InputControl>
<InputButton
modifier={"Button--profile"}
disabled={!profileFormValidation.valid}
buttonText="Update Profile"
type="submit"
/>
</FormControl>
</>
);
Ниже приведен мой useValidatedForm
пользовательский хук
import { useState } from "react";
import ValidaJS from "valida-js";
function stateFactory(fields) {
return Object.keys(fields).reduce((acc, key) => {
acc[key] = {
value: fields[key],
meta: {
touched: false,
dirty: false
}
};
return acc;
}, {});
}
function emptyErrorFactory(fields) {
return Object.keys(fields).reduce((acc, key) => {
acc[key] = [];
return acc;
}, {});
}
function rulesByNameFactory(descriptors, validators) {
const descriptorBy = descriptors.reduce((acc, descriptor) => {
acc[descriptor.type] = acc[descriptor.type];
acc[descriptor.name] = acc[descriptor.name]
? acc[descriptor.name].concat([descriptor])
: [descriptor];
return acc;
}, {});
return Object.keys(descriptorBy).reduce(
(acc, key) => {
acc[key] = ValidaJS.rulesCreator(validators, descriptorBy[key]);
return acc;
},
{ default: ValidaJS.rulesCreator(validators, descriptors) }
);
}
function getDataFromState(state) {
return Object.keys(state).reduce((acc, key) => {
acc[key] = state[key].value;
return acc;
}, {});
}
function extendsValidations(key, validation, newErrors = []) {
const newValidation = {
errors: {
...validation.errors,
[key]: newErrors
}
};
newValidation["valid"] = Object.keys(newValidation.errors).every(errorKey => {
return newValidation.errors[errorKey].length === 0;
});
return newValidation;
}
function onChangeHandlerByKey(
state,
key,
setState,
setValidation,
validation,
rulesBy
) {
return event => {
const newState = {
...state,
[key]: {
...state[key],
value:
event.currentTarget.type == "checkbox"
? event.currentTarget.checked
: event.currentTarget.value,
meta: {
...state[key].meta,
dirty: true
}
}
};
const newErrors = ValidaJS.validate(
rulesBy[key],
getDataFromState(newState)
).errors[key];
setState(newState);
setValidation(extendsValidations(key, validation, newErrors));
};
}
function onClickHandlerByKey(state, key, setState) {
return _ => {
setState({
...state,
[key]: {
...state[key],
meta: {
...state[key].meta,
touched: true
}
}
});
};
}
function formDataFactory(state, setState, setValidation, validation, rulesBy) {
return Object.keys(state).reduce((acc, key) => {
acc[key] = {
meta: state[key].meta,
input: {
value: state[key].value,
onClick: onClickHandlerByKey(
state,
key,
setState,
setValidation,
validation,
rulesBy
),
onChange: onChangeHandlerByKey(
state,
key,
setState,
setValidation,
validation,
rulesBy
)
}
};
return acc;
}, {});
}
const useValidatedForm = (
fields = {},
descriptors = [],
validators = ValidaJS.validators
) => {
const initialErrorsObj = emptyErrorFactory(fields);
const initialState = stateFactory(fields);
console.log("initial state = " initialState.firstName.value);
const [state, setState] = useState(initialState);
console.log("state = " state.firstName.value);
const [validation, setValidation] = useState({
valid: true,
errors: initialErrorsObj
});
const rulesBy = rulesByNameFactory(descriptors, validators);
const form = formDataFactory(
state,
setState,
setValidation,
validation,
rulesBy
);
const getData = () => getDataFromState(state);
const setData = data => setState(stateFactory(data));
const validate = () => {
const newValidations = ValidaJS.validate(
rulesBy.default,
getDataFromState(state)
);
setValidation({
...newValidations,
errors: { ...initialErrorsObj, ...newValidations.errors }
});
return newValidations.valid;
};
return [form, validation, validate, getData, setData];
};
export default useValidatedForm;
В useValidatedForm
функции проблема, с которой я сталкиваюсь, заключается в том, что когда я отправляю форму и вызывается эта функция, initialState
правильно, она возвращает first name
, она используется в качестве начального значения для, state
но state
вернется в виде пустой строки и будет делать это до тех пор, пока я не введу входные данные, а затем она правильно обновится. Итак, я не совсем уверен, как заставить эту проверку работать, поскольку она зависит от state
значения и обновляет state
значение с помощью setState
? Любая помощь была бы высоко оценена.
Ответ №1:
При отладке вашего кода вам действительно нужно идти шаг за шагом:
-
Начните с того места, где он неисправен -> validateProfileForm -> откуда он берется?
-
проверка, возвращаемая вашим хуком -> откуда validate получает свои данные?
-
переменная состояния верхней области видимости -> откуда она берется?
-
Строка 147 useState в этой изолированной среде -> откуда useState получает это значение?
-
initialState -> имеет ли initialState правильное значение?
-
Да -> почему useState не использует initialState ?
-
О, подождите, useState использует initialParameters ТОЛЬКО при первом вызове, поэтому я не могу использовать параметр для повторной инициализации значений. Поскольку вы задаете initialState позже, первое значение, которое было у useState, теперь застряло в состоянии, если вы не используете setState
Итак, теперь ваши варианты таковы: используйте setState только при изменении initialState (потому что, если вы будете делать это каждый раз, вы будете придерживаться начальных значений), но поскольку ваш stateFactory всегда возвращает новый объект, я не рекомендую этого (вам пришлось бы глубоко сравнивать)
или
Предоставьте обязательный метод для повторной инициализации состояния, таким образом, вам не придется иметь дело с возможно раздражающей системой различий.
Кроме того, причина, по которой вы вообще получаете «first name» во входных данных, в первую очередь, заключается в том, что вы задаете его значение с помощью value={profileFormInitialState.firstName}
, когда ваша форма должна быть той, которая обрабатывает это за вас.