javascript - React Hooks,渲染之间的 prop 值随机变化

标签 javascript reactjs react-hooks

我正在尝试将类组件(即 Gatsby 布局组件)转换为使用 React hooks 来管理状态的功能组件。我的目标是在到达页面中的某个点并向上滚动时打开一个模式。

我面临的问题是,我传递给此布局组件(TrustedInView,请参阅下面的代码)的一个 Prop 正在更改渲染之间的值,如控制台日志所示。所以我对那里发生的事情感到困惑。我希望 prop 的值始终相同,因为 index.js 中记录的值不会改变(也不应该改变)。

这是布局组件中的代码:

// Hook
const usePrevious = value => {
  const ref = useRef();

  // Store current value in ref
  useEffect(() => {
    ref.current = value;
  }, [value]); // Only re-run if value changes

  // Return previous value (happens before update in useEffect above)
  return ref.current;
};

let notOpenedYet = true;
// Layout component
const Layout = ({ intl, children, trustedInView }) => {
  const [modalIsOpen, setOpen] = useState(false);

  // Get the previous value (was passed into hook on last render)
  let prevPosition = usePrevious(window.scrollY);

  const handleNavigation = e => {
    const custWindow = e.currentTarget;
    if (prevPosition > custWindow.scrollY) {
      console.log('trustedInView when goes up:', trustedInView);
      if (trustedInView && notOpenedYet) {
        notOpenedYet = false;
        setTimeout(() => setOpen(true), 1500);
      }
    }
    prevPosition = custWindow.scrollY;
  };

  useEffect(() => {
    // Add event listener when component mounts:
    window.addEventListener('scroll', e => handleNavigation(e));
    // Remove event listener when component unmounts:
    return window.removeEventListener('scroll', e => handleNavigation(e));
  });

这是带有布局组件的index.js渲染方法:

 render() {
    const { intl } = this.props;
    const { activeVideo, tabs, tabNumber, trustedInView } = this.state;
    const features = [
      // some objects
    ];
    console.log('trustedInView in index :', trustedInView);
    return (
      <Layout trustedInView={trustedInView}>

这是控制台日志:

I expect the value of the prop to always be the same

有人能让我走上正轨吗?

最佳答案

在每次渲染中都会设置一个新的监听器,并且永远不会被删除。因此,在后续滚动中,您实际上会在每个事件中看到多个 console.log,其中一些通过闭包捕获了不同的 trustedInView ,使得该值看起来似乎正在更改,但实际上并未更改。

这段代码有几个问题

  1. useEffect 没有任何依赖项,因此在每次渲染时都会调用它
  2. useEffect removeEventListener 当组件卸载时,并没有真正被调用,useEffect 希望您返回一个函数,而您将返回removeEventListener 的结果。
  3. 您不会删除任何监听器,因为您正在创建一个新函数并将其传递给 removeEventListener,而不是传递给每次创建的另一个函数 addEventListener 的函数。
  4. 每个渲染都有一个新版本的handleNavigation,因此即使我们要修复以前的错误,我们也必须确保删除正确的监听器。幸运的是,这就是 useCallback 的用途。

考虑到这些因素将如下所示:

const handleNavigation = useCallback(() => { /* ... */ }, [prevPosition, trustedInView])

useEffect(() => {
  window.addEventListener('scroll', handleNavigation);
  return () => {
    window.removeEventListener('scroll', handleNavigation);
  }
}, [handleNavigation])

但即使这样也是有问题的,因为每次破坏效果时 prevPosition 都会发生变化,而且 plus 对于您的目的来说也会变得陈旧。因此,最好直接使用 ref,而不使用 usePrevious 钩子(Hook)

const prevValueRef = useRef(0);

const handleNavigation = useCallback(e => {
  const scrollY = e.currentTarget.scrollY;
  const prevScrollY = prevValueRef.current;

  // Save the value for the next event
  prevValueRef.current = scrollY;

  // ...

}, [trustedInView])

关于javascript - React Hooks,渲染之间的 prop 值随机变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58473633/

相关文章:

javascript - react useEffect 异步警告?

javascript - 很难理解 d3.range.js 和 d3.scale.linear

javascript - React Native 中组件之间的通信(子 -> 父)

reactjs - 使用 sessionStorage 刷新时 React 状态对象变成 "[object Object]"

reactjs - 我可以依赖组件中的 useEffect 顺序吗?

reactjs - React - useState 未设置初始值

javascript - Angular JS 当前范围是什么?

javascript - 如何通过 npm 更改用于引导 reactJs 的默认配置文件

javascript - 使用 React 和 Javascript 保存我动态创建的数据的最佳方法是什么?

javascript - 外部js文件中的Reactjs组件有时无法加载