UseState hook with gotchas in depth.

UseState:

Using React A function component's state can be tracked using useState Hook. State typically refers to the data or variables that a program needs to track.

Please visit Rendering in react to understand more about the state.

Syntax:

//importing
import { useState } from "react";

function App(){
//usage
 const [stateVariable,setStateVariableMethod] = useState("any default value that you want to set")

}

Example:

each time when the user hits click me button. The incrementCount() function will be called, which increases the count variable and which causes changing the state, which makes react to re-render the component and update the count variable value on the webpage.

import {useState} from "react"
function App() {
  const [count,setCount] = useState(0)

  function incrementCount() {
    setCount(count=>count+1)
  }

  return (
    <>
      <h1>your count is {count}</h1>
      <button onClick={incrementCount} style = {{backgroundColor:'black',color:'yellow'}}>Click ME</button>
    </>
  );
}
export default App;

useState with functions:

Note: if we pass a use state with a hardcode value like below

  const [count,setCount] = useState(4)

Every time we render our component, it will utilize or call the number 4. Since it is only a value, the performance won't be affected, but what if we want to perform an operation by calling a function and getting a value back? What if the operation is complex? If we call that function to retrieve a value each time, our application will run very slowly. To avoid this by default react will call the method once.

Note: In strict mode, it will run twice for debugging purposes but after building the app. In production, it will only be once.

import { useState } from "react";
function App() {
  const [count, setCount] = useState(() => {
    console.log(
      "I will run only once durning first render and return the value 4"
    );
    return 4;
  });

  function incrementCount() {
    setCount((count) => count + 1);
  }

  return (
    <>
      <h1>your count is {count}</h1>
      <button
        onClick={incrementCount}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

To make it run each time the state gets updated or for each render by using IIFE (Immediately Invoked Function Expression). we can change the code below.

import { useState } from "react";
function App() {
  const [count, setCount] = useState(
    (() => {
      console.log(
        "I will run only once durning first render and return the value 4"
      );
      return 4;
    })()
  );

  function incrementCount() {
    setCount((count) => count + 1);
  }

  return (
    <>
      <h1>your count is {count}</h1>
      <button
        onClick={incrementCount}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

OR

import { useState } from "react";
function App() {
  const countFun = ()=>{
    console.log(
      "I will run only once durning first render and return the value 4"
    );
    return 4;
  }
  const [count, setCount] = useState(countFun());

  function incrementCount() {
    setCount((count) => count + 1);
  }

  return (
    <>
      <h1>your count is {count}</h1>
      <button
        onClick={incrementCount}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

OR

// export default App;
import { useState } from "react";
function App() {
  function countFun() {
    console.log(
      "I will run only once durning first render and return the value 4"
    );
    return 4;
  }
  const [count, setCount] = useState(countFun());

  function incrementCount() {
    setCount((count) => count + 1);
  }

  return (
    <>
      <h1>your count is {count}</h1>
      <button
        onClick={incrementCount}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

After clicking:

If you look at the console, the count has gone up by 1, and there are now 4 log entries. Since we are in strict mode, it is being called four times, but whenever our app is built in production, it will only be called twice because I have only pressed the button once.

useState with objects:

Syntax:

  const [object, setObjectmethod] = useState({object});

Example:

import { useState } from "react";
function App() {
  const [div, setdiv] = useState({ theme: "light", fontSize: "normal" });

  function editDiv() {
    setdiv((div) => ({theme:"dark"}));
  }

  return (
    <>
      <h1>your div is {JSON.stringify(div)}</h1>
      <button
        onClick={editDiv}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

before click:

after click:

if you observe it overrides font size by removing it. to avoid this we need to spread the previous state of the object by using the spread (...) operator shown below:

   setdiv((div) => ({...div,theme:"dark"}));
import { useState } from "react";
function App() {
  const [div, setdiv] = useState({ theme: "light", fontSize: "normal" });

  function editDiv() {
    setdiv((div) => ({ ...div, theme: "dark" }));
  }

  return (
    <>
      <h1>your div is {JSON.stringify(div)}</h1>
      <button
        onClick={editDiv}
        style={{ backgroundColor: "black", color: "yellow" }}
      >
        Click ME
      </button>
    </>
  );
}
export default App;

before click:

after click:

How react is optimized with useState hook?

If we try to update the state of a variable to the same as its previous state, the react will

not re-render the page.

let's look at some examples to understand it much better.

lets us create a state variable count using useState hook and create a button that updates the count state to the same as previously. let the initial state of the count variable be 0 and when the user clicks on the button click the count will every time the count will update to 0 which is the same as its previous state, lets create a function display() which will display the function invoked on the console like shown below.

import { useState } from "react";
function App() {
  const [count, setCount] = useState(0);
  function display() {
    console.log("display function is invoked");
  }
  display();
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button
        onClick={() => setCount((count) => 0)}
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

output:

No matter how many times the user clicks on the button. React will not re-render the page as the state is always updating to 0.

Note: In strict mode react will render the component two times for debugging purposes.

Now, lets change the code a bit, let's increase the count value by 1 every time the user clicks on the button like shown below:


import { useState} from "react";
function App() {
  const [count, setCount] = useState(0);
  function display(){
    console.log("display function is invoked");
  }
  display()
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button
        onClick={() => setCount((count) => count+1) }
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

Output:

Now each time the user clicks on the button Click the count variable state changes by increasing its value by 1. Each time the state is updated to a new value which is not the same as previous. React will re-render the component that's on the console we can see the display function is invoked message on the console every time the user clicks on the button.