首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >响应useCallback不更新函数

响应useCallback不更新函数
EN

Stack Overflow用户
提问于 2021-09-15 16:49:04
回答 1查看 1.9K关注 0票数 1

钩子useCallback不是应该在每次依赖项更改时返回更新的函数吗?

我编写了这个代码沙箱,试图将我在实际应用程序中面临的问题减少到最小的可重现性示例。

代码语言:javascript
复制
import { useCallback, useState } from "react";

const fields = [
  {
    name: "first_name",
    onSubmitTransformer: (x) => "",
    defaultValue: ""
  },
  {
    name: "last_name",
    onSubmitTransformer: (x) => x.replace("0", ""),
    defaultValue: ""
  }
];

export default function App() {
  const [instance, setInstance] = useState(
    fields.reduce(
      (acc, { name, defaultValue }) => ({ ...acc, [name]: defaultValue }),
      {}
    )
  );

  const onChange = (name, e) =>
    setInstance((instance) => ({ ...instance, [name]: e.target.value }));

  const validate = useCallback(() => {
    Object.entries(instance).forEach(([k, v]) => {
      if (v === "") {
        console.log("error while validating", k, "value cannot be empty");
      }
    });
  }, [instance]);

  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      e.stopPropagation();
      setInstance((instance) =>
        fields.reduce(
          (acc, { name, onSubmitTransformer }) => ({
            ...acc,
            [name]: onSubmitTransformer(acc[name])
          }),
          instance
        )
      );
      validate();
    },
    [validate]
  );

  return (
    <div className="App">
      <form onSubmit={onSubmit}>
        {fields.map(({ name }) => (
          <input
            key={`field_${name}`}
            placeholder={name}
            value={instance[name]}
            onChange={(e) => onChange(name, e)}
          />
        ))}

        <button type="submit">Create object</button>
      </form>
    </div>
  );
}

这是我的密码。基本上,它呈现一个基于fields的表单。字段是包含字段特征的对象列表。其中一个特点是在用户提交表单时应用onSubmitTransformer。当用户在转换值之后提交表单时,将执行验证。我将validate封装在一个useCallback钩子中,因为它使用了转换函数之前更改的instance值。

要测试代码沙箱示例,请输入first_name输入字段并提交。

预期的行为将是在控制台中看到first_name的错误日志语句,因为转换器将将其更改为“”。

validate 的问题是似乎没有正确更新。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-15 19:05:16

这似乎是一个理解如何反应生命周期工作的问题。调用setInstance不会立即更新instance,而是在下一个呈现时更新instance。类似地,validate在下一次呈现之前不会更新。因此,在您的onSubmit函数中,您通过调用setInstance来触发一个重命名器,但是在这个呈现开始时(在onSubmitTransformer函数运行之前)使用instance的值运行validate

修复此问题的一个简单方法是重构validate,以便它接受instance的值,而不是直接使用状态值。然后将instance上的值转换到setInstance之外。

下面是一个例子:

代码语言:javascript
复制
function App() {
    // setup

    const validate = useCallback((instance) => {
        // validate as usual
    }, []);

    const onSubmit = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        const transformedInstance = fields.reduce((acc, {name, onSubmitTransformer}) => ({
            ...acc,
            [name]: onSubmitTransformer(acc[name]),
        }), instance);
        setInstance(transformedInstance);
        validate(transformedInstance);
    }, [instance, validate]);

    // rest of component
}

现在唯一担心的可能是使用过时版本的instance (如果instance被更新并且onSubmit在相同的呈现中被调用)。如果您关心这一点,您可以为instance添加一个参考值,并将其用于提交和验证。这样会更接近您当前的代码。

下面是使用该方法的另一个示例:

代码语言:javascript
复制
function App() {
    const [instance, setInstance] = useState(/* ... */);

    const instanceRef = useRef(instance);
    useEffect(() => {
        instanceRef.current = instance;
    }, [instance]);

    const validate = useCallback(() => {
        Object.entries(instanceRef.current).forEach(([k, v]) => {
            if (v === "") {
              console.log("error while validating", k, "value cannot be empty");
            }
          });
    }, []);

    const onSubmit = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        const transformedInstance = fields.reduce((acc, {name, onSubmitTransformer}) => ({
            ...acc,
            [name]: onSubmitTransformer(acc[name]),
        }), instanceRef.current);
        setInstance(transformedInstance);
        validate(transformedInstance);
    }, [validate]);

    // rest of component
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69197031

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档