React Hooks

Why are Hooks needed?

We know that React’s one-way data flow and componentization help us turn a large project into a small, independent, reusable component. Sometimes, however, very complex components cannot be further broken down because their internal logic is stateful and cannot be abstracted into functional components. So sometimes we might write something that is very unsuitable for reusable development:

  • Large components are difficult to refactor
  • Repetitive logic requires repetitive code to be written in multiple life cycles of multiple components
  • Complex application patterns are similar to render props for higher-order components

Thankfully, Hooks let us organize the logic inside components into reusable isolation units.

Which are fixed by Hooks:

Reusing state-containing logic across components. Hooks can abstract state-containing logic from components and also help reuse logic without rewriting component structure. Hooks are generally used for functional components and do not work in class components. Let’s break them up based on what the code does, not the lifecycle. In short, Hooks enable us to use state variables and life-cycle like operations in functional components.

Use the syntax rules of Hooks

  • Hooks can only be called at the top level. Hooks are not called in loops, control flows, and nested functions.
  • You can only call hooks from functional components of React. Hooks are not called in regular JS functions.

Create Hooks

  • useuseStateCreate a Hook
import {useState} from 'react'; Function hooks(){// Declare a new state variable called count const [count, setCount] = useState(0); Return (<div> <p> <div> <p> {count} < / p > < button onClick = {() = > setCount (count + 1)} > click on add a < / button > < / div >)}Copy the code
  • useuseEffectTo perform the corresponding operation
import {useState, useEffect} from 'react'; function hooks(){ const [count, setCount] = useState(0); ComponentDidMount and componentDidUpdate are analogous to componentDidMount and componentDidUpdate. => { window.alert(`You have clicked ${count} times`); }) return (< div > < p > the current state of the amount is: {count} < / p > < button onClick = {() = > setCount (count + 1)} > click on add a < / button > < / div >)}Copy the code

The hook is independent

We use the same hook in two different components, they are independent of each other, and even two hooks in one component are independent of each other.

How does React ensure that useState is independent of each other

React ensures that usestates are independent of each other based on the order in which they appear.

// render const [num, setNum] = useState(1); // Initialize num to 1 const [STR, setStr] = useState('string'); // initialize STR to 'string' const [obj, setObj] = useState({id:1}); / /... Const [num, setNum] = useState(1); // Const [num, setNum] = useState(1); Const [STR, setStr] = useState('string'); const [STR, setStr] = useState('string'); Const [obj, setObj] = useState({id:1}); const [obj, setObj] = useState({id:1}); / /...Copy the code

React specifies that the hooks must be in the outer layer, not in the condition statement, to ensure that the hooks are executed in the same order. To do this, write the condition in the useEffect function

Effect Hooks

UseEffect is passed to React as a method that will be called after a DOM update. We usually put useEffect inside the component so that we can access the state and props directly. Remember, useEffect is called after every render.

Effects that need to be cleaned up

Sometimes we need to get data from an external data source, and we need to ensure that Effect is cleaned to avoid memory leaks. In this case, we need to return a function in Effect to clean it up. React will clean up every time the component touches the mount. A more commonly used scenario is that if an asynchronous request is executed in useEffect, due to the time uncertainty of asynchrony, it is necessary to terminate the previous request before executing the next asynchronous request, so we need to clean it up.

useEffect(() => { let canceled = false; const getData = async () => { const res = await fetch(api); if(! Canceled) {// show res}} getData(); Return () => {canceled = true; }});Copy the code

At this point, we can avoid race problems caused by asynchronous requests and thus avoid data instability when we re-render.

Configure effects that execute based on conditions

We can pass useEffect a second argument and only re-execute Effect if all the state values in the second argument (array) change

useEffect(() => { window.alert(`you had clicked ${count} times`); }, [count]); // Effect is re-executed only if count changesCopy the code

Use instances in functional components

We cannot useRef because the functional component does not have this, but hooks help us solve this problem by providing the useRef method to create an instance for us, and the parameters passed in will be mounted to the.current property of the instance, and the returned instance will last until the end of its life.

function RefExample() {
    const ref1 = useRef(null);
    return (
    	<div>
            <input ref={ref1} type="text" />
            <button onClick={() => {ref1.current.focus()}}
    	</div>
    )
}
Copy the code

Types of Hooks

If you prefer to use the Redux type of state management over the above state variable types, OK and React also provide the useReducer method. As an alternative to useState, we can use the Dispatch method to change the state variables.

// 初始化的状态变量
const initState = {count:0};
// 编写 reducer 处理函数
function reducer(state, action) {
    switch(action.type) {
        case 'increment': return {count: state.count + 1};
        case 'decrement': return {count: state.count - 1};
    }
}

function counter({initState}) {
    const [state, dispatch] = useReducer(reducer, initState);
    return (
    <div>
            <p>Count: {state.count}</p>
            <button onClick={() => dispatch({type: 'increment'})}>+</button>
            <button onClick={() => dispatch({type: 'decrement'})}>-</button>
   	</div>
    )
}
Copy the code

Hooks in callback form

Effect can be implemented by listening to the state variables and executing the callback function after the change. You may ask: why do Hooks use so many inline functions? Thankfully, closure functions in JavaScript are very fast, and they help a lot. There are two types of Hooks in the form of callback, useCallback and useMemo.

The conversion relationship between the two is:

useCallback(fn, inputs) === useMemo(() => fn, inputs)

How does useCallback help us improve performance? In fact, it caches an instance of the inline callback function for each render, which is then combined with shouldComponentUpdate or react.Memo to reduce unnecessary rendering. React.memo and react. useCallback are used together, and either of them may not improve performance.

The usage is shown as a form component below

function FormComponent() { const [text, setText] = useState(' '); const handleSubmit = useCallback(() => { console.log(`new test is ${text}`); }, [text]); return ( <div> <input value={text} onChange={(e) => setText(e.target.value)} /> <BigTree onSubmit={handleSubmit} /> // </div>)}Copy the code

A serious problem is that our BigTree relies on a state that is too variable, and whenever we type anything in the input box, BigTree will re-render multiple times to get the latest callback, which will no longer be cached.

One solution is for us to define a new instance that will update the latest value only when re-render is performed, so that instead of an oft-changing state, we can update it based on a ref instance that is updated in useLayoutEffect.

function FormComponent() { const [text, setText] = useState(' '); const textRef = useRef(); useLayoutEffect(() => { textRef.current = text; }) const handleSubmit = useCallback(() => { console.log(`new test is ${text}`); }, [textRef]); // Change only when textRef changes, Return (<div> <input value={text} onChange={(e) => setText(e.target.value)} /> <BigTree OnSubmit ={handleSubmit} /> //Copy the code

Multiple Effect update scenarios for Hooks

useLayoutEffect

Synchronization fires after DOM mutation before redrawing

It serves the same purpose as useEffect in that it performs side effects, except that effect is called synchronously after all DOM changes have been made. One big difference with useEffect is that useLayoutEffect is synchronous, whereas useEffect is asynchronous. Updates within useLayoutEffect are refreshed synchronously before the browser redraws the page layout. However, the official advice is to use useEffect whenever possible to avoid blocking visual updates.

The benefits of Hooks

  • Avoid using it when we reuse classesrender propsHigh order componentWhen the

Exaggerated level nesting.

  • Prevent us from writing a lot of repetitive code in the lifecycle function to implement the function.
  • This in classes is very confusing.