Which is the right way to detect first render in a react component

39.6k Views Asked by At

I have a scenario where I need to detect the first render of a component. Here I have build a small example. Could someone explain to me what is the correct approach?

Why do most of the people suggest to use a ref instead of a plain state.

https://codesandbox.io/s/condescending-burnell-0ex3x?file=/src/App.js

import React, { useState, useRef, useEffect } from "react";
import "./styles.css";

export default function App() {
  const firstRender = useDetectFirstRender();
  const [random, setRandom] = useState("123");
  useEffect(() => {
    if (firstRender) {
      console.log("first");
    } else {
      console.log("second");
    }
  }, [random]);
  return (
    <div className="App">
      <h1>Random Number is {random}</h1>
      <button onClick={() => setRandom(Math.random())}>Change Name</button>
    </div>
  );
}

//Approach 1
// export function useDetectFirstRender() {
//   const firstRender = useRef(true);

//   useEffect(() => {
//     firstRender.current = false;
//   }, []);

//   return firstRender.current;
// }

//Approach 2
export function useDetectFirstRender() {
  const [firstRender, setFirstRender] = useState(true);

  useEffect(() => {
    setFirstRender(false);
  }, []);

  return firstRender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

7

There are 7 best solutions below

0
On

You can use the following custom hook:

import { useEffect, useState } from "react"

export const useIsFirstRender = () => {
    const [isFirst, setIsFirst] = useState(true);
    useEffect(() => { setIsFirst(false) }, [])
    return isFirst
}

Then use it as the following

import isFirstRender=useIsFirstRender();
console.log('Is first render:',isFirstRender)
0
On

you can detect and save it by using useMemo or useCallback hook. but here the most preferable is useMemo as it prevent the same rendering again and again.

const firstRender = useMemo(
    () =>console.log('first Render'),
    []
  );

here it will render once and save value in the first Render,so you can use this anywhere where you need.

0
On

The useEffect hook takes a second parameter. This second param is an array of variables that the component will check ensure they've changed before re-rendering. However, if that array is empty, the hook is only called once during initial render. This is similar to the useMemo() trick posted previously.

useEffect(() => doSomethingOnce(), [])
                                   ^^
2
On

You could create a reusable custom hook for that, based on useRef.

function useFirstRender() {
  const ref = useRef(true);
  const firstRender = ref.current;
  ref.current = false;
  return firstRender;
}
0
On
const firstRender = useRef(true);

useEffect(() => {
  if (firstRender.current) {
    firstRender.current = false;
    return;
  }
  doSomething();
});
0
On

When using the StrictMode then it's important to do some useEffect() reset/cleanup as well (return () => { isFirstRender.current = true };). Otherwise the isFirstRender.current will not really reflect what you want, causing the unexpected issues, despite using the [] dependency that suppose to run only on the first render (when component is mounted).

From How to handle the Effect firing twice in development?:

React intentionally remounts your components in development to find bugs like in the last example. The right question isn’t “how to run an Effect once”, but “how to fix my Effect so that it works after remounting”.

const isFirstRender = useRef(true);

useEffect(() => {

  if (isFirstRender.current) {
    // Do something on first render...
  }

  isFirstRender.current = false;

  // ---> StrictMode: The following is REQUIRED to reset/cleanup:
  return () => { isFirstRender.current = true };

}, []); // ---> The `[]` is required, it won't work with `[myDependency]` etc.
0
On

You can store a value in sessionStorage and check if the value is present in sessionStorage. if it is present, it is not first render. This is not specific to react.

if(sessionStorage.getItem('is_first_render') === null) sessionStorage.setItem('is_first_render', 'somevalue')