react hooks闭包问题

83 阅读1分钟

从class组件迁移到function+hooks组件,在开发中需要注意的问题,从this指向转译到了闭包。

useEffect导致的闭包问题。

先来看以下代码。在class中实现

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: this.state.count + 1,
      });
    }, 500);
  }

  render() {
    return <div>class组件中的效果:{this.state.count}</div>;
  }
}

Kapture 2023-03-17 at 18.48.46.gif

同样的逻辑在使用hooks-useEffect实现

const ClosureQ = () => {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 500);
    
    return () => {
      clearInterval(id);
    };
  }, []);
  
  return <div>闭包问题下的count: {count}</div>;
};

Kapture 2023-03-17 at 18.53.12.gif

闭包问题导致useEffect中缓存的函数中,储存了第一次加载时的count,导致每次执行interval时,都是count(0) + 1 -> 1

解决方案

使用useRef缓存count变量


export const ClosureARef = () => {

  const [count, setCount] = useState(0);
  const countSave = useRef(count);
  
  useEffect(() => {
  
    const id = setInterval(() => {
      countSave.current += 1;
      setCount(countSave.current);
    }, 500);
    
    return () => {
      clearInterval(id);
    };
  });
  
  return <div>用useRef解决闭包问题: {count}</div>;
};

Kapture 2023-03-17 at 18.57.25.gif

用useEffect第二个参数,解决闭包问题(btw:这样会重复创建触发中的函数)


export const ClosureAParam = () => {

  const [count, setCount] = useState(0);
  
  useEffect(() => {
  
    const id = setInterval(() => {
      setCount(count + 1);
    }, 500);
    
    return () => {
      clearInterval(id);
    };
    
  }, [count]);
  
  return (
    <div>
      用useEffect第二个参数,解决闭包问题(btw:这样会重复创建触发中的函数):
      {count}
    </div>
  );
};

Kapture 2023-03-17 at 19.00.09.gif

使用setCount方法传递函数,通过形参能获取到最新的count

export const ClosureACallback = () => {

  const [count, setCount] = useState(0);
  
  useEffect(() => {
  
    const id = setInterval(() => {
      setCount((oldCount) => oldCount + 1);
    }, 500);
    
    return () => {
      clearInterval(id);
    };
    
  }, []);
  
  return <div>setCount方法传递函数,通过形参能获取到最新的count: {count}</div>;
};

Kapture 2023-03-17 at 19.02.08.gif