【问题标题】:React does not update State (React-Hooks)React 不更新状态(React-Hooks)
【发布时间】:2021-02-23 20:20:49
【问题描述】:

我有以下设置:

  const templates = [
    'dot',
    'line',
    'circle',
    'square',
  ];
  
  const [currentTemplate, setCurrentTemplate] = useState(0);

  const changeTemplate = (forward = true) => {
    let index = currentTemplate;
    index = forward ? index + 1 : index - 1;
    if (index > templates.length - 1) {
      index = 0;
    } else if (index < 0) {
      index = templates.length - 1;
    }
    setCurrentTemplate(index);
  };

  useEffect(() => {
    console.log(`Current Template is: ${templates[currentTemplate]}`);
  }, [currentTemplate]);

  useKeypress('ArrowLeft', () => {
    changeTemplate(false);
  });
  useKeypress('ArrowRight', () => {
    changeTemplate(true);
  });

这是useKeypress-Hook使用的:

  import { useEffect } from 'react';
/**
 * useKeyPress
 * @param {string} key - the name of the key to respond to, compared against event.key
 * @param {function} action - the action to perform on key press
 */
export default function useKeypress(key: string, action: () => void) {
  useEffect(() => {
    function onKeyup(e: KeyboardEvent) {
      if (e.key === key) action();
    }
    window.addEventListener('keyup', onKeyup);
    return () => window.removeEventListener('keyup', onKeyup);
  }, []);
}

每当我按向左或向右箭头键时,都会触发该功能。但是currentTemplate 变量并没有改变。它始终保持在 0。useEffect 仅在我从左到右或以其他方式切换键时触发。单击同一个键两次,不会再次触发useEffect,但它应该!从右到左变化时,输出为Current Template is: square,从左到右变化时,输出为Current Template is: line。但这永远不会改变。 currentTemplate 的值始终保持为0

我错过了什么?

【问题讨论】:

  • 我认为问题可能与useKeypress 有关。您可以通过将更改功能附加到按钮来测试这一点 - 如果这有效,则意味着故障在于useKeypress-hook。你能分享一下这个钩子的实现吗?
  • 你完全正确!使用 to 按钮时,一切正常。我包含了我的 useKeypress-Hook,但不知道这个用例可能有什么问题......

标签: reactjs react-hooks use-state


【解决方案1】:

问题在于useKeypress 挂钩中的useEffect

export default function useKeypress(key: string, action: () => void) {
  useEffect(() => {
    function onKeyup(e: KeyboardEvent) {
      if (e.key === key) action();
    }
    window.addEventListener('keyup', onKeyup);
    return () => window.removeEventListener('keyup', onKeyup);
  }, []); // ? problem
}

钩子是一个没有依赖关系的useEffect。意思是,它只会触发一次。这是您传递到此挂钩的action 的问题。每当您的组件中的changeTemplate-action 发生变化时(即在重新渲染时),对此的引用不会在useKeypress-hook 内更新。

要解决这个问题,需要将action添加到useEffect的依赖数组中:

export default function useKeypress(key: string, action: () => void) {
  useEffect(() => {
    function onKeyup(e: KeyboardEvent) {
      if (e.key === key) action();
    }
    window.addEventListener('keyup', onKeyup);
    return () => window.removeEventListener('keyup', onKeyup);
  }, [action]); // ? whenever `action` changes, the effect will be updated
}

这是为了确保在给定的action 更改时更新效果。进行此更改后,事情应该会按预期进行。

优化:

此时将为每次渲染重新生成useEffect,因为每次渲染都会实例化changeTemplate 函数。为避免这种情况,您可以用useCallback 包装changeTemplate,以便记住函数:

const changeTemplate = useCallback(
  (forward = true) => {
    let index = currentTemplate;
    index = forward ? index + 1 : index - 1;
    if (index > templates.length - 1) {
      index = 0;
    } else if (index < 0) {
      index = templates.length - 1;
    }
    setCurrentTemplate(index);
  }, 
  // Regenerate the callback whenever `currentTemplate` or `setCurrentTemplate` changes
  [currentTemplate, setCurrentTemplate]
); 

【讨论】:

  • 非常感谢。它就像一个魅力!将currentTemplatesetCurrentTemplate 设置为依赖项有什么特别的好处吗?删除后者时它的工作方式相同......
  • useState 变量可能有一些魔力,但是在引用 react hooks 的依赖数组时,最好是明确的。在此处阅读更多信息:reactjs.org/docs/hooks-reference.html#usecallback
猜你喜欢
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2019-08-08
  • 2020-12-28
  • 1970-01-01
  • 2020-01-16
  • 2021-07-27
  • 2020-06-19
相关资源
最近更新 更多