繁体   English   中英

React 组件在重新渲染时创建 state 的新实例,即使 state 没有更改

[英]React component create a new instance of the state when re-rendering even the state is not changed

I'm using Socket.IO with React.js and I want the websocket to start only when a specific component is rendered, so I use the websocket as a state of that component, like this:

const Comp = () => {
  const [ws] = useState(socketIO());
  // ... the rest of the component ...
};

但是当Comp重新渲染时,它会创建新的 websocket 连接,即使我没有对连接进行任何更改。 过了一会儿,我得到了超过 10 个 websocket 连接。 如何使组件仅保持 1 个连接? 我不希望 websocket 连接成为全球性的。

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

const Comp = () => {
  const [ws] = useState(socketIO());
  // ... the rest of the component ...
};

始终调用socketIO以便它可以将它的返回值传递给useState

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

const Comp = () => {
  const {current: instance} = useRef({});
  const ws = instance.ws = instance.ws || socketIO();
  // ... the rest of the component ...
};

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

这种用法在useRef文档中被提及:

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

这是一个带有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不能保证您的 function 不会被第二次调用。 useMemo用于性能优化,而不是语义。 文档

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

(他们的重点)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM