[英]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.