Использование lodash debounce для установки состояния в реакции JS - PullRequest
1 голос
/ 08 июля 2019

Я знаю, что об этом спрашивали тысячу раз, но я не уверен, как я делаю это неправильно.Я прочитал тонны stackoverflow и что я ищу: Как я делаю это неправильно?

Поэтому рассмотрим следующую функцию:

setFormValues(fieldName, value) {
    let values = this.state.values;

    values[fieldName] = value;

    this.setState({
      values: values
    }, () => {
      debounce(() => {
        this.setState({
          validationErrors: validator(this.state.validations, this.state.values),
        });
      }, 350);
    });
  }

Каждый раззначение элемента изменяется, мы передаем его в форму (каждый элемент является собственным классом реакции, который вызывает эту функцию).После того, как мы установили значение в форме (используется для публикации данных), мы должны проверить значения формы.

Давайте посмотрим на файл валидатора:

import React from 'react';
import moment from 'moment';
import startCase from 'lodash.startcase';

/**
 * Validate the form values.
 */
export const validator = (validations, formValues) => {
  let validationMessages = {};

  for (const key in formValues) {
    if (validations.hasOwnProperty(key)) {
      const fieldValidations  = validations[key];
      const validationMessage = createValidationMessage(key, formValues[key], formValues, fieldValidations);

      if (validationMessage !== null) {
        validationMessages[key] = validationMessage;
      }
    }
  }

  return validationMessages;
}

/**
 * Create a a validation message when the validatiom for the form value
 * fails against the form field validations.
 */
const createValidationMessage = (fieldName, value, formValues, fieldValidations) => {
  let message = null;

  fieldValidations.forEach((fieldValidation) => {
    if (fieldValidation.hasOwnProperty('cannot_percede_field')) {
      if (formValues.hasOwnProperty(fieldValidation.cannot_percede_field)) {
        const formValue = formValues[fieldValidation.cannot_percede_field];

        if (isFutureDate(value, fieldValidation)) {
          message = {
            message: 'Date cannot be greator then today.',
            isError: fieldValidation.show_error,
          };
        }

        if (isDateLessThen(value, formValue, fieldValidation)) {
          message = {
            message: 'Date cannot be less then: ' + startCase(fieldValidation.cannot_percede_field) + '.',
            isError: fieldValidation.show_error,
          };
        }
      }
    }
  })

  return message;
}

/**
 * Is the date in question greator then date saved?
 */
const isFutureDate = (value, validationObject) => {
  if (validationObject.cannot_be_future_date) {
    return moment(value).isAfter(moment());
  }

  return false;
}

/**
 * Is the date in question less then the date saved?
 */
const isDateLessThen = (value, formValue, validationObject) => {
  if (validationObject.cannot_percede_field) {
    return moment(value).isBefore(formValue);
  }

  return false;
}

Все, что мы делаем, этоЗацикливайте значения, проверяйте, есть ли у валидатора определенные правила для этого поля.В этом случае, если поле определяет, что оно не может предшествовать другому полю, мы сообщаем сообщение проверки, если рассматриваемое поле больше, чем сегодня, мы снова устанавливаем сообщение.

В чем проблема?

Если я снимаю отладку и оставляю ее как установку состояния с сообщениями проверки, если они есть, ввод формы очень медленный.Но проверка работает, и сообщения отображаются правильно.

, если я оставляю дебат как есть, и форсирую сбой (например, поле даты, которое предшествует другому полю даты), ничего не происходит.

Что я понимаю, так это то, что проблема может быть в том, что я вызываю debounce снова и снова, поэтому функция никогда не срабатывает.но когда вы выбираете дату, она должна вызывать ее только один раз, подождать 350 мс, а затем либо показать, либо нет ошибок валидации.

Что я делаю не так с отладкой lodashes, так что она недаже работа?

Ответы [ 2 ]

1 голос
/ 08 июля 2019

причина "медленного ввода формы" заключается в том, что вы изменили state, что не является отрицательным для React.После того, как вы изменяете состояние, компонент не перерисовывается.Он обновляется только тогда, когда отклоненный валидатор вызывает setState.

. Хотя я согласен с Ори Дрори, вы можете изменить свою логику, чтобы запустить валидацию другим способом, но вам определенно нужно прекратить изменяющееся состояние, например

setFormValues(fieldName, value) {
    let values = this.state.values;
    // here you mutate this.state.values[fieldName]!
    values[fieldName] = value;

    this.setState({
      values: values
    }
* 1007.* и вместо этого сделайте
setFormValues(fieldName, value) {
    this.setState(({values: oldValues}) => ({
      values: {
        ...oldValues,
        [fieldName]: value
      }
    }), ........

.

1 голос
/ 08 июля 2019

Debounce принимает функцию и возвращает новую функцию, которая оборачивает оригинальную. Всякий раз, когда вызывается функция-обертка, она ждет определенное время, прежде чем вызывать функцию-обертку Если обертка вызывается снова, она откладывает вызов обернутой функции на отведенное время и так далее. Debounce работает, поддерживая внутренний таймер. Когда вызывается обертка, таймер сбрасывается, и только когда определенное время прошло без сброса, вызывается упакованная функция.

Когда вы используете debounce, вам нужно определить функцию один раз и вызывать ее много раз. Если вы продолжаете создавать новую функцию (как вам нужно внутри setState), внутренний таймер функции никогда не сбрасывается, потому что вы никогда не вызываете функцию более одного раза. По сути, вы получаете одну задержку, а затем вызывается упакованная функция и т.д ...

Я бы сгенерировал validate дебазованную функцию и вызвал бы ее в обратном вызове setState:

validate = debounce(() => {
  this.setState(({ validations, values }) => ({
    validationErrors: validator(this.state.validations, this.state.values),
  }))
})

setFormValues(fieldName, value) {
  let values = this.state.values;

  values[fieldName] = value;

  this.setState({
      values: values
    }, this.validate;
  });
}

Почему ваши проверки такие медленные?

Хотя я согласен с @skyboyer, что вы не должны изменять состояние, вероятно, это не является причиной медленной проверки. Глядя на проверку, становится ясно, что для каждого нажатия клавиши скрипт запускает все проверки, относящиеся ко всем входам формы. Две из этих проверок используют момент.js для преобразования и сравнения дат, что очень медленно.

Поскольку вы знаете текущее значение при запуске setFormValues(fieldName, value), проверьте только это поле. Кроме того, один раз преобразуйте formValues[fieldValidation.cannot_percede_field] и будущие данные по одному моменту и кешируйте результат для использования, вместо преобразования при каждом нажатии клавиши, или канавном моменте, и используйте собственные js или более быструю библиотеку.

датируется моментом.js только один раз

...