[英]Input field value not recognized unless clicked on ReactJs
I am building a form with react which has three fields: Make, Model and ZipCode.我正在构建一个包含三个字段的 react 表单:Make、Model 和 ZipCode。 The zipcode field comes with a default value, so when I choose a make and a model and click the submit button, it should pull that make and model in the selected zipcode, however, the zipcode input field will tell me to enter a valid zipcode unless I click on the zipcode input before hitting submit:邮政编码字段带有默认值,因此当我选择品牌和 model 并单击提交按钮时,它应该在所选邮政编码中拉出该品牌和 model,但是,邮政编码输入字段将告诉我输入有效的邮政编码除非我在点击提交之前点击邮政编码输入:
A console.log
of the zipcode field will show the default zipcode is in the input, however, it will only be recognized after clicking in the input itself.邮政编码字段的console.log
将显示默认邮政编码在输入中,但是,只有在单击输入本身后才能识别它。
The complete component:完整的组件:
// Packages
import { useEffect, useState } from 'react';
// Definitions
import { IPlainObject } from '@/def/IPlainObject';
import { IModel } from '@/def/IModel';
// Components
import Box from '@/comp/box';
import Button from '@/comp/button';
import Input from '@/comp/form-elements/input';
import Select from '@/comp/form-elements/select';
// Contents
import { labelHelper } from '@/contents/index';
// Context
import { useAppContext } from '@/ctx/app-context';
// Services
import { getZipCodeInfo as getZipCodeInfoService, getZipCodeByIPAddress } from '@/src/services';
import { getCampaignModels } from '@/util/get-campaign-makes';
const FormOne: React.FC<IPlainObject> = (props) => {
const { buttonText, makes, campaign } = props;
const {
state: { zipCodeInfo },
setSelectedMake,
setSelectedModel,
setZipCodeInfo,
} = useAppContext();
const [cue, setCue] = useState<string>('make');
const [error, setError] = useState<string>('');
const [models, setModels] = useState<IModel[]>(props.models || []);
const [isLoading, setIsLoading] = useState<boolean>(false);
const preSelectedMakeName: string = props.preSelectedMake !== undefined ? props.preSelectedMake.seoName : '';
const preSelectedModelName: string = props.preSelectedModel !== undefined ? props.preSelectedModel.seoName : '';
useEffect(() => {
const getZipcodeIP = async () => {
const zipcodeByIPAddress = await getZipCodeByIPAddress();
setZipCodeInfo({
city: zipcodeByIPAddress.city,
state: zipcodeByIPAddress.state,
zip: zipcodeByIPAddress.code,
});
};
getZipcodeIP();
}, []);
const fields = [
{
field: 'make',
value: preSelectedMakeName,
empty: preSelectedMakeName.length !== 0 ? false : true,
error: false,
success: preSelectedMakeName.length !== 0 ? true : false,
},
{
field: 'model',
value: preSelectedModelName,
empty: preSelectedModelName.length !== 0 ? false : true,
error: false,
success: preSelectedModelName.length !== 0 ? true : false,
},
{ field: 'zip-code', value: '', empty: true, error: false, success: false },
];
const [formFields, setFormFields] = useState<object[]>(fields);
useEffect(() => {
if (props.preSelectedMake) setSelectedMake(props.preSelectedMake);
if (props.preSelectedModel) setSelectedModel(props.preSelectedModel);
}, []);
// Find next empty and update cue
const updateInputs = (doError: boolean) => {
setError('');
setCue('');
for (let i = 0; i < formFields.length; i++) {
const current = formFields[i]['field'];
const empty = formFields[i]['empty'];
const formFieldError = formFields[i]['error'];
let next = '';
i < 2 ? (next = formFields[i + 1]['field']) : (next = '');
switch (true) {
case empty:
setCue(current);
if (doError) {
setError(current);
}
return;
case formFieldError:
setCue(current);
setError(current);
return;
default:
setCue(next);
}
}
};
const handleMakeChange = (makeName: string) => {
const make = makes.filter((make) => make.seoName === makeName) || [];
setSelectedMake(make.length !== 0 ? make[0] : {});
setSelectedModel({});
};
const handleModelChange = (modelName: string) => {
const model = models.filter((model) => model.seoName === modelName) || [];
setSelectedModel(model.length !== 0 ? model[0] : {});
};
const getModelsByMake = async (makeName: string) => {
const make = makes.find((make) => make.seoName === makeName);
if (makeName !== '') {
const { models } = await getCampaignModels(campaign, make);
setModels(models || []);
} else {
setModels([]);
}
};
const getZipCodeInfo = async (zipCode: string) => {
setIsLoading(true);
let zipCodeData = await getZipCodeInfoService(zipCode);
if (zipCodeData.length !== 0 && zipCodeData[0]['status'] === undefined) {
zipCodeData = zipCodeData[0].zipcodes[0];
setZipCodeInfo({
city: zipCodeData.default_city,
state: zipCodeData.state_abbreviation,
zip: zipCodeData.zipcode,
});
} else {
setZipCodeInfo({});
}
setIsLoading(false);
};
const validateDropdown = (
e: React.ChangeEvent<HTMLSelectElement>,
inputIndex: number,
changeHandler: (value: string) => void
) => {
const value = e.target.value;
const newFormFields = [...formFields];
const formFieldMake = { ...newFormFields[0] };
const formFieldModel = { ...newFormFields[1] };
changeHandler(value);
switch (true) {
case inputIndex === 0:
if (value) {
Object.assign(formFieldMake, { empty: false, error: false, value });
} else {
Object.assign(formFieldMake, { empty: true, error: false, value });
}
Object.assign(formFieldModel, { empty: true, error: false, value: '' });
newFormFields[inputIndex] = formFieldMake;
newFormFields[1] = formFieldModel;
setModels([]);
getModelsByMake(value);
break;
case inputIndex === 1:
if (value) {
Object.assign(formFieldModel, { empty: false, error: false, value });
} else {
Object.assign(formFieldModel, { empty: true, error: false, value });
}
newFormFields[inputIndex] = formFieldModel;
break;
}
setFormFields(newFormFields);
};
const setZipCode = (value: string) => {
const zipRegex = /^\d{5}$|^\d{5}$/;
const newFormFields = [...formFields];
const formField = { ...newFormFields[2] };
if (zipRegex.test(value)) {
Object.assign(formField, { empty: false, value: value });
} else {
setZipCodeInfo({});
Object.assign(formField, { empty: true, error: false, success: false, value: '' });
}
newFormFields[2] = formField;
setFormFields(newFormFields);
};
const handlerZipBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setZipCode(e.target.value);
};
const validateZipCode = async (e: React.ChangeEvent<HTMLInputElement>) => {
const zipRegex = /^\d{5}$|^\d{5}$/;
const value = e.target.value;
setZipCode(value);
if (zipRegex.test(value)) getZipCodeInfo(value);
};
const handleSubmit = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
updateInputs(true);
const errorInputs = formFields.filter((item) => item['empty'] || item['error']);
if (errorInputs.length === 0 && props.onSubmit !== undefined) {
props.onSubmit();
}
};
// Ctrl + R on Firefox when the input is not empty
const valueFromReload = (value: string) => {
const zipRegex = /^\d{5}$|^\d{5}$/;
setZipCode(value);
if (zipRegex.test(value)) getZipCodeInfo(value);
};
useEffect(() => {
updateInputs(false);
}, [formFields]);
useEffect(() => {
if (formFields[2]['value'] !== '') {
if (!isLoading) {
const newFormFields = [...formFields];
const formField = { ...newFormFields[2] };
Object.assign(formField, {
error: zipCodeInfo.city === undefined,
success: zipCodeInfo.city !== undefined,
});
newFormFields[2] = formField;
setFormFields(newFormFields);
}
}
}, [zipCodeInfo]);
return (
<Box
step="1"
totalSteps="3"
title={labelHelper.staticLabel('stepOneFormTitle')}
subtitle={labelHelper.staticLabel('stepOneFormSubtitle')}
>
<Select
id="make"
initialValue={preSelectedMakeName}
name="make"
label="Make"
cue={cue === 'make'}
error={error === 'make'}
message="Select a"
options={makes}
handlerChange={(e) => validateDropdown(e, 0, handleMakeChange)}
/>
<Select
id="model"
initialValue={preSelectedModelName}
name="model"
label="Model"
cue={cue === 'model'}
error={error === 'model'}
message="Select a"
options={models}
handlerChange={(e) => validateDropdown(e, 1, handleModelChange)}
/>
<Input
id="zip-code"
name="zip-code"
label="Zip Code"
icon="#icon-location"
cue={cue === 'zip-code'}
error={error === 'zip-code'}
success={!cue && zipCodeInfo.city !== undefined}
type="tel"
message="Please enter a valid"
length={5}
value={zipCodeInfo.zip}
handlerBlur={handlerZipBlur}
handlerChange={validateZipCode}
handlerEffect={valueFromReload}
autocomplete="off"
onlyNumbers
/>
<Button disabled={isLoading} loading={isLoading} handlerClick={handleSubmit}>
{buttonText}
</Button>
</Box>
);
};
export default FormOne;
Your problem is that initially in cost fields
you mark your zip code as empty
and update it only on the validateZipCode
that you call on the handlerChange
of the input component.您的问题是,最初在cost fields
中,您将 zip 代码标记为empty
,并且仅在您在输入组件的handlerChange
上调用的validateZipCode
上更新它。 It means that until you click on the field on the zip code you never call the function and never update the fields
const.这意味着在您单击 zip 代码上的字段之前,您永远不会调用 function 并且永远不会更新fields
const。
A solution will be to call initially this function, for example in the initially useEffect
when you update zipCodeInfo
state.一个解决方案是首先调用此 function,例如在您更新zipCodeInfo
useEffect
时的初始 useEffect 中。 In this way, you update the empty
value of fields
before you click the submit
button.这样,您可以在单击submit
按钮之前更新fields
的empty
值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.