useState在useEffect中无法按预期工作

时间:2019-09-20 14:30:09

标签: reactjs

我想跟踪用户是在页面顶部还是向下滚动以相应地更改标题。这是我的代码。

const [isTop, changeIsTop] = useState(true)

useEffect(() => {
  const handleScroll = () => {
    changeIsTop(window.scrollY < 100)
  }

  window.addEventListener("scroll", handleScroll)
  return () => {
    window.removeEventListener("scroll", handleScroll)
  }
}, [])

changeisTop不执行任何操作。 isTop始终设置为其初始值。知道为什么会这样吗?

2 个答案:

答案 0 :(得分:2)

您的代码可以正常工作。您正在以正确的方式设置事件监听器。

useEffect(() =>{
    const listener = () => {}
    window.addEventListener('click', listener)
    return () => window.removeEventListener('click', listener)
},[])

此效果将仅在组件的首次挂载(相当于componentDidMount)中运行。装入组件后,侦听器将附加到全局click事件,并且每次发生click时都会调用listener。您的问题是在哪里印刷价值。请记住,useEffect只会运行一次,因此,如果您在console.loguseEffect会看到它的初始值(在附加listener之前)。

useEffect(() =>{
    const listener = () => {}
    window.addEventListener('click', listener)
    console.log('only run once')
    return () => window.removeEventListener('click', listener)
},[])

要正确设置console.log的值,请将其放在useEffect之外

useEffect(() =>{
    const listener = () => {}
    window.addEventListener('click', listener)
    return () => window.removeEventListener('click', listener)
},[])
console.log('run every render')

Edit late-leaf-mnncu

答案 1 :(得分:1)

您的代码应该可以正常运行(经过测试),我只建议在初始安装时仅一次注册您的回调:

 const [isTop, changeIsTop] = useState(true);

 const handleScroll = useCallback(() => {
    changeIsTop(window.scrollY < 100)
  }, []);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll)
    return () => { window.removeEventListener("scroll", handleScroll) }
  }, [handleScroll])
  

但是为什么isTop的控制台日志总是在其中返回true。

您的初始isTop值为true。由于useEffect是在initialMount上运行的,因此isTop(即true)将在handleScroll回调中传递。每当检测到滚动时,将触发handleScroll,但不会在组件上下文中触发,这意味着不会重新分配回调中的isTop

尝试设置

const [isTop, changeIsTop] = React.useState('bla');

,您会发现,如果您在console.log(isTop)handleScrollisTop将是'bla'(永远为字符串!),但是在您的组件中它将被转换为{在第一个boolean之后发生的{1}}。