为了账号安全,请及时绑定邮箱和手机立即绑定

React 组件在重新渲染时创建一个新的状态实例,即使状态没有改变

React 组件在重新渲染时创建一个新的状态实例,即使状态没有改变

慕桂英3389331 2022-07-15 10:12:06
我正在使用Socket.IOwithReact.js并且我希望 websocket 仅在呈现特定组件时启动,所以我使用 websocket 作为该组件的状态,如下所示:const Comp = () => {  const [ws] = useState(socketIO());  // ... the rest of the component ...};但是当Comp重新渲染时,它会创建新的 websocket 连接,即使我没有对连接进行任何更改。过了一会儿,我最终得到了 10 多个 websocket 连接。如何使组件仅保持 1 个连接?我不希望 websocket 连接成为全球性的。
查看完整描述

1 回答

?
MYYA

TA贡献1868条经验 获得超4个赞

请记住,功能组件只是功能,您所知道的有关功能的所有常见知识都适用。尽管钩子在幕后做了一些表面上的魔术(这并不是真正的魔术,只是它们具有我们看不到的上下文信息),但组件函数中的代码按照通常的规则运行。这意味着这段代码


const Comp = () => {

  const [ws] = useState(socketIO());

  // ... the rest of the component ...

};

将始终调用socketIO,因此它可以将它的返回值传递给useState.


要只计算一次值,当组件第一次创建时,使用 ref( useRef) 来表示非状态实例信息(您可以直接存储在类组件实例上的那种东西),如下所示:


const Comp = () => {

  const {current: instance} = useRef({});

  const ws = instance.ws = instance.ws || socketIO();

  // ... the rest of the component ...

};

{}每次Comp调用时仍然会评估,但开销是微不足道的。你得到的useRef是一个对象,它的current属性引用了一个可变对象——在组件的整个生命周期中总是相同的。第二行使用该ws对象的属性,如果它是虚假的,则第一次初始化它。


useRef文档中提到了这种用法:


然而,useRef()有用的不仅仅是 ref 属性。它可以方便地保留任何可变值,类似于您在类中使用实例字段的方式。


这是一个带有替代的示例 for socketIO:

const { useRef, useState } = React;


function socketIO() {

  console.log("socketIO called");

  return {};

}


const Comp = () => {

  console.log("Comp called");

  const {current: instance} = useRef({});

  const ws = instance.ws = instance.ws || socketIO();

  const [counter, setCounter] = useState(0);

  

  return (

    <div>

      {counter} <input type="button" value="+" onClick={() => setCounter(c => c + 1)} />

    </div>

  );

};


ReactDOM.render(<Comp/>, document.getElementById("root"));

<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

我应该注意,useMemo在这里使用它很诱人,但useMemo不能保证您构建记忆结果的函数不会被第二次调用。useMemo是为了性能优化,而不是语义。从文档

您可能依赖 useMemo 作为性能优化,而不是语义保证。将来,React 可能会选择“忘记”一些先前记忆的值并在下一次渲染时重新计算它们,例如为屏幕外组件释放内存。编写你的代码,让它在没有的情况下仍然可以工作useMemo——然后添加它以优化性能。

(他们的重点)


查看完整回答
反对 回复 2022-07-15
  • 1 回答
  • 0 关注
  • 114 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信