React类组件访问useRef变量current属性报TypeError问题排查
错误原因
- 违反React Hook使用规则:所有React Hook(包括
useRef、useWebSocket、useMemo、useCallback等)只能在函数组件或自定义Hook的顶层调用,禁止在类组件的内部函数中使用。你代码中定义的Component是类组件Home的实例方法,在这里调用Hook本身就不符合React规则,无法正常执行。 - 变量访问逻辑完全错误:你在
Component方法内部定义的messageHistory是函数内部的局部变量,不会自动挂载到Component类上,你在render中写的Component.messageHistory实际是尝试访问React基类Component的静态属性,这个属性从始至终都不存在,读取其current属性自然会抛出undefined相关报错。 - 定义的方法从未执行:你只是声明了
Component这个实例方法,没有在任何地方调用过,就算没有Hook限制,方法内部的变量也从未完成初始化,根本不可能被外部访问到。 - 命名冲突:你将类的实例方法命名为
Component,和你导入的React基类Component重名,会引发额外的作用域混淆问题。
解决方案
优先推荐方案1,直接将类组件改为函数组件,适配你现有的Hook用法,改造成本极低:
import React, { useCallback, useMemo, useRef } from "react"; import useWebSocket, { ReadyState } from 'react-use-websocket'; import Select from "react-select" import "./index.scss"; export default function Home() { const socketUrl = process.env.REACT_APP_WEBSOCKET_URL; const { sendJsonMessage, lastJsonMessage, readyState } = useWebSocket( socketUrl, ); const messageHistory = useRef([]) messageHistory.current = useMemo( () => messageHistory.current.concat(lastJsonMessage ?? []), [lastJsonMessage], ); const handleClickSendMessage = useCallback(() => sendJsonMessage({ event: 'subscribe', subscription: 'test.test', }), [sendJsonMessage], ); const handleClickUnSendMessage = useCallback(() => sendJsonMessage({ event: 'unsubscribe', subscription: 'test.test', }), [sendJsonMessage], ); const connectionStatus = { [ReadyState.CONNECTING]: 'Connecting', [ReadyState.OPEN]: 'Open', [ReadyState.CLOSING]: 'Closing', [ReadyState.CLOSED]: 'Closed', [ReadyState.UNINSTANTIATED]: 'Uninstantiated', }[readyState]; return ( <React.Fragment> <div className="container"> <div className="signals"> <button onClick={handleClickSendMessage} disabled={readyState !== ReadyState.OPEN} > Subscribe </button> <button onClick={handleClickUnSendMessage} disabled={readyState !== ReadyState.OPEN} > Unsubscribe </button> <span>The WebSocket is currently {connectionStatus}</span> {lastJsonMessage ? ( <span> Last message:{' '} {JSON.stringify(lastJsonMessage.data, null, 4)} </span> ) : null} <ul> {messageHistory.current.map((message, idx) => ( <span key={idx}> {JSON.stringify(message.data, null, 4)} </span> ))} </ul> </div> </div> </React.Fragment> ); }
如果必须保留类组件结构,可以将使用Hook的逻辑抽为独立的子函数组件,在类组件的render方法中渲染该子组件即可:
// 抽离的子函数组件 const WsContent = () => { // 把上面所有Hook逻辑、渲染内容都放在这里 } // 原有类组件 export default class Home extends Component { render() { return <WsContent /> } }
内容的提问来源于stack exchange,提问作者zkbitcoin.com
相关产品推荐
相关产品推荐

