集成去抖以响应 Formik 字段

Integrating debounce to respond to a Formik field

我的目标是用户可以在 websiteUrl 的输入字段中键入内容。一旦他停止输入,iFrameUrl 字段应在 2 秒后更新并显示 iFrame。但是,一旦我开始输入,它似乎会立即加载。

const IntegrationWebsite = ({
  isShown,
  onClose,
  onSave,
  integration,
  ...props
}) => {
  const [iFrameUrl, setIFrameUrl] = useState();

  const handleWebsiteUrlChange = (event, setFieldValue) => {
    const value = event.target.value;
    setFieldValue("websiteUrl", event.target.value);
    const debouncedSave = debounce(() => setIFrameUrl(value), 1000);
    debouncedSave();
  };

  return (
    <SideSheet
      title="Add Website"
      isOpen={isShown}
      onClose={onClose}
      onSave={onSave}
      initialValues={{
        websiteUrl: integration?.properties.websiteUrl ?? "",
      }}
      validationSchema={validationSchema}
      {...props}
    >
      {({ values, setFieldValue }) => (
        <Grid container spacing={3}>
          <Grid xs={12} item>
            <Field
              InputProps={{
                onChange: (event) =>
                  handleWebsiteUrlChange(event, setFieldValue),
              }}
              required
              name="websiteUrl"
              label="Website URL"
              component={TextField}
            />
          </Grid>
          <Grid xs={12} item>
            <iframe
              src={iFrameUrl}
              height="300"
              width="100%"
              title="Iframe Example"
            ></iframe>
          </Grid>
        </Grid>
      )}
    </SideSheet>
  );
};
export default IntegrationWebsite;

去抖函数,是一个带有内部超时的函数。每当您再次调用 same 函数时,计时器会重置,倒计时会再次开始。由于您在每次击键时都重新创建了 debounced 函数,因此永远不会再次调用该函数,而是调用包装函数。

const debouncedSave = debounce(() => setIFrameUrl(value), 1000);

为防止这种情况发生,您需要生成一次去抖动函数并将其记忆,以便始终使用相同的函数。

为此,将 Formik 包装器的内容提取到另一个组件。创建一个 memoized debounced 函数,并从 handleWebsiteUrlChange.

调用它

示例(未测试):

const IntegrationField = ({ setFieldValue }) => {
  const [iFrameUrl, setIFrameUrl] = useState();
  
  const debouncedSave = useMemo(
    () => debounce(value => setIFrameUrl(value), 1000),
    []
  );
  
  const onChange = useCallback(event => {
    const value = event.target.value;
    setFieldValue("websiteUrl", value);
    debouncedSave(value);
  }, [setFieldValue]);

  return (
    <Grid container spacing={3}>
      <Grid xs={12} item>
        <Field
          InputProps={{ onChange }}
          required
          name="websiteUrl"
          label="Website URL"
          component={TextField}
        />
      </Grid>
      <Grid xs={12} item>
        <iframe
          src={iFrameUrl}
          height="300"
          width="100%"
          title="Iframe Example"
        ></iframe>
      </Grid>
    </Grid>
  );
};

const IntegrationWebsite = ({
  isShown,
  onClose,
  onSave,
  integration,
  ...props
}) => {
  return (
    <SideSheet
      title="Add Website"
      isOpen={isShown}
      onClose={onClose}
      onSave={onSave}
      initialValues={{
        websiteUrl: integration?.properties.websiteUrl ?? "",
      }}
      validationSchema={validationSchema}
      {...props}
    >
      {formik => (
        <IntegrationField {...formik} />
      )}
    </SideSheet>
  );
};
export default IntegrationWebsite;