[英]Input field value not recognized unless clicked on ReactJs
我正在构建一个包含三个字段的 react 表单:Make、Model 和 ZipCode。 邮政编码字段带有默认值,因此当我选择品牌和 model 并单击提交按钮时,它应该在所选邮政编码中拉出该品牌和 model,但是,邮政编码输入字段将告诉我输入有效的邮政编码除非我在点击提交之前点击邮政编码输入:
邮政编码字段的console.log
将显示默认邮政编码在输入中,但是,只有在单击输入本身后才能识别它。
完整的组件:
// 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;
您的问题是,最初在cost fields
中,您将 zip 代码标记为empty
,并且仅在您在输入组件的handlerChange
上调用的validateZipCode
上更新它。 这意味着在您单击 zip 代码上的字段之前,您永远不会调用 function 并且永远不会更新fields
const。
一个解决方案是首先调用此 function,例如在您更新zipCodeInfo
useEffect
时的初始 useEffect 中。 这样,您可以在单击submit
按钮之前更新fields
的empty
值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.