在事件监听器函数中无法访问React Hooks值

时间:2019-03-24 17:16:56

标签: javascript reactjs addeventlistener react-hooks

我决定通过windom宽度和resize事件监听器将React挂钩用于我的组件。问题是我无法访问所需的当前值。我得到在添加事件侦听器时设置的嵌套值。

将函数传递给值设置器函数对我来说不是一个解决方案,因为它会强制渲染并破坏我的其他功能。

我正在附上最简单的示例来介绍核心问题:

import React, { Component, useState, useEffect } from 'react';
import { render } from 'react-dom';

const App = () => {
  const [width, setWidth] = useState(0);

  const resize = () => {
    console.log("width:", width); // it's always 0 even after many updates
    setWidth(window.innerWidth);
  };

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

  return <div>{width}</div>;
}

render(<App />, document.getElementById('root'));

LIVE DEMO IS HERE

请寻求帮助。

2 个答案:

答案 0 :(得分:5)

每个渲染都会得到resize函数的新副本。每个副本都捕获width的当前值。您的听众有一个副本,该副本是使用width = 0在第一个渲染上创建的。

要解决此问题,您有几种选择:

  1. width更改时更新侦听器

    useEffect(() => {
      resize();
      window.addEventListener("resize", resize);
      return () => window.removeEventListener("resize", resize);
    }, [width]);
    
  2. 使用functional updates获取监听器中的当前宽度

    const resize = () => {
      setWidth(oldWidth => {
        console.log("width:", oldWidth);
        return window.innerWidth
      });
    };
    
  3. 将宽度存储在mutable reference

    const widthRef = useRef(width);
    
    const resize = () => {
      console.log("width:", widthRef.current);
      widthRef.current = window.innerWidth;
      setWidth(window.innerWidth);
    };
    

答案 1 :(得分:0)

获取数据的另一种方法是在组件之外设置变量

DEMO

import React, { Component, useState, useEffect } from 'react';
import { render } from 'react-dom';

//******* 
const appData = {
      width:0
}
//*******

const App = () => {
  const [width, setWidth] = useState(0);

  const resize = () => {
    //******* get width
    console.log('===>',appData.width);
    //*******

    console.log("width:", width); // it's always 0 even after many updates
    setWidth(window.innerWidth);
    //******* update width
    appData.width = window.innerWidth;
    //*******
  };

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

  return <div>{width}</div>;
}

render(<App />, document.getElementById('root'));