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

时间:2019-03-27 22:44:04

标签: javascript reactjs validation react-hooks

这可能不是正确的问题。但是我遇到了一个自定义表单验证挂钩的问题,当通过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值吗?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

调试代码时,您必须逐步进行操作:

  • 从有问题的地方开始-> validateProfileForm->它从哪里来?

  • 您的钩子返回的验证->验证从哪里获取数据?

  • 上限状态变量->它从哪里来?

  • sandbox->中的
  • useState行147是useState从哪里获取它的值?

  • initialState->具有initialState正确的值吗?

  • 是->为什么useState不使用initialState?

  • 哦,等等,useState仅在第一次调用时才使用initialParameters,因此我无法使用该参数来重新初始化值。因为您之后设置了initialState,所以除非您使用setState

  • ,否则useState的第一个值现在会停留在状态中

现在,您的选项是,仅在initialState更改时才使用setState(因为如果每次都被初始值所卡住,则使用setState),但是因为stateFactory始终返回一个新对象,所以我不建议使用(您必须进行深入比较)

OR

提供一种强制性的方法来重新初始化状态,这样您就不必处理可能令人讨厌的差异系统。

首先,您甚至在输入中获得“名字”的原因是,当表单应由您自己处理时,您用value={profileFormInitialState.firstName}设置了它的值。

相关问题