React Hooks (Functional Components)

What are React Hooks ?

Hooks were added to React in version 16.8.

Hooks allow function components to have access to state and other React features. Because of this, class components are generally no longer needed.

Hooks are more restrictive than other functions. You can only call Hooks at the top of your components (or other Hooks). If you want to use useState in a condition or a loop, extract a new component and put it there.

Hook allows us to “hook” in to React features such as state and lifecycle methods:

There are 3 rules for hooks :

  1. Hooks can only be called inside React Function Components. Hooks will not work in class components.
  2. Hooks can only be called at the top level of a component
  3. Hooks can not be conditional

What are the main benefits of using – React Hooks?

React Hooks – provide a more modern, streamlined approach to React development that can help developers write cleaner, more efficient code, simplify their code, improve readability, increase testability, and boost performance, ultimately leading to better overall development outcomes.

BASIC HOOKS

(useState)– manage component state
(useEffect)– handle side effects, fetching data from an API or update the DOM
(useContext)– share data between components, without passing data through props
(useReducer)– complex state management, managing state that depends on previous state
(useCallback)– memoize a function to prevent unnecessary re-renders

ADVANCED HOOKS

(useMemo)– memoize a value to prevent unnecessary re-computation
(useRef)– store a mutable value, such as a reference to a DOM element
(useImperativeHandle)– expose a child component’s functionality to its parent component
(useLayoutEffect)– perform an action before the browser paints the screen
(useDebugValue)– debug a custom hook
(useDeferredValue)– Deferring re-rendering for a part of the UI or to optimize Performance
(useErrorBoundary)– handle errors that occur in child components
(useTransition)– is used to add transitions to components that are being mounted or unmounted. It’s typically used to improve the user experience by providing visual cues when a component is appearing or disappearing.

LIBRARY HOOKS
(useQuery) – to fetch data from a GraphQL API
(useMutation) – to perform mutations on a GraphQL API
(useFetch) – fetch data from an API
(useForm) – to handle form data

ALL Hooks :

17 hooks here : (13 react hooks) 2 (graphql ) 1 api, 1 form

  • useState()
  • useEffect()
  • useRef()
  • useMemo()
  • useReducer()
  • useContext()
  • useCallback()
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue
  • useDeferredValue
  • useErrorBoundary
  • useTransition
  • useQuery (Graphql)
  • useMutation (GraphQL)
  • useFetch (API)
  • useForm (Forms)
  • Custom Hooks

1. useState() Hook

const [count, setCount] = useState(initialCount);

The React useState Hook allows us to track state in a function component.
State generally refers to data or properties that need to be tracking in an application

We initialise our state by calling useState in our function component.

useState accepts an initial state and returns two values:

  • The current state.
  • A function that updates the state.

The useState can hold: it can be used to keep track of strings, numbers, booleans, arrays, objects, and any combination of these!

Update State :
To update our state, we use our state updater function.
We should never directly update state. Ex: color = “red” is not allowed


import { useState } from "react";
import ReactDOM from "react-dom/client";

function FavouriteColor () {
		const [color, setColor] = useState("red");
	    return (
			<>
				<h1>My favorite color is {color}!</h1>
				 <button
        			type="button"
        			onClick={() => setColor("blue")}
      		>Blue</button>
			</>
		)
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FavoriteColor />);

2. useEffect() Hook

The lifecycle methods componentDidMount, componentDidUpdate, componentWillUnmountwere included in one place – the useEffect hook.

componentDidMount, componentDidUpdate, componentWillUnmount = useEffect hook

The useEffect hook in React is a powerful tool that allows you to run side effects in your functional components. Side effects are actions that your component takes that affect the outside world, such as fetching data from an API or updating the DOM.

Before the useEffect hook was introduced, side effects were handled using class components and their lifecycle methods. However, this approach could be cumbersome and difficult to manage, especially for complex components.

The useEffect hook simplifies the process of handling side effects in functional components by allowing you to encapsulate them in a single function. This makes it easier to keep track of your side effects and to ensure that they are executed in the correct order.

The syntax for using the useEffect hook is as follows:

useEffect(() => {
  // Your side effect code goes here.
}, [dependencies]);

The first argument to the useEffect hook is a function that contains your side effect code. This function will be executed whenever the component mounts or whenever any of the dependencies in the second argument array change.

The second argument to the useEffect hook is an array of dependencies. This array contains the values that you want to watch for changes. If any of the values in the array change, the useEffect hook will be re-executed and your side effect code will be run again.

If you want your side effect code to be executed only once, you can pass an empty array to the second argument. For example:

useEffect(() => {
  // Your side effect code goes here.
}, []);

This will ensure that your side effect code is only executed when the component mounts.

If you want your side effect code to be executed whenever the component state changes, you can pass the component state array to the second argument. For example:

useEffect(() => {
  // Your side effect code goes here.
}, [state]);

This will ensure that your side effect code is re-executed whenever the component state changes.

The useEffect hook is a powerful tool that can be used to simplify the process of handling side effects in functional components. By encapsulating your side effects in a single function, you can make them easier to keep track of and to ensure that they are executed in the correct order.

For more deep read – https://dmitripavlutin.com/react-useeffect-explanation/

function App() {
	const [name, setName]  = useState(“Jan”);
	const [admin, setAdmin] = useState(false);

	useEffect(() => {
		console.log(`Celebrate ${name}`);
	},[name]) // the effect will only pass once when rendered
	
	useEffect(() => {
			console.log(`The user is ${admin ? "Admin" : "Not admin" }.`)
	}, [admin]);

	return (
		<section>
		<p> Congratulations  {name} ! </p>
		<button onClick={() => setName(“Will”)}>
			Change Winner
		</button>
		</section>
	)
}

Also used for data fetching :

function App() {
    const [data, setData] = useState([]);

  useEffect(() => {
         fetch(`https://api.github.com/users`)
        .then(responese => response.json())
        .then(setData);
  }, []);

    if (data) {
     return (
        <div>
         <ul> 
            {data.map((user) => ()
                <li key={user.id}> {user.login} </li>
            )}
         </ul>
         <button onClick={() => setData([]) }> Remove Data </button>
        </div>
     )  
    }   

}

3. useRef()

const ref = useRef();

The useRef Hook allows you to persist values between renders.

Does Not Cause Re-renders

It can be used to store a mutable value that does not cause a re-render when updated.
It can be used to access a DOM element directly.
useRef can be particularly be useful in forms and to get the value from any input.


function App() {
	const sound = useRef();
	const color = useRef();
	
	const submit = (e) => {
		e.preventDefault();
		const soundVal = sound.current.value;
		const colorVal = color.current.value;

		alert(`${soundVal} sounds like ${colorVal}`);
		
		// reset
		sound.current.value = "";
		color.current.value=  "";

	}

	return (
		<form onSubmit={submit}>
			<input ref={sound} type="text" placeholder="Sound..." />
			<input ref={color} type="color" />
			<button> Add </button>
		</form>
	)
}

If we tried to count how many times our application renders using the useState Hook, we would be caught in an infinite loop since this Hook itself causes a re-render.
To avoid this, we can use the useRef Hook.

But it also allows you to just hold a mutable value through any render. Also, mutating the value of ref.current will not cause any render

In general, we want to let React handle all DOM manipulation.
But there are some instances where useRef can be used without causing issues.
In React, we can add a ref attribute to an element to access it directly in the DOM.


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

const Counter = () => {
  const [inputValue, setInputValue] = useState("");
  const count = useRef(0); // set count = {current: 0} 
  const inputElement = useRef(null);  // use useRef to access dom element

  // { current: 0 }
  useEffect(() => {
    count.current = count.current + 1;
  });

  const focusInput = () => {
    inputElement.current.focus();
  };

  return (
    <>
      <input
        type="text"
        value={inputValue}
        ref={inputElement}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <h1> Render Count : {count.current} </h1>

      <button onClick={focusInput}> Focus Input </button>
    </>
  );
};

export default Counter;

useRef() only returns one item. It returns an Object called current.

When we initialise useRef we set the initial value: useRef(0).

It’s like doing this: const count = {current: 0}. We can access the count by using count.current.

Run this on your computer and try typing in the input to see the application render count increase.

Tracking State Changes

The useRef Hook can also be used to keep track of previous state values.
This is because we are able to persist useRef values between renders.

Use useRef to keep track of previous state values:

import { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom/client";

function App() {
  const [inputValue, setInputValue] = useState("");
  const previousInputValue = useRef("");

  useEffect(() => {
    previousInputValue.current = inputValue;
  }, [inputValue]);

  return (
    <>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <h2>Current Value: {inputValue}</h2>
      <h2>Previous Value: {previousInputValue.current}</h2>
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

4. useMemo()


The React useMemo Hook returns a memoized value.

Think of memoization as caching a value so that it does not need to be recalculated.

The useMemo Hook only runs when one of its dependencies update.

This can improve performance.

The useMemo and useCallback Hooks are similar. The main difference is that useMemo returns a memoized value and useCallback returns a memoized function.

Performance

The useMemo Hook can be used to keep expensive, resource intensive functions from needlessly running.

5. useReducer

The useReducer Hook is similar to the useState Hook.

It allows for custom state logic.


function App () {
	const [number, setNumber] = useReducer((number, newNumber) => number + newNumber), 0);

	return (
		<h1 onClick={() => setNumber(1)}> {number} </h1>
	);
}

If you find yourself keeping track of multiple pieces of state that rely on complex logic, useReducer may be useful.

function App() {
		const [checked, setChecked] = useState(false);

		const [checked, toggle] = useReducer((checked) => !checked, false) // function to change the state, and intialState
		return (
			<>
				<input type="checkbox" value={checked} onChange={toggle} />
				{checked ? "checked": "not checked"}
			</>
		);
}

Another Pattern :

The reducer function contains your custom state logic and the initialState can be simple value but generally will contain an object.

The useReducer hook returns the current state and a dispatch method

import {useReducer} from 'react';

const initialState = {
    message: "hi"
}

function reducer(state, action) {
    switch(action.type) {
        case "yell": 
            return {
                message: `Hey! I Just said ${state.message}`
            };
        case "whisper":
            return {
                message: `excuse me I just said ${state.message}`
            }
    }
}

function App() {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <>
            <p> Message: {state.message} </p>
            <button onClick={() => dispatch({type: "yell"})}> 
                YELL
            </button>       
            <button onClick={() => dispatch({type: "whisper"})}>
                Whisper
            </button>
        </>
    )
}

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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