我正在尝试将类组件(即 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
,使得该值看起来似乎正在更改,但实际上并未更改。
这段代码有几个问题
useEffect
没有任何依赖项,因此在每次渲染时都会调用它useEffect
removeEventListener
当组件卸载时,并没有真正被调用,useEffect 希望您返回一个函数,而您将返回removeEventListener 的结果。- 您不会删除任何监听器,因为您正在创建一个新函数并将其传递给
removeEventListener
,而不是传递给每次创建的另一个函数addEventListener
的函数。 - 每个渲染都有一个新版本的
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/