React 函数组件中对window添加事件监听resize导致回调不能获得Hooks最新状态的问题解决思路

React 函数组件中对window添加事件监听resize导致回调不能获得Hooks最新状态的问题解决思路

这几天在忙着把自己做的项目中的类组件转化为功能相同的函数组件,首先先贴一份该组件类组件的关键代码:

import React, {Component} from "react" // 防抖组件 import { debounce } from 'throttle-debounce'; // 引入connect用于连接UI组件与redux import {connect} from "react-redux" class HandSendMsg extends Component { state = { w_width:window.innerWidth, w_height:window.innerHeight, } // 向UE4像素流发送消息 emitMessageToUE4 = (msgObj) => { // this.props.video_ref证明已经挂载了UE4像素流推送标签组件 if(this.props.video_ref && this.props.video_ref.current && this.props.video_ref.current.emitMessage instanceof Function) { this.props.video_ref.current.emitMessage(msgObj); } } // 修改分辨率的函数回调 handleResize = () => { let w_width = window.innerWidth let w_height = window.innerHeight this.setState({w_width, w_height}) this.emitMessageToUE4({ time:Date.now(), width:w_width, height:w_height, funcName:"onResize" }) } componentDidMount() { // 给全局绑定一个修改分辨率的操作, 当窗口发生变化的时候就修改分辨率,加入了防抖操作 window.addEventListener("resize", debounce(300, false, this.handleResize.bind(this))) } render() { const {w_height, w_width, iconLocList } = this.state return ( <div> </div> ) } } // 使用connect()()创建并暴露一个Count的容器组件 export default connect( state => ({ signal_state:state.signal, video_ref:state.videoRef }), { } )(HandSendMsg)

然后我试着改写成函数组件,初始关键代码是这样的:

import React, { useState, useEffect } from 'react' // 防抖组件 import { debounce } from 'throttle-debounce'; // 引入useSelector用于读取redux store中保存的数据 import { useSelector } from "react-redux" export default function HandSendMsg() { // state数据 const [wh, setWh] = useState({ width:window.innerWidth, height:window.innerHeight }) // 标志是否开始监听UE4消息,确保只监听一次 const [start_listen, setStart_listen] = useState(false) // store里保存的数据 const signal_state = useSelector(state => state.signal) const video_ref = useSelector(state => state.videoRef) // 向UE4像素流发送消息 const emitMessageToUE4 = (msgObj) => { // video_ref不为null证明已经挂载了UE4像素流推送组件 if(video_ref && video_ref.current && video_ref.current.emitMessage instanceof Function) { video_ref.current.emitMessage(msgObj); } } // 修改分辨率的函数回调 function handleResize() { let w_width = window.innerWidth let w_height = window.innerHeight emitMessageToUE4({ time:Date.now(), width:w_width, height:w_height, funcName:"onResize" }) setWh({ width:w_width, height:w_height }) } useEffect(() => { // 给全局绑定一个修改分辨率的操作, 当窗口发生变化的时候就修改分辨率 window.addEventListener('resize',debounce(300,false,handleResize)) // eslint-disable-next-line }, []) return ( <div> </div> ) }

useState钩子替代类组件中的state,useEffect副作用钩子替代类组件中的生命周期钩子(componentDidMount(), componentDidUpdate()和 componentWillUnmount()), useSelector和useDispatch来取代redux中的connect,这些都没什么好说的,但是在我给window添加onresize的事件监听的时候, 却发现只能获取到hooks的初始值,里面函数的调用根本无法获得最新的hooks值,无法更新state值:

window.addEventListener('resize',debounce(300,false,handleResize))

于是我在return里加了几个button,一个button用来获取最新的hooks的值,一个button用来调用window的onresize所调用的handleResize函数,发现在外面可以进行最新hooks的获取和更新,如下图所示:

img

在费劲周折之后,在网上看到帖子说可以对这个useEffect进行优化,使其监听一个变量,从而来使事件的绑定有效,我想了一下,要想使信息发送过去,我必须得确保signal_state这个值为true,那么我就用它来进行监听吧,当其从false改为true的时候再开启onResize的事件监听,问题不就解决了么。然后实践了一下,果然可以了,关键代码如下:

import React, { useState, useEffect } from 'react' // 防抖组件 import { debounce } from 'throttle-debounce'; // 引入useSelector用于读取redux store中保存的数据 import { useSelector } from "react-redux" export default function HandSendMsg() { // state数据 const [wh, setWh] = useState({ width:window.innerWidth, height:window.innerHeight }) // 标志是否开始监听UE4消息,确保只监听一次 const [start_listen, setStart_listen] = useState(false) // store里保存的数据 const signal_state = useSelector(state => state.signal) const video_ref = useSelector(state => state.videoRef) // 向UE4像素流发送消息 const emitMessageToUE4 = (msgObj) => { // video_ref不为null证明已经挂载了UE4像素流推送组件 if(video_ref && video_ref.current && video_ref.current.emitMessage instanceof Function) { video_ref.current.emitMessage(msgObj); } } // 修改分辨率的函数回调 function handleResize() { let w_width = window.innerWidth let w_height = window.innerHeight emitMessageToUE4({ time:Date.now(), width:w_width, height:w_height, funcName:"onResize" }) setWh({ width:w_width, height:w_height }) } useEffect(() => { // 给全局绑定一个修改分辨率的操作, 当窗口发生变化的时候就修改分辨率 // 只有signal_state即信令状态由false改为true的时候才添加一次事件 if(signal_state) { window.addEventListener('resize',debounce(300,false,handleResize)) } // eslint-disable-next-line }, [signal_state]) return ( <div> </div> ) }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzwgjf.html