Things I learned recently in React
Tags: react, webdev, javascript
Published: 2021-01-09 13:26:06


I have been working in react for more than a year now and here are the few things I learned recently which I will like to share.
Please correct me if I am wrong anywhere.


**Clean up function in useEffect**

You can optionally return a function from useEffect hook that React will run to clean up subscriptions. This function runs every time components gets unmounted. React will also clean up effect from previous render before running effect next time.

**Don't misuse useEffect**
This looks pretty simple, but ppl often make mistakes of using useEffect + useState when they should use derivedState/useMemo.
If there is some calculation that needs to be done on every render depending on changed props - Don't put in useEffect. Remember useEffect runs post render. We can totally use useMemo here if it is just some calculation. Of course if you got to make async calls and all, those are fitted for useEffect where you update your state and component re-renders.

```
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [name, setName] = useState("");
  return (
    <>
    <input
      value={name}
      onChange={(e) => {
        setName(e.target.value);
      }}
    />

    <Comp name={name} />
    </>
  );
};

const Comp = (props) => {

  console.log('rendered');
  const [title, setTitle] = useState('');

  useEffect(()=>{
    setTitle(`Hi ${props.name}`);
  }, [props.name]);

  return <div>{title}</div>;
  
}

ReactDOM.render(<App />, document.getElementById("container"));

```
Above is an example of common mistake ppl make. The Comp component renders two times unnecessarily. One for prop change and other for state update I am making in useEffect.

The title state is derived state. We don't need to useState + useEffect for it.

The Comp component can be written as 
```
const Comp = (props) => {
  console.log('rendered');
  const title  = `Hi ${props.name}`;
  return <div>{title}</div>;
}
```
Benefit - Just one render and much readable and maintainable code.
Now one can argue that I want to update only name prop gets updated. I want that optimisation. For that, useMemo is your friend.

```
const Comp = (props) => {
  console.log('rendered');
  const title  = React.useMemo(()=>{ return `Hi ${props.name}`}, [props.name]);
  return <div>{title}</div>;
}
```

**Don't use useMemo and useCallback for everything**
We all know they optimise, but these optimisations come with some cost. Use them only when there are expensive calculations involved. I am not at all against it but in favour of using them judiciously. It is totally fine to use useCallback while passing a callback method to an optimised component or when we are binding methods to DOM events.
Kent C. Dodds has written a very intersting article around it. https://kentcdodds.com/blog/usememo-and-usecallback

Optimisations with useMemo also come up with cost. There is extra code that runs to bring in these optimisations which may cost you more than what optimisation from it save for you.

**State update in async methods can be tricky**
Multiple setState calls are batched before a component is rerendered with the new state but thats not the case in async methods.
 An example below-
_Sorry for not so practical example but I hope it helps understand the problem_

```
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [name, setName] = useState("");
  return (
    <>
      <input
        value={name}
        onChange={(e) => {
          setName(e.target.value);
        }}
      />

      <Comp name={name} />
    </>
  );
};

const Comp = (props) => {
  console.log("rendered");
  const [obj, setObj] = useState({ objKey: "John" });
  const [num, setNumber] = useState(2);

  const onButtonClick = () => {
    setObj(null);
    setObj({ objKey: "John Updated" });
    setNumber(4);
    window.setTimeout(() => {
      setObj(null);
      setObj({ objKey: "John Updated" });
      setNumber(4);
    }, 1000);
  };

  const title = React.useMemo(() => {
    return `Hi ${props.name}`;
  }, [props.name]);
  return (
    <div>
      {title}
      <p>{obj && num}</p>
      <p>{obj.objKey}</p>
      <button onClick={onButtonClick}>Update State</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("container"));

```
We can get around this problem by using object or use usereducer.

**Array keys misconceptions**
I have seen discussions where ppl suggest against using index as key. This is a fair point if elements in array are going to be re-ordered but if they are not then array index is fine as key and one doesn't need to specify it explicitly as this is default.

**Not everything that can be a component should be a component**
Maintain readability.Co-locating code has various advantages. Again putting up a link to Kent C. Dodds article which make slots of sense.
https://kentcdodds.com/blog/colocation

**No conditional hooks**
We all know this but often make mistakes with it.Always use Hooks at the top level of your React function. By following this rule, we can ensure that Hooks are called in the same order each time a component renders.
There is this hook that ensures it https://www.npmjs.com/package/eslint-plugin-react-hooks

We often have code like 
```
const comp = ()=>{
if(something){
return null;
}
const [state, setState] = useState(); //we often make mistake here!
//rest of the comp
}
```
The above code may give issues because there is a condition before the hooks.

**useState lazy initialisation**
We can pass a function to useState rather than a value and it comes with an advantage.
Suppose one has to read the initial value from localStorage so
what we usually do in this case is
```
const [savedId, setSavedId] = useState(window.localStorage.getItem('ID'));
```
The above code looks fine but there is a catch. Even though the value we put as parameter with useState is set just once when component mounts but it is read every time when a component renders. Yes it is read though nothing is done with that value. So if getting initial value is an expensive task then this can hit the performance.
Passing function as parameter helps optimise it. So when we pass a function to useState, React will only call the function when it needs the initial value (which is when the component is initially rendered).
```
const [savedId, setSavedId] = useState(() => window.localStorage.getItem('ID'));
```

This is called "lazy initialization." It's a performance optimisation.