Basic Hooks in React

Demilade Leshi
6 min readMay 24, 2024

--

According to the React documentation,

Hooks are a new addition in React 16.8. They are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes.

There are 10 React hooks on the official page. They are:

Basic Hooks

  • useState
  • useEffect
  • useContext

Additional Hoooks

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

In this article, I will only be talking about the basic hooks in React.

useState Hook

The most commonly used React hook is the useState hook. It helps us to change the state of an HTML element and automatically render the value unto the screen.

What exactly is state in React? State is a built-in React object that is used to contain data or information about the component. A component’s state can change over time; whenever it changes, the component re-renders. If we ever needed to change the value of an HTML element without state, we would need to manually grab the element like so:

import React from "react";

const MyState = () => {
let count = 0;
const increment = () => {
count = count + 1;
console.log(count);
};
return (
<div>
<h1> {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
};

export default MyState;

However, this would not work the way we expect it to. On clicking the button, the count variable seems to remain the same but that is not the case and that is seen by logging the value to the console. The variable is indeed incrementing but the new value is not being rerendered to the screen. In order to render it, we would need to use the .innerHTML method which in React is quite burdensome.

In React, we do not need to manually change the values of elements. React helps us to change the values of HTML elementsonce we determine a state for it. To do this, we use the useState hook.

import React, { useState } from "react";

const MyState = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<h1> {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
};

export default MyState;

We cannot alter the value of a state variable by simply assigning a new value to it because it was defined as a const. We need to use the state function to mutate the value. Whatever value is in the parenthesis will be assigned to the state variable. React knows to rerender the state when every single element that uses that state changes.

useEffect Hook

The useEffect hook is simply a function that is called every time the page rerenders. By using this Hook, we tell React that the component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. It is used to perform side effects in our components such as data fetching, manually updating the DOM and setting timers.

Let us edit our counter app from above and add the useEffect hook to it. Suppose we wanted to simply console log the number of times the button was clicked as above, we simply run the function as seen above and we get our required results because we actually want to see the count increase in our console(only when the button is clicked).

import React, { useEffect, useState } from "react";

const MyEffect = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
useEffect(() => {
console.log(`You've clicked the button ${count} times`);
});

return (
<div>
<h1> {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
};

export default MyEffect;

The issue with useEffect is that the function is called every single time to page rerenders which could also be when any other state that is not related to the useEffect rerenders.

React provides a solution for this by letting us add a dependency array to our useEffect. This is simply an array of all states we want to listen to in our useEffect. Then every state passed in will trigger a useEffect every time the value of that state changes. Therefore the useEffect will not be triggered by any state that it is not listening to.

Suppose we had the following: I’m making use of an external API called axios to fetch some random data and using a useEffect to set the value of the data I want to rendered unto the screen.

import React, { useEffect, useState } from "react";
import axios from "axios";

const MyEffect = () => {
const [count, setCount] = useState(0);
const [data, setData] = useState("");

useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/comments")
.then((response) => {
setData(response.data[0].email);
console.log("API was called");
});
}, []);

return (
<div>
<h1>Hello World {data} </h1>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};

export default MyEffect;

Without the dependency array, initially, the function runs when the page is rendered, once again when the data state is rendered and once again when the count state changes. In order to prevent this, I added an empty dependency array which means that the useEffect will only be triggered once after the initial render.

useContext Hook

This is a hook that comes together with the Context API. If we had an app that has a lot of states where we had to pass them into our separate components through props, it is not very maintainable especially if the app is a large app.

We use Context to be able to have access to all the states we pass into that context . We organize our states using the Context API by creating a context (a collection of states or information we want to access throughout the whole tree of components inside the Context component).

Take for example a login app using props:

//Context component
import React, { useState } from "react";
import Login from "./Login";
import User from "./User";

const MyContext = () => {
const [username, setUsername] = useState("");

return (
<div>
<Login setUsername={setUsername} />
<User username={username} />
</div>
);
};

export default MyContext;
//Login component
import React from "react";

const Login = ({ setUsername }) => {
return (
<div>
<input
onChange={(e) => {
setUsername(e.target.value);
}}
/>
</div>
);
};

export default Login;
//User component
import React from "react";

const User = ({ username }) => {
return (
<div>
<h1>User: {username} </h1>
</div>
);
};

export default User;

For a small app like this, this is easy to maintain using props. However, it is better managed using Context. Now consider the same app using Context:

//Context component
import React, { useState, createContext } from "react";
import Login from "./Login";
import User from "./User";

export const AppContext = createContext(null);

const MyContext = () => {
const [username, setUsername] = useState("");
return (
<AppContext.Provider value={{ username, setUsername }}>
<Login />
<User />
</AppContext.Provider>
);
};

export default MyContext;
//Login component
import React, { useContext } from "react";
import { AppContext } from "./myHooks";

const Login = () => {
const { setUsername } = useContext(AppContext);

return (
<div>
<input
onChange={(e) => {
setUsername(e.target.value);
}}
/>
</div>
);
};

export default Login;
//User component
import React, { useContext } from "react";
import { AppContext } from "./myHooks";

const User = () => {
const { username } = useContext(AppContext);

return (
<div>
<h1>User: {username} </h1>
</div>
);
};

export default User;

The value passed into the Context component is all the states and functions we want to have access to in our other components. Instead of passing these values through props, we import the useContext hook as well as the context we created in our context component in the components we need it in.

We then grab the values we need from the useContext hook and access the values in the context we created. We can then grab whatever values we need from the context.

This hook is very useful in an app such as a login app where the user has a lof of information such as username, email, birthday etc that we would like to have access to quickly and efficiently in our app. We can simply create the necessary information and pass them through to the needed components through context instead of props.

In conclusion, React hooks help us to access state and other react features without the use of classes. I have briefly touched on the 3 basic hooks:

  • useState: Helps us to keep track of the state of a variable and automatically rerender the variable when it’s value is changed.
  • useEffect: Helps us to manage side effects in the DOM.
  • useContext: Helps us to pass data to needed components without the use of props.

--

--