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

Trí Phan

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 ...
};

But when the Comp re-rendering, it creates new websocket connection, even I don't make any changes on the connection. After a while, I end up with more than 10 websocket connections. How can I make the component keep 1 connection only? I don't want the websocket connection to become global.

T.J. Crowder

Remember that functional components are just functions, all the usual things you know about functions apply. Although hooks do some seeming-magic behind the scenes (it's not really magic, it's just they have context information we don't see), the code in your component function runs according to the usual rules. That means this code

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

will always call socketIO so it can pass the return value of it into useState.

To calculate a value only once, when the component is first created, use a ref (useRef) to represent non-state instance information (the kind of thing you would have stored directly on a class component instance), like this:

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

The {} is still evaluated every time Comp is called, but that overhead is trivial. What you get back from useRef is an object with a current property referring to a mutable object — one which will always be the same throughout the lifetime of the component. The second line uses the ws property of that object, initializing it the first time if it's falsy.

This usage is called out in the useRef docs:

However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

Here's an example with a stand-in 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>


I should note that it's tempting to use useMemo here, but useMemo doesn't guarantee that your function to build the memoized result won't be called a second time. useMemo is for performance optimization, not semantics. From the docs:

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

(their emphasis)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

React component not re-rendering on state change

React: Parent component re-renders all children, even those that haven't changed on state change

ReactJS: checkbox state persists even after re-rendering of Component

React-Redux : Child component not re-rendering on parent state change when using connect() in child

Which React cycle method should I use to re-render component when redux state has changed?

React.js component not re-rendering children when the useState hook changes state

React component not re-rendering when state changes

React component not rerendring when state is changed with Redux

React-Native-Component not rendering when state is changed

How to prevent Re-render in react when the state is changed using Functional Component

React not re-rendering component after page refresh, even if the state changes

React not re-rendering when state changed from hook

React/react hooks: child component is not re-rendering after the state is changed?

React-redux component is not re-rendering after state change

React-Redux connect() not updating component even when the state is changed without mutation

React component is rendering but not updating when state is updating

How to stop re render child component when any state changed in react js?

React-Redux re-rendering after state changed

React - Component is rendered unnecessarily when state is changed

React component not refreshing when state changed

React Component does not re rendering when updating an object in state hooks

React function component state not re-rendering?

Component not re-rendering when state is changed

React component rendering even when there is no change in the state value

react is not re-rendering component inside array.map, when state of array changes

App is not rendering even when the state in the redux is changed

Why React Parent not re-rendering when it's local state it's changed by children

React component not re-rendering on component state change

why child components div in react is not re rendering even though child component re renders at state change

TOP Ranking

HotTag

Archive