簡體   English   中英

除非單擊 ReactJs,否則無法識別輸入字段值

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM