Now if you are a fresh react developer like I am, I’m pretty sure you’ve come across questions about React hooks in your technical rounds. This blog can be served as a resource for revision of all 9 built-in React hooks.
With the intro out of the way let’s dive right in!
-
useState
People usually mix state with props, but its very different from each other.
Props are actually what you pass to a component from outside of it. It can only be changed from outside the component.
State are values which is triggered inside the component itself. It is also mutable by the component.
useState is a hook we call inside a functional component to add a local state to it. React preserves the state values between re-renders.
useState returns the current state value and a function that lets you update it.
useState takes only one arguement, which is the initial state. In the example below, it is
Name
When you change the state of an application the DOM itself re-renders automatically to show new values.
import {useState} from "react"; const StateTutorial = () => { const [inputValue, setInputValue] = useState("Name") let onChange = (event) => { const newValue = event.target.value; setInputValue(newValue); } return( <div> <input placeholder="Enter your name.." onChange={onChange}/> {inputValue} </div> ) } export default StateTutorial;
Normally, variables “disappear” when the functional component exits but the state variables are preserved by React.
-
useReducer
There comes a time when using useState that the state management logic takes a significant part of the component body, that’s a problem because the react component should contain logic to produce the output. Ideally, state management is something that should be done in its own separate space, otherwise we get a mix of rendering logic and state management which is difficult to maintain and read!
To solve this issue react provides the useReducer hook to extract the state management out of the component.
useReducer accepts 2 arguements: the reducer function and the initial state.
It returns an array of 2 items: the current state and dispatch function.Initial state: It is the value the state is initialised with.
Current state: It holds the value of the state currently in.
Reducer function: The reducer function accepts current state and an action object as parameters. And depending on the action object, the reducer function must update the state and hence returning a new state.
Action object: The object that describes how to update the state. It has a property called type which describes what kind of state update the reducer function must do.
Dispatch function: This function dispatches an action object i.e. whenever you want to update the state you simply call the dispatch function with the appropriate action object.Knowing all these we can write a state management logic by our own!
Here, I wrote the code so a click on the button would increase the value of the counter and as well as show/hide the text on alternate clicks.
import React, { useReducer } from "react"; const reducer = (state, action) => { switch (action.type) { case "INCREMENT": return { count: state.count + 1, showText: state.showText }; case "toggleShowText": return { count: state.count, showText: !state.showText }; default: return state; } }; const ReducerTutorial = () => { const [state, dispatch] = useReducer(reducer, { count: 0, showText: true }); return ( <div> <h1>{state.count}</h1> <button onClick={() => { dispatch({ type: "INCREMENT" }); dispatch({ type: "toggleShowText" }); }} > Click Here </button> {state.showText && <p>This is a text</p>} </div> ); }; export default ReducerTutorial;
TLDR; When you'd like to update the state, simply call
dispatch(action)
with the appropriate action object. The action object is then forwarded to thereducer()
function that updates the state. If the state has been updated by the reducer, then the component re-renders, and[state, ...] = useReducer(...)
hook returns the new state value. -
useEffect
useEffect is an extremely importantly hook that every React user must have come acrossed.
This hook takes a function as a parameter which then executes after the render is committed to the screen. Normally, the callback passed to useEffect is run after every initial render, but that can be changed with the help of dependency array, which contains the value; when changed triggers the effect!
Here, the program calls the API on initial render only as the dependency array is empty.
function EffectTutorial() { const [data, setData] = useState(""); const [count, setCount] = useState(0); useEffect(() => { axios .get("https://jsonplaceholder.typicode.com/comments") .then((response) => { setData(response.data[0].email); console.log("API WAS CALLED"); }); }, []); return ( <div> Hello World <h1>{data}</h1> <h1>{count}</h1> <button onClick={() => { setCount(count + 1); }} > Click </button> </div> ); }
I’m sure I don’t have to explain further how useEffect works as its one of the first few hooks a react developer gets to know about.
-
useLayoutEffect
This hook is almost identical to useEffect, and yes there is similarities between them! Both updates DOM and takes in the same type of parameters, except there is a fundamental difference between both of them.
useLayoutEffect is executed before the screen is rendered but only after the DOM has been updated by it, which is opposite of how useEffect works.
Here, the component demonstrates the working of this hook.
function LayoutEffectTutorial() { const inputRef = useRef(null); useLayoutEffect(() => { console.log(inputRef.current.value); }, []); useEffect(() => { inputRef.current.value = "HELLO"; }, []); return ( <div className="App"> <input ref={inputRef} value="SEKIRO" style={{ width: 400, height: 60 }} /> </div> ); }
Even though there isn’t a noticeable effect when the useEffect hook is replaced by useLayoutEffect, but it is advised to stick with useEffect unless it causes some problems.
-
useRef
useRef is a built-in React hook that accepts one argument as the initial value and returns a reference. A reference is an object having a special property called “current”.
reference.current access the reference value and reference.current.value updates it.
The value of the reference is persisted between component re-renders, and updating it doesn’t trigger a component re-rendering unlike updating a state. Which is why I conclude that reference update is synchronous while on the other hand the state update is asynchronous.
Here, the program simply clears the input of anything written on it with a click on the button.
function RefTutorial() { const inputRef = useRef(null); const onClick = () => { inputRef.current.value = ""; }; return ( <div> <h1>Pedro</h1> <input type="text" placeholder="Ex..." ref={inputRef} /> <button onClick={onClick}>Change Name</button> </div> ); }
-
useImperativeHandle
The data flow in React is unidirectional; that is to say, you should pass functions and data down through props and a component should only ever be able to access what is passed in as props. In cases where bidirectional dataflow is needed, we use libraries such as Redux or React’s context API.
However, in some cases, importing redux or using context is simply just like using sword to cut nails — This is where
useImperativeHandle
comes in. This will provide us a lightweight solution to have bidirectional flow.This is best demonstrated with the example below:
In the function ImperativeHandle we have used useRef hook to refer the Button component. Now I know we can’t use reference on a component and useRef should be used on DOM elements but, notice I have imported forwardRef.
The forwardRef function allows us to transform a functional component and allow it to accept reference form its parent component. Which is how not only we can grab props but we can also grab any references that is passed through the parent.
In the component useImperativeHandle allows us to define functions that can be called using a ref. That is exactly what is being accomplished here!
This hook as shown, takes 2 parameters:
a. ref: reference from the parent component
b. a function which returns an objectSince, this still might be confusing to many of you folks it’ll be wiser to copy paste the code below to your code editor and execute to understand it better.
import React, { forwardRef, useImperativeHandle, useState } from "react"; const Button = forwardRef((props, ref) => { const [toggle, setToggle] = useState(false); useImperativeHandle(ref, () => ({ alterToggle() { setToggle(!toggle); }, })); return ( <> <button>Button From Child</button> {toggle && <span>Toggle</span>} </> ); }); function ImperativeHandle() { const buttonRef = useRef(null); return ( <div> <button onClick={() => { buttonRef.current.alterToggle(); }} > Button From Parent </button> <Button ref={buttonRef} /> </div> ); }
Since, all this information can be a bit overwhelming in a single read. Soon there will be another blog covering useContext, useMemo and useCallback hooks.
Top comments (1)
Many early birds have already started using this custom hooks library
in their ReactJs/NextJs project.
Have you started using it?
scriptkavi/hooks
PS: Don't be a late bloomer :P