使用lodash防反跳来设置react js中的状态

时间:2019-07-07 22:57:07

标签: javascript reactjs lodash debounce

我知道这个问题已经被问了一千遍了,但是我不确定我是怎么做的。我已经阅读了大量的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);
    });
  }

每次元素值更改时,我们都将其传递给表单(每个元素都是调用此函数的其自己的react类)。在表单中设置了值(用于过帐数据)之后,我们必须验证表单的值。

让我们看看验证器文件:

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;
}

我们要做的就是遍历值,检查验证器是否对该字段具有特定规则。在这种情况下,如果该字段的状态不能在另一个字段之前,则说明验证消息,如果所讨论的字段大于当前字段,则再次设置该消息。

出了什么问题?

如果我取消反跳,并且将其设置为带有验证消息的状态,那么如果有的话,表单输入将非常缓慢。但是验证有效并且消息正确显示。

如果我将防抖保持不变,并强制失败(例如,日期字段位于另一个日期字段之前),则不会发生任何事情。

我了解的问题是,我可能一遍又一遍地调用去抖,因此该函数从不触发。但是选择日期时,它只能调用一次,等待350毫秒,然后显示或不显示验证错误。

我在用去污线去抖(甚至无法正常工作)时怎么办?

2 个答案:

答案 0 :(得分:1)

去抖动使用一个函数,并返回一个包装原始函数的新函数。每当调用包装函数时,它都会在调用包装函数之前等待定义的时间。如果再次调用包装器,则将包装的函数的调用延迟指定的时间,依此类推。去抖动通过维护内部计时器来工作。每当调用包装器时,计时器都会重置,并且仅当经过定义的时间而没有重置时,才会调用包装的函数。

使用去抖动功能时,您需要定义一次函数,并多次调用它。如果您继续创建一个新函数(例如需要在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的观点,即您不应该更改状态,但这可能不是验证缓慢的原因。查看验证,很明显,对于每个按键,脚本都会运行与所有表单输入相关的所有验证。其中两个验证使用moment.js来转换和比较日期,这非常慢。

由于您在运行setFormValues(fieldName, value)时了解当前值,因此仅验证该字段。此外,只需一次转换formValues[fieldValidation.cannot_percede_field]和将来的数据,并缓存结果以供使用,而不是在每次击键或转换时转换并使用本机js或更快的库。

仅一次约会moment.js

答案 1 :(得分:1)

“表单输入缓慢”的原因是您突变了state,这对React来说是不行的。突变后,状态组件不会重新呈现。仅当去抖动的验证程序调用{​​{1}}时才会更新。

虽然我同意Ori Drori的意见,但您可能会改变您的逻辑,以肯定需要停止突变状态的其他方式来运行验证

setState

然后做

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

    this.setState({
      values: values
    }

相反。