如何去除 Formik Field ReactJS 的抖动

How to debounce Formik Field ReactJS

我想去抖动 Formik <Field/> 但是当我在字段中输入时,去抖动似乎不起作用。我也试过 lodash.debounce、油门去抖和相同的结果。如何解决?

CodeSandbox - https://codesandbox.io/s/priceless-nobel-7p6nt

片段:

import ReactDOM from "react-dom";
import { withFormik, Field, Form } from "formik";

const App = ({ setFieldValue }) => {
  let timeout;
  const [text, setText] = useState("");

  const onChange = text => {
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => setText(text), 750);
  };

  return (
    <Form>
      <Field
        type="text"
        name="textField"
        placeholder="Type something..."
        onChange={e => {
          onChange(e.target.value);
          setFieldValue("textField", e.target.value);
        }}
        style={{ width: "100%" }}
      />
      <br />
      <br />
      <div>output: {text}</div>
    </Form>
  );
};

const Enhanced = withFormik({
  mapPropsToValues: () => ({
    textField: ""
  }),
  handleSubmit: (values, { setSubmitting }) => {
    setSubmitting(false);
    return false;
  }
})(App);

ReactDOM.render(<Enhanced />, document.getElementById("root"));
  const [text, setText] = useState("");
  const [t, setT] = useState(null);

  const onChange = text => {
    if (t) clearTimeout(t);
    setT(setTimeout(() => setText(text), 750));
  };

我建议将调用移到超时函数中。

const App = ({ setFieldValue }) => {
      let timeout;
      const [text, setText] = useState("");

      const onChange = text => {
        if (timeout) clearTimeout(timeout);
        timeout = setTimeout(() => {
          setText(text);
          //changing value in container
          setFieldValue("textField", text);
        }, 750);
      };

      return (
        <Form>
          <Field
            type="text"
            name="textField"
            placeholder="Type something..."
            onChange={e => {
              onChange(e.target.value);
            }}
            style={{ width: "100%" }}
          />
          <br />
          <br />
          <div>output: {text}</div>
        </Form>
      );
    };

使用自定义挂钩

这是从@Skyrocker提供的答案中抽象出来的

如果您发现自己经常使用此模式,则可以将其抽象为 custom hook

hooks/useDebouncedInput.js

const useDebouncedInput = ({ defaultText = '', debounceTime = 750 }) => {
  const [text, setText] = useState(defaultText)
  const [t, setT] = useState(null)

  const onChange = (text) => {
    if (t) clearTimeout(t)
    setT(setTimeout(() => setText(text), debounceTime))
  }

  return [text, onChange]
}

export default useDebouncedInput

components/my-component.js

const MyComponent = () => {
  const [text, setTextDebounced] = useDebouncedInput({ debounceTime: 200 })

  return (
    <Form>
      <Field
        type="text"
        name="textField"
        placeholder="Type something..."
        onChange={(e) => setTextDebounced(e.target.value)}
      />
      <div>output: {text}</div>
    </Form>
  )
}

使用 Redux、获取和验证的示例

下面是使用自定义钩子去抖字段验证器的部分示例。

注意:我确实注意到字段验证似乎没有验证 onChange 但你可以期待它 onBlur 当你在你的去抖动更新执行后离开字段时(我没有尝试赛车它或者长时间去抖动看看会发生什么)。这可能是一个应该打开的错误(我正在打开票证)。

hooks/use-debounced-validate-access-code.js

const useDebouncedValidateAccessCode = () => {
  const [accessCodeLookUpValidation, setAccessCodeLookUpValidation] = useState()
  const [debounceAccessCodeLookup, setDebounceAccessCodeLookup] = useState()
  const dispatch = useDispatch()

  const debouncedValidateAccessCode = (accessCodeKey, debounceTime = 500) => {
    if (debounceAccessCodeLookup) clearTimeout(debounceAccessCodeLookup)

    setDebounceAccessCodeLookup(
      setTimeout(
        () =>
          setAccessCodeLookUpValidation(
            dispatch(getAccessCode(accessCodeKey)) // fetch
              .then(() => undefined)               // async validation requires undefined for no errors
              .catch(() => 'Invalid Access Code'), // async validation expects a string for an error
          ),
        debounceTime,
      ),
    )

    return accessCodeLookUpValidation || Promise.resolve(undefined)
  }

  return debouncedValidateAccessCode
}

some-component.js

const SomeComponent = () => {
  const debouncedValidateAccessCode = useDebouncedValidateAccessCode()

  return (
    <Field
      type="text"
      name="accessCode"
      validate={debouncedValidateAccessCode}
    />
  )
}