Categories
JavaScript React

Basic Built-in React Hooks- useState and useEffect

Spread the love

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at some basic built-in React hooks, including useState and useEffect .

useState

The useState hook lets us manage the internal state of a function component. It takes an initial value as an argument and returns an array with the current state and a function to update it.

It returns the initial state when the component is initially rendered.

We can pass in a function to update the value if the new value is computed using the previous state.

For example, we can write the following to update the value based on a previous one:

function App() {  
  const [count, setCount] = React.useState(0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

setCount(prevCount => prevCount - 1)}

and:

setCount(prevCount => prevCount + 1)}

which decrements the count and increment it respectively by passing in functions that take the previous count as the parameter and return the new count.

Otherwise, we can just pass in the new value to the state update function as we did in:

setCount(0)

useState doesn’t automatically merge update objects. We can replicate this behavior with the spread syntax:

function App() {  
  const [nums, setNums] = React.useState({});  
  return (  
    <>  
      <p>{Object.keys(nums).join(",")}</p>  
      <button  
        onClick={() =>  
          setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })  
        }  
      >  
        Click Me  
      </button>  
    </>  
  );  
}

In the code above, we have:

setNums(oldNums => {  
            const randomObj = { [Math.random()]: Math.random() };  
            return { ...oldNums, ...randomObj };  
          })

to create a randomObj object with a random number as the key and value, and we merge that into another object with the old value then return it.

Then we display it with:

Object.keys(nums).join(",")

by getting the keys and joining them together.

Lazy initial state

We can pass in a function to useState if we want to delay the setting of the initial state.

It’ll be ignored after the initial render if we pass in a function.

This is useful if the initial state is computed from some expensive operation.

For example, we can write:

function App() {  
  const [count, setCount] = React.useState(() => 0);  
  return (  
    <>  
      Count: {count}  
      <button onClick={() => setCount(() => 0)}>Reset</button>  
      <button onClick={() => setCount(prevCount => prevCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(prevCount => prevCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we have:

React.useState(() => 0)

which is a function that just returns 0.

We’ll see the same results as before.

Bailing out of a state update

If we update a state hook with the same value as the current. React will skip updating the state without rendering the children or firing effects.

React uses Object.is() to compare the current and new states, which is close to the === operator except that +0 and -0 are treated to be not equal and NaN is equal to itself.

React may still render before bailing out but it won’t go deeper into the tree if it finds that the old and new values are the same.

useEffect

We can use the useEffect hook to do various operations that aren’t allowed inside the main body of the function component, which is anything outside the rendering phase.

Therefore, we can use this to do any mutations, subscriptions, setting timers, and other side effects.

It takes a callback to run the code.

We can return a function inside to run any cleanup code after each render and also when the component unmounts.

The callback passed into useEffect fires after layout and paint, during a deferred event.

This makes this suitable for running operations that shouldn’t block the browser from updating the screen.

Code that must be run synchronously can be put into the callback of the useLayoutEffect hook instead, which is the synchronous version of useEffect .

It’s guaranteed to fire before any new renders. React will always flush the previous render’s effects before starting a new update.

Conditionally firing an effect

We can pass in a second argument to useEffect with an array of values that requires an effect to be run when they change.

For example, we can use it to get data from an API on initial render as follows:

function App() {  
  const [joke, setJoke] = React.useState({});  
  useEffect(() => {  
    (async () => {  
      const response = await fetch("https://api.icndb.com/jokes/random");  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, []);  
  return (  
    <>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

Passing an empty array as the second argument will stop it from loading in subsequent renders.

We can pass in a value to the array to watch the value in the array change and then run the callback function:

function App() {  
  const [joke, setJoke] = React.useState({});  
  const [id, setId] = React.useState(1);  
  useEffect(() => {  
    (async () => {  
      const response = await fetch(`https://api.icndb.com/jokes/${id}`);  
      const res = await response.json();  
      console.log(res);  
      setJoke(res);  
    })();  
  }, [id]);  
  return (  
    <>  
      <button onClick={() => setId(Math.ceil(Math.random() * 100))}>  
        Random Joke  
      </button>  
      <p>{joke.value && joke.value.joke}</p>  
    </>  
  );  
}

In the code above, when we click the Random Joke button, setId is called with a new number between 1 and 100. Then id changes, which triggers the useEffect callback to run.

Then joke is set with a new value, then the new joke is displayed on the screen.

Conclusion

useState and useEffect are the most basic built-in React hooks.

useState lets us update the state by passing in a function which returns the initial state, or passing in the initial state directly.

It returns an array with the current state and a function to update the state. The state update function returned takes in a value or a function that has the previous value as the parameter and returns the new value if it depends on the previous value.

useEffect lets us run any side effect, mutation, or anything outside of the rendering phase.

It takes a callback with the code to run as the first argument, and an array with the values to watch for changes for before the callback is run.

If an empty array is passed in, then the callback only runs on initial render.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *