Home >Web Front-end >CSS Tutorial >React Hooks: The Deep Cuts
Hooks are reusable functions that allow you to use state and other functions (such as lifecycle methods, etc.) without writing classes. Hook functions allow us to "hook" the React state lifecycle using function components, allowing us to manipulate the state of the function component without converting it into a class component.
React introduced Hooks in version 16.8 and has been adding more Hooks since then. Some Hooks are more commonly used and popular than others, such as useEffect
, useState
, and useContext
. If you use React, I believe you must have used these Hooks.
But what I'm interested in is the little-known React Hooks. While all React Hooks have their own features, I really want to show you five Hooks because they may not appear in your day-to-day job – or maybe, knowing that they give you some extra superpowers.
useReducer
useRef
useImperativeHandle
useMemo
useCallback
useReducer
useReducer
Hook is a status management tool similar to other Hooks. Specifically, it is an alternative to the useState
Hook.
If you use the useReducer
Hook to change two or more states (or actions), you do not need to operate them separately. The Hook tracks all states and manages them collectively. In other words: it manages and re-renders state changes. Unlike the useState
Hook, useReducer
is easier when dealing with many states in complex projects.
useReducer
can help reduce the complexity of handling multiple states. It can be used when you find yourself needing to track multiple states collectively, as it allows you to treat the state management and rendering logic of components as separate concerns.
useReducer
accepts three parameters, one of which is optional:
const [state, dispatch] = useReducer(reducer, initialState) const [state, dispatch] = useReducer(reducer, initialState, initFunction) // Initialization using an optional third parameter
The following example is an interface containing text input, counters, and buttons. Interacting with each element will update the status. Note that useReducer
allows us to define multiple cases at once, rather than setting them separately.
import { useReducer } from 'react'; const reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count 1 }; case 'DECREMENT': return { ...state, count: state.count - 1 }; case 'USER_INPUT': return { ...state, userInput: action.payload }; case 'TOGGLE_COLOR': return { ...state, color: !state.color }; default: throw new Error(); } } function App() { const [state, dispatch] = useReducer(reducer, { count: 0, userInput: '', color: false }) return ( <main classname="App App-header" style="max-width:90%" backgroundcolor: state.color :> <input type="text" onchange="{(e)"> dispatch({ type: 'USER_INPUT', payload: e.target.value })} /> <br><br><p style="{{" margin:>{state.count}</p> <button onclick="{()"> dispatch({ type: 'DECREMENT' })}>-</button> <button onclick="{()"> dispatch({ type: 'INCREMENT' })}> </button> <button onclick="{()"> dispatch({ type: 'TOGGLE_COLOR' })}> Color</button> <button onclick="{()"> dispatch({ type: 'TOGGLE_COLOR' })}>Color</button> <br><br><p style="{{" margin:>{state.userInput}</p> </main> ); } export default App;
From the above code, you can notice that we can easily manage multiple states in reducer
(switch-case), which demonstrates the advantages of useReducer
. This is the power it provides when working in complex applications with multiple states.
useRef
useRef
Hook is used to create references on elements to access the DOM. But not only that, it returns an object with the .current
attribute, which can be used throughout the life of the component, allowing data to be persisted without causing re-rendering. Therefore, the useRef
value remains unchanged between renders; updating the reference does not trigger re-rendering.
When you want to:
The useRef
Hook can be used. It is most useful when storing variable data in your application without causing re-rendering.
useRef
only accepts one parameter, i.e. initial value.
const newRefComponent = useRef(initialValue);
Here, I used the useRef
and useState
Hook to show how many times the application re-renders the update state as it types in text input.
import './App.css' function App() { const [anyInput, setAnyInput] = useState(" "); const showRender = useRef(0); const randomInput = useRef(null); // Note that null needs to be set here const toggleChange = (e) => { setAnyInput(e.target.value); showRender.current ; } const focusRandomInput = () => { if (randomInput.current) { // Add judgment to prevent null reference errors randomInput.current.focus(); } } return ( <div classname="App"> <h3>Amount Of Renders: {showRender.current}</h3> <input type="text" ref="{randomInput}" onchange="{toggleChange}"> <button onclick="{focusRandomInput}">Click To Focus On Input</button> </div> ); } export default App;
Note that typing each character in the text field will update the application's status, but will not trigger a full re-render.
useImperativeHandle
Do you know how child components access the functionality passed to them from parent components? Parent components pass these functions through props, but this kind of pass is in a sense "one-way" because the parent component cannot call functions in the child component.
Then, useImperativeHandle
enables parent components to access functions of children.
How does this work?
forwardRef
, which allows the defined ref to be passed to child components. useImperativeHandle
Exposes functions of child components through ref. useImperativeHandle
works well when you want the parent component to be affected by changes in the child component. So, cases like changing focus, increasing and decreasing, and blurring elements may be cases where you find yourself needing to use this Hook in order to update the parent component accordingly.
useImperativeHandle(ref, createHandle, [dependencies])
In this example, we have two buttons, one in the parent component and one in the child component. Clicking the parent button allows us to retrieve data from the child component, allowing us to manipulate the parent component. It is set up in that clicking the child button does not pass anything from the parent component to the child component to help illustrate how we pass the content in the opposite direction.
// Parent component import React, { useRef } from "react"; import ChildComponent from "./childComponent"; import './App.css'; function ParentComponent() { const controlRef = useRef(null); return ( <div> <button onclick="{()"> { if (controlRef.current) { controlRef.current.controlPrint(); } }}>Parent Box</button> <childcomponent ref="{controlRef}"></childcomponent> </div> ); } export default ParentComponent;
// Child component import React, { forwardRef, useImperativeHandle, useState } from "react"; const ChildComponent = forwardRef((props, ref) => { const [print, setPrint] = useState(false); useImperativeHandle(ref, () => ({ controlPrint() { setPrint(!print); }, })); return ( <div> <button>Child Box</button> {print && <p>I am from the child component</p>} </div> ); }); export default ChildComponent;
...(Sample output screenshot or description should be included here)
useMemo
useMemo
is one of the least commonly used but interesting React Hooks. It improves performance and reduces latency, especially in large computing in applications. How could this happen? The useMemo
Hook prevents React from recalculating the value every time the component's state is updated and the component is re-rendered.
You will see that the function responds to state changes. useMemo
Hook accepts a function and returns the return value of the function. It caches the value to prevent extra work from being spent to re-render it, and then returns it when one of the dependencies changes.
This process is called memorization, which helps improve performance by remembering the previously requested value so that it can be used again without repeating all these calculations.
The best use case will be any time you are dealing with heavy calculations that you want to store the value and use it in subsequent state changes. It can bring a nice performance boost, but overuse of it can have the exact opposite effect, thus taking up the application's memory.
useMemo(() => { // Code here}, [])
When the button is clicked, this mini program indicates whether the number is an even or an odd number, and then squares the value. I've added many zeros to the loop to increase its computational power. It returns the value in seconds and is still working fine due to the useMemo
Hook.
// UseMemo.js import React, { useState, useMemo } from 'react' function Memo() { const [memoOne, setMemoOne] = useState(0); const incrementMemoOne = () => { setMemoOne(memoOne 1) } const isEven = useMemo(() => { let i = 0; while (i < 1000000000) { i } //Increase the calculation amount return memoOne % 2 === 0; }, [memoOne]); const squaredNumber = useMemo(() => { let i = 0; while (i < 100000000) { i } //Increase the calculation amount console.log("squared the number"); return memoOne * memoOne; }, [memoOne]); return ( <div> <h1>{memoOne}</h1> <button onClick={incrementMemoOne}>Increment</button> <p>Is Even: {isEven ? 'Yes' : 'No'}</p> <p>Squared Number: {squaredNumber}</p> </div> ); } export default Memo;
...(The sample output screenshot or description should be included here)
useMemo
is a bit like a useCallback
Hook, but the difference is that useMemo
can store memorized values from a function, while useCallback
stores and returns the function itself.
useCallback
useCallback
The Hook is another interesting Hook, and the previous section is a spoiler of its features.
As we just saw, useCallback
works similarly to useMemo
Hooks, because they all use memory to cache certain content for later use. useMemo
stores the function's calculation as a cached value, while useCallback
stores and returns the function.
Like useMemo
, useCallback
is a good performance optimization because it stores and returns memorized callbacks and any of its dependencies without re-rendering.
const getMemoizedCallback = useCallback( () => { doSomething() }, [] );
import React, { useCallback, useState } from "react"; import CallbackChild from "./UseCallback-Child"; import "./App.css" export default function App() { const [toggle, setToggle] = useState(false); const [data, setData] = useState("I am a data that would not change at every render, thanks to the useCallback"); const returnFunction = useCallback( (name) => { return data name; }, [data] ); return ( <div> <button onclick="{()"> { setToggle(!toggle); }}> {/* Click To Toggle */} </button> {toggle && <h1>Toggling me no longer affects any function</h1>} <callbackchild returnfunction="{returnFunction}"></callbackchild> </div> ); }
// Subcomponent
import React, { useEffect } from "react"; function CallbackChild({ returnFunction }) { useEffect(() => { console.log("FUNCTION WAS CALLED"); }, [returnFunction]); return <p>{returnFunction(" Hook!")}</p>; } export default CallbackChild;
...(Sample or description of the sample output should be included here)
That's it! We just looked at five very handy React Hooks that I think are often overlooked. Like many such reviews, we just touch the surface of these Hooks. They each have their own subtleties and precautions that need to be considered when using them. But hopefully you have a good advanced concept for what they are and when they are better suited than other Hooks you may use more often.
The best way to fully understand them is practice. Therefore, I encourage you to practice using these Hooks in your app to better understand them. To do this, you can learn more about it by looking at the following resources:
<code>- Intro to React Hooks (Kingsley Silas) - Hooks at a Glance (React documentation) - Hooks Cheatsheet (Ohans Emmanuel) - The Circle of a React Lifecycle (Kingsley Silas) - Hooks of React Router (Agney Menon) - Testing React Hooks With Enzyme and React Testing Library (Kingsley Silas)</code>
The above is the detailed content of React Hooks: The Deep Cuts. For more information, please follow other related articles on the PHP Chinese website!