Доступ к состоянию после того, как он правильно установлен внутри пользовательского хука с помощью React Hooks - PullRequest
0 голосов
/ 28 марта 2019

Возможно, это не правильный вопрос. Но у меня проблема с хуком проверки пользовательских форм, который не читает входные значения, когда они обновляются через 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 голосов
/ 28 марта 2019

При отладке кода вам действительно нужно идти шаг за шагом:

  • Начните с того, где он неисправен -> validateProfileForm -> откуда он взялся?

  • validate, возвращаемый вашей ловушкой -> откуда validate получает свои данные?

  • верхняя переменная состояния области видимости -> откуда она взялась?

  • useState строка 147 в этой песочнице -> откуда useState получает его значение?

  • initialState -> имеет initialState правильное значение?

  • Да -> почему не используетсяState, используется initialState?

  • Ой, подождите, useState использует initialParameters ТОЛЬКО при первом вызове, поэтому я не могу использовать параметр для повторной инициализации значений. Поскольку вы устанавливаете initialState позже, первое значение useState теперь застряло в состоянии, если вы не используете setState

Итак, теперь вы можете использовать setState только при изменении initialState (потому что если вы будете делать это каждый раз, когда застрянете с начальными значениями), а поскольку ваш stateFactory всегда возвращает новый объект, я не рекомендую это (вам придется глубоко сравнить)

OR

Предоставьте императивный метод для повторной инициализации состояния, чтобы вам не приходилось иметь дело с возможно раздражающей системой разметки.

Кроме того, причина, по которой вы даже получаете «имя» во вводе, в первую очередь, в том, что вы устанавливаете его значение с помощью value={profileFormInitialState.firstName}, когда ваша форма должна быть той, которая обрабатывает это для вас.

...