最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to debounce Formik Field ReactJS - Stack Overflow

programmeradmin0浏览0评论

I want to debounce Formik <Field/> but when I type in the field seems debounce does not work. Also I have tried lodash.debounce, throttle-debounce and the same result. How to solve this?

CodeSandbox -

Snippet:

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"));

I want to debounce Formik <Field/> but when I type in the field seems debounce does not work. Also I have tried lodash.debounce, throttle-debounce and the same result. How to solve this?

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

Snippet:

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"));
Share Improve this question asked Jul 15, 2019 at 12:11 ArthurArthur 3,5067 gold badges35 silver badges64 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 14
  const [text, setText] = useState("");
  const [t, setT] = useState(null);

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

I would like to suggest to move the call inside of timeout function.

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

Using Custom Hooks

This is abstracted from the answer provided by @Skyrocker

If you find yourself using this pattern a lot you can abstract it out to a 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>
  )
}

An Example Using Redux, Fetching, and Validation

Here's a partial example of using a custom hook for a debounced field validator.

Note: I did notice that Field validation seems to not validate onChange but you can expect it onBlur when you leave the field after your debounced update has executed (I did not try racing it or with a long debounce to see what happens). This is likely a bug that should be opened (I'm in the process of opening a ticket).

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}
    />
  )
}
发布评论

评论列表(0)

  1. 暂无评论