使用React Hooks在自定义钩子中正确设置后访问状态

问题描述 投票:0回答:1

这可能不是正确的问题。但是我遇到了自定义表单验证挂钩的问题,当通过http请求更新输入值时,它不会读取输入值。

如果你想尝试一下,这是一个CodeSandbox

这是自定义钩useValidatedForm

const [
    profileFormData,
    profileFormValidation,
    validateProfileForm,
    getProfileFormData
  ] = useValidatedForm(
    profileFormInitialState,
    profileFormValidationDefinitions
  );

我有profileFormInitialState的州

const [profileFormInitialState, setProfileFormInitialState] = useState({
    firstName: ""
  });

我在http请求中更新此状态(此时为虚拟http请求)

useEffect(() => {
    const fn = "first name";
    setProfileFormInitialState({
      firstName: fn
    });
    setContent({ title: "My Personal Details" });
    setLoading({ status: false });
  }, []);

这是我的表单,它被呈现给DOM。输入值通过profileFormInitialState状态设置。

const form = (
    <>
      <FormControl
        key={"profileForm"}
        submit={profileFormSubmit}
        form={profileFormData}
        validation={profileFormValidation}
      >
        <InputControl
          autoComplete="off"
          type="text"
          name="firstName"
          placeholder={profileFormInitialState.firstName}
          value={profileFormInitialState.firstName}
          onInput={e =>
            setProfileFormInitialState({ firstName: e.target.value })
          }
          onChange={e => e.preventDefault()}
          label="First Name*"
          columns="2"
          position="left"
        >
          <ErrorMsg map="required" msg="First Name is required" />
        </InputControl>
        <InputButton
          modifier={"Button--profile"}
          disabled={!profileFormValidation.valid}
          buttonText="Update Profile"
          type="submit"
        />
      </FormControl>
    </>
  );

下面是我的useValidatedForm定制钩子

import { useState } from "react";
import ValidaJS from "valida-js";

function stateFactory(fields) {
  return Object.keys(fields).reduce((acc, key) => {
    acc[key] = {
      value: fields[key],
      meta: {
        touched: false,
        dirty: false
      }
    };
    return acc;
  }, {});
}

function emptyErrorFactory(fields) {
  return Object.keys(fields).reduce((acc, key) => {
    acc[key] = [];
    return acc;
  }, {});
}

function rulesByNameFactory(descriptors, validators) {
  const descriptorBy = descriptors.reduce((acc, descriptor) => {
    acc[descriptor.type] = acc[descriptor.type];
    acc[descriptor.name] = acc[descriptor.name]
      ? acc[descriptor.name].concat([descriptor])
      : [descriptor];
    return acc;
  }, {});
  return Object.keys(descriptorBy).reduce(
    (acc, key) => {
      acc[key] = ValidaJS.rulesCreator(validators, descriptorBy[key]);
      return acc;
    },
    { default: ValidaJS.rulesCreator(validators, descriptors) }
  );
}

function getDataFromState(state) {
  return Object.keys(state).reduce((acc, key) => {
    acc[key] = state[key].value;

    return acc;
  }, {});
}

function extendsValidations(key, validation, newErrors = []) {
  const newValidation = {
    errors: {
      ...validation.errors,
      [key]: newErrors
    }
  };
  newValidation["valid"] = Object.keys(newValidation.errors).every(errorKey => {
    return newValidation.errors[errorKey].length === 0;
  });
  return newValidation;
}

function onChangeHandlerByKey(
  state,
  key,
  setState,
  setValidation,
  validation,
  rulesBy
) {
  return event => {
    const newState = {
      ...state,
      [key]: {
        ...state[key],
        value:
          event.currentTarget.type == "checkbox"
            ? event.currentTarget.checked
            : event.currentTarget.value,
        meta: {
          ...state[key].meta,
          dirty: true
        }
      }
    };
    const newErrors = ValidaJS.validate(
      rulesBy[key],
      getDataFromState(newState)
    ).errors[key];
    setState(newState);
    setValidation(extendsValidations(key, validation, newErrors));
  };
}

function onClickHandlerByKey(state, key, setState) {
  return _ => {
    setState({
      ...state,
      [key]: {
        ...state[key],
        meta: {
          ...state[key].meta,
          touched: true
        }
      }
    });
  };
}

function formDataFactory(state, setState, setValidation, validation, rulesBy) {
  return Object.keys(state).reduce((acc, key) => {
    acc[key] = {
      meta: state[key].meta,
      input: {
        value: state[key].value,
        onClick: onClickHandlerByKey(
          state,
          key,
          setState,
          setValidation,
          validation,
          rulesBy
        ),
        onChange: onChangeHandlerByKey(
          state,
          key,
          setState,
          setValidation,
          validation,
          rulesBy
        )
      }
    };
    return acc;
  }, {});
}

const useValidatedForm = (
  fields = {},
  descriptors = [],
  validators = ValidaJS.validators
) => {
  const initialErrorsObj = emptyErrorFactory(fields);
  const initialState = stateFactory(fields);
  console.log("initial state = " + initialState.firstName.value);
  const [state, setState] = useState(initialState);
  console.log("state = " + state.firstName.value);
  const [validation, setValidation] = useState({
    valid: true,
    errors: initialErrorsObj
  });
  const rulesBy = rulesByNameFactory(descriptors, validators);
  const form = formDataFactory(
    state,
    setState,
    setValidation,
    validation,
    rulesBy
  );

  const getData = () => getDataFromState(state);
  const setData = data => setState(stateFactory(data));
  const validate = () => {
    const newValidations = ValidaJS.validate(
      rulesBy.default,
      getDataFromState(state)
    );
    setValidation({
      ...newValidations,
      errors: { ...initialErrorsObj, ...newValidations.errors }
    });
    return newValidations.valid;
  };

  return [form, validation, validate, getData, setData];
};

export default useValidatedForm;

useValidatedForm函数中,我遇到的问题是,当我提交表单并调用此函数时,initialState是正确的,它返回first name,它用作state的初始值,但state将返回为空字符串并且我会这样做,直到我输入输入然后它正确更新。所以我不确定如何使这个验证工作,因为它依赖于state值并用state更新setState值?任何帮助将不胜感激。

javascript reactjs validation react-hooks
1个回答
0
投票

在调试代码时,您必须一步一步地进行:

  • 从错误的地方开始 - > validateProfileForm - >它来自哪里?
  • 你的钩子返回验证 - > validate从哪里获取数据?
  • 上部范围状态变量 - >它来自哪里?
  • 在这个sandbox中使用stateState 147 - > useState从哪里获取它的值?
  • initialState - > initialState是否正确?
  • 是 - >为什么useState不使用initialState?
  • 哦等等,useState只使用initialParameters第一次调用,所以我不能使用该参数重新初始化值。因为之后设置了initialState,所以除非使用setState,否则第一个值useState现在处于状态

所以现在,您的选项是,仅在initialState更改时使用setState(因为如果您每次都使用初始值时都这样做),但是因为您的stateFactory总是返回一个新对象,所以我不推荐(你必须深入比较)

要么

提供重新初始化状态的必要方法,这样您就不必处理可能烦人的差异系统。

此外,您首先在输入中获得“名字”的原因是因为您使用value={profileFormInitialState.firstName}设置它的值,当您的表单应该是为您处理的那个。

© www.soinside.com 2019 - 2024. All rights reserved.