简体   繁体   English

除非单击 ReactJs,否则无法识别输入字段值

[英]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按钮之前更新fieldsempty值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM