Preface React launched the new feature React Hooks in version v16.8. In my opinion, using React Hooks has the following advantages over previous class components:
In this article, we will give examples based on the usage scenarios to help you understand and skillfully use most of the features of React Hooks. The blog github address is: https://github.com/fengshi123/blog 1. State Hook 1. Basic usagefunction State(){ const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) } 2. UpdateThere are two ways to update: direct update and functional update. The differences in their application scenarios are:
// Update setState(newCount) directly; // Functional update setState(prevCount => prevCount - 1); 3. Achieve the merger Unlike the setState method in class components, useState does not automatically merge the updated object, but directly replaces it. We can use the functional setState combined with the spread operator to achieve the effect of merging and updating objects. setState(prevState => { // You can also use Object.assign return {...prevState, ...updatedValues}; }); 4. Lazy initialization state The initialState parameter only takes effect in the initial rendering of the component and is ignored in subsequent renderings. Its application scenario is: when creating the initial state is expensive, for example, it needs to be obtained through complex calculations; then you can pass in a function to calculate and return the initial state in the function. This function is only called during the initial rendering: const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; }); 5. Some key points (1) Unlike this.setState in a class, Hook updates a state variable by always replacing it instead of merging it; Effect Hook 1. Basic usagefunction Effect(){ const [count, setCount] = useState(0); useEffect(() => { console.log(`You clicked ${count} times`); }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) } 2. Clear operation To prevent memory leaks, the cleanup function is executed before the component is uninstalled; if the component is rendered multiple times (usually the case), the previous effect is cleared before the next effect is executed, that is, the return function in the previous effect is executed first, and then the non-return function in this effect is executed. useEffect(() => { const subscription = props.source.subscribe(); return () => { // Clear subscription subscription.unsubscribe(); }; }); 3. Execution period Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect do not block the browser from updating the screen, which makes your app look more responsive; (componentDidMount or componentDidUpdate will block the browser from updating the screen) 4. Performance optimization By default, React will delay calling the effect each time it waits for the browser to finish rendering the screen; however, if certain values have not changed between two re-renders, you can tell React to skip calling the effect by passing an array as the second optional parameter of useEffect: As shown below, if the count value has not changed between two renderings, then the effect will be skipped after the second rendering; useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Update only if count changes 5. Simulate componentDidMount If you want to run the effect only once (only when the component is mounted and unmounted), you can pass an empty array ([]) as the second parameter, as shown below. The principle is the same as described in point 4 performance optimization; useEffect(() => { ..... }, []); 6. Best Practices It can be hard to remember which props and state a function outside of an effect uses, which is why you usually want to declare the functions it needs inside an effect. // bad, not recommended function Example({ someProp }) { function doSomething() { console.log(someProp); } useEffect(() => { doSomething(); }, []); // 🔴 This is unsafe (it calls `doSomething` which uses `someProp`) } // good, recommended function Example({ someProp }) { useEffect(() => { function doSomething() { console.log(someProp); } doSomething(); }, [someProp]); // ✅ Safe (our effect only uses `someProp`) } If for some reason you can't move a function into an effect, there are a few other options:
It is recommended to enable the exhaustive-deps rule in eslint-plugin-react-hooks, which will issue warnings and provide repair suggestions when adding incorrect dependencies; // 1. Install the plugin npm i eslint-plugin-react-hooks --save-dev // 2. eslint configuration { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" } } 7. Some key points (1) The useEffect Hook can be considered as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount. 3. useContext A method used to handle multi-level data transfer. In the previous component tree, when a cross-level ancestor component wants to pass data to a grandchild component, in addition to passing props down layer by layer, we can also use the React Context API to help us do this. The usage examples are as follows: (1) Use the React Context API to create a Context outside the component import React from 'react'; const ThemeContext = React.createContext(0); export default ThemeContext; (2) Use Context.Provider to provide a Context object that can be shared by child components import React, { useState } from 'react'; import ThemeContext from './ThemeContext'; import ContextComponent1 from './ContextComponent1'; function ContextPage () { const [count, setCount] = useState(1); return ( <div className="App"> <ThemeContext.Provider value={count}> <ContextComponent1 /> </ThemeContext.Provider> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } export default ContextPage; (3) The useContext() hook function is used to introduce the Context object and obtain its value // Subcomponent, use grandchild component in subcomponent import React from 'react'; import ContextComponent2 from './ContextComponent2'; function ContextComponent () { return ( <ContextComponent2 /> ); } export default ContextComponent; // Grandchild component, use the Context object value in the grandchild component import React, { useContext } from 'react'; import ThemeContext from './ThemeContext'; function ContextComponent () { const value = useContext(ThemeContext); return ( <div>useContext:{value}</div> ); } export default ContextComponent; 4. useReducer 1. Basic usage Scenarios where useState is more suitable: for example, when the state logic processing is complex and contains multiple sub-values, or when the next state depends on the previous state; examples are shown below import React, { useReducer } from 'react'; interface stateType { count: number } interface actionType { type: string } const initialState = { count: 0 }; const reducer = (state:stateType, action:actionType) => { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } }; const UseReducer = () => { const [state, dispatch] = useReducer(reducer, initialState); return ( <div className="App"> <div>useReducer Count:{state.count}</div> <button onClick={() => { dispatch({ type: 'decrement' }); }}>useReducer reduce</button> <button onClick={() => { dispatch({ type: 'increment' }); }}>useReducer increase</button> </div> ); }; export default UseReducer; 2. Lazy initialization of stateinterface stateType { count: number } interface actionType { type: string, paylod?: number } const initCount = 0 const init = (initCount:number)=>{ return {count:initCount} } const reducer = (state:stateType, action:actionType)=>{ switch(action.type){ case 'increment': return {count: state.count + 1} case 'decrement': return {count: state.count - 1} case 'reset': return init(action.paylod || 0) default: throw new Error(); } } const UseReducer = () => { const [state, dispatch] = useReducer(reducer,initCount,init) return ( <div className="App"> <div>useReducer Count:{state.count}</div> <button onClick={()=>{dispatch({type:'decrement'})}}>useReducer reduce</button> <button onClick={()=>{dispatch({type:'increment'})}}>useReducer increase</button> <button onClick={()=>{dispatch({type:'reset',paylod:10 })}}>useReducer add</button> </div> ); } export default UseReducer; 5. Memo As shown below, when the parent component re-renders, the child component will also re-render, even if the child component's props and state have not changed. // Child component const ChildComp = () => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; // Parent component const Parent = () => { const [count, setCount] = useState(0); return ( <div className="App"> <div>hello world {count}</div> <div onClick={() => { setCount(count => count + 1); }}>Click to increase</div> <ChildComp/> </div> ); }; export default Parent; Improvement: We can use memo package to solve the above problem; but it only solves the situation where the parent component does not pass parameters to the child component and the parent component passes simple type parameters to the child component (such as string, number, boolean, etc.); if complex properties are passed, useCallback (callback event) or useMemo (complex properties) should be used // Child component const ChildComp = () => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; const MemoChildComp = memo(ChildComp); 6. useMemo Assume the following scenario: the parent component passes the info object property when calling the child component. When the parent component button is clicked, the console prints out the information about the child component being rendered. import React, { memo, useState } from 'react'; // Child component const ChildComp = (info:{info:{name: string, age: number}}) => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; const MemoChildComp = memo(ChildComp); // Parent component const Parent = () => { const [count, setCount] = useState(0); const [name] = useState('jack'); const [age] = useState(11); const info = { name, age }; return ( <div className="App"> <div>hello world {count}</div> <div onClick={() => { setCount(count => count + 1); }}>Click to increase</div> <MemoChildComp info={info}/> </div> ); }; export default Parent; Analysis of the reasons:
solve: Use useMemo to wrap the object attributes. useMemo has two parameters:
import React, { memo, useMemo, useState } from 'react'; // Child component const ChildComp = (info:{info:{name: string, age: number}}) => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; const MemoChildComp = memo(ChildComp); // Parent component const Parent = () => { const [count, setCount] = useState(0); const [name] = useState('jack'); const [age] = useState(11); // Use useMemo to wrap object properties const info = useMemo(() => ({ name, age }), [name, age]); return ( <div className="App"> <div>hello world {count}</div> <div onClick={() => { setCount(count => count + 1); }}>Click to increase</div> <MemoChildComp info={info}/> </div> ); }; export default Parent; 7. useCallback Continuing with the example in Chapter 6, suppose you need to pass the event to the child component, as shown below. When you click the parent component button, you will find that the console prints out information about the child component being rendered, indicating that the child component has been re-rendered. import React, { memo, useMemo, useState } from 'react'; // Child component const ChildComp = (props:any) => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; const MemoChildComp = memo(ChildComp); // Parent component const Parent = () => { const [count, setCount] = useState(0); const [name] = useState('jack'); const [age] = useState(11); const info = useMemo(() => ({ name, age }), [name, age]); const changeName = () => { console.log('Output name...'); }; return ( <div className="App"> <div>hello world {count}</div> <div onClick={() => { setCount(count => count + 1); }}>Click to increase</div> <MemoChildComp info={info} changeName={changeName}/> </div> ); }; export default Parent; Analyze the reasons:
solve: import React, { memo, useCallback, useMemo, useState } from 'react'; // Child component const ChildComp = (props:any) => { console.log('ChildComp...'); return (<div>ChildComp...</div>); }; const MemoChildComp = memo(ChildComp); // Parent component const Parent = () => { const [count, setCount] = useState(0); const [name] = useState('jack'); const [age] = useState(11); const info = useMemo(() => ({ name, age }), [name, age]); const changeName = useCallback(() => { console.log('Output name...'); }, []); return ( <div className="App"> <div>hello world {count}</div> <div onClick={() => { setCount(count => count + 1); }}>Click to increase</div> <MemoChildComp info={info} changeName={changeName}/> </div> ); }; export default Parent; 8. useRef The following are two usage scenarios of useRef: 1. Point to DOM element As shown below, a variable created using useRef points to an input element and focuses the input after the page is rendered: import React, { useRef, useEffect } from 'react'; const Page1 = () => { const myRef = useRef<HTMLInputElement>(null); useEffect(() => { myRef?.current?.focus(); }); return ( <div> <span>UseRef:</span> <input ref={myRef} type="text"/> </div> ); }; export default Page1; 2. Storing variables The role of useRef in react hook, as the official website says, is like a variable, similar to this, it is like a box, you can store anything. createRef returns a new reference each time it renders, while useRef returns the same reference each time, as shown in the following example: import React, { useRef, useEffect, useState } from 'react'; const Page1 = () => { const myRef2 = useRef(0); const [count, setCount] = useState(0) useEffect(()=>{ myRef2.current = count; }); function handleClick(){ setTimeout(()=>{ console.log(count); // 3 console.log(myRef2.current); // 6 },3000) } return ( <div> <div onClick={()=> setCount(count+1)}>Click count</div> <div onClick={()=> handleClick()}>View</div> </div> ); } export default Page1; 9. useImperativeHandle Usage scenario: The entire DOM node is obtained through ref, and useImperativeHandle can be used to control the exposure of only some methods and properties instead of the entire DOM node. 10. useLayoutEffectIts function signature is the same as useEffect, but it calls the effect synchronously after all DOM changes, so it is not shown here. useLayoutEffect is executed at the same time as componentDidMount and componentDidUpdate of the Class component you normally write; Summarize In this article, we will give examples based on the usage scenarios, hoping to help you understand and skillfully use most of the features of React Hooks. This concludes this article about the use of common scenarios of React Hooks (summary). For more content related to common scenarios of React Hooks, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: How to let DOSBox automatically execute commands after startup
>>: Basic installation tutorial of mysql decompression package
How to write configuration files and use MyBatis ...
Preface This article summarizes some implementati...
Overview Indexing is a skill that must be mastere...
ins and del were introduced in HTML 4.0 to help au...
1. Introduction Recently, I helped a friend to ma...
Here is a brief introduction to indexes: The purp...
Today I will share with you a breathing carousel ...
Regarding the issue of MySQL remote connection, w...
Table of contents Overview 1. Path module 2. Unti...
How to solve VMware workstation virtual machine c...
Table of contents 1. What is Pinia? 2. Pinia is e...
Have you ever had the need to compute a very larg...
Table of contents background question Problem ana...
Google's goal with Flutter has always been to...
1. Introduction Earlier we introduced the rapid d...