This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

1. The introduction of the hooks

Hook is a new feature added in React version 16.8.0 that allows use of state and other React features in function components.

✌️ Why use hooks?

  • It is difficult to reuse state logic between components

    Components that consist of providers, consumers, high-level components, render props, and other abstraction layers can form nested inferno. Hooks can be used to extract state logic from the components, making them individually testable and reusable. Hooks let you reuse state logic without modifying the component structure.

  • Complex components are difficult to understand

    Each lifecycle often contains some unrelated logic. For example, components often get data in componentDidMount and componentDidUpdate. However, the same componentDidMount may also contain a lot of other logic, such as setting event listeners, which will later be cleared in componentWillUnmount. Code that is related to each other and needs to be modified by comparison is split, while completely unrelated code is grouped together in the same way.

  • Unintelligible class

Here are some commonly used hooks.

2. useState

UseState allows function components to also have state states and to read and write state data.

const [xxx, setXxx] = useState(initValue); // Deconstruct the assignment
Copy the code

📐 useState () method

Parameters:

The first initialization of the specified value is internally cached. They can be assigned as needed using numbers or strings, not necessarily objects.

If you want to store two different variables in state, just call useState() twice.

The return value:

An array containing two elements, the first is the internal current state value, and the second is a function to update the state value, which is usually obtained directly by deconstructing assignment.

The 📐 setXxx () method

SetXxx (newValue) : takes a non-function value, specifies the new state value directly, and internally overwrites the original state value.

SetXxx (value => newValue) : a function that takes the original state value, returns the new state value, and internally overwrites the original state value.

📐 Complete example

const App = () = > {
    const [count, setCount] = useState(0);

    const add = () = > {
        // The first way
        // setCount(count + 1);
        // The second way
        setCount(count= > count + 1);
    };

    return (
        <Fragment>
            <h2>The current sum is: {count}</h2>
            <button onClick={add}>I + 1 point</button>
        </Fragment>
    );
};
Copy the code

UseState is just a Hook, and the only argument is the initial state, so we declare a state variable called count here, and we set it to 0. React will remember its current value when it repeats the render and provide the latest value to our function. We can update the current count by calling setCount.

In a function, we can just use count:

<h2> {count}</h2>Copy the code

Update the state:

setCount(count + 1);
setCount(count= > count + 1);
Copy the code

📐 uses multiple state variables

You can use State hooks multiple times in a component:

// Declare multiple state variables
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'learning Hook' }]);
Copy the code

📌 It is not necessary to use multiple state variables; you can still group related data. However, unlike this.setState in class, updating the state variable in useState is a replacement. It’s not a merger.

3. useEffect

UseEffect can perform side effects in function components (used to simulate lifecycle hooks in class components).

React side effects:

  • hairajaxRequest data fetch
  • Set the subscription/start timer
  • Manually change the real DOM

📐 Usage Rules

useEffect(() = > {
    // Any operation with side effects can be performed here
    // Equivalent to componentDidMount()
    return () = > {
        // Execute before component uninstallation
        // Do some finishing touches here, such as clearing the timer/unsubscribe, etc
        // Equivalent to componentWillUnmount()
    };
}, [stateValue]); / / to monitor stateValue
// If the array is omitted, all states are detected and the callback is called again if the state is updated
// If [] is specified, the callback is executed only once after the first render()
Copy the code

UseEffect can be thought of as a combination of three functions:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

📐 run Effect for each update

// ...
useEffect(() = > {
    // ...
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () = > {
        ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
});
Copy the code

Before calling a new effect, the previous effect is cleaned up. Here is a sequence of possible subscribe and unsubscribe operation calls by time:

// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run the first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clear the previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run the next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clear the previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run the next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clear the last effect
Copy the code

📐 Optimize the performance by skipping Effect

If certain values haven’t changed between rerenders, React can be told to skip the effect call by passing an array as the second optional argument to useEffect:

useEffect(() = > {
    document.title = `You clicked ${count} times`;
}, [count]); // Update only when count changes
Copy the code

If the value of count is 5 and the component is rerendered with count equal to 5, React will compare the previous render [5] to the last render [5]. Because all elements in an array are equal (5 === 5), React skips this effect, which is a performance optimization.

When rendering, if the count value is updated to 6, React will compare the elements in the array [5] from the previous render with the elements in the array [6] from this render. This time because 5! == 6, React will call effect again.

📌 React performs effect if there are multiple elements in the array, even if only one element changes.

The same applies to effect with a clear operation:

useEffect(() = > {
    function handleStatusChange(status) {
        setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () = > {
        ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
}, [props.friend.id]); // Re-subscribe only if props.friend.id changes
Copy the code

📐 Uses multiple effects for separation of concerns

One of the purposes of using hooks is to solve the problem that lifecycle functions in a class often contain unrelated logic that is separated into several different methods. The following code is the component that combines the counter and friend online status indicator logic from the previous example:

function FriendStatusWithCounter(props) {
    const [count, setCount] = useState(0);
    useEffect(() = > {
        document.title = `You clicked ${count} times`;
    });

    const [isOnline, setIsOnline] = useState(null);
    useEffect(() = > {
        function handleStatusChange(status) {
            setIsOnline(status.isOnline);
        }

        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () = > {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    });
    // ...
}
Copy the code

📐 Complete example

import React, { useState, Fragment, useEffect } from 'react';
import ReactDOM from 'react-dom';

const App = () = > {
    const [count, setCount] = useState(0);

    useEffect(() = > {
        let timer = setInterval(() = > {
            setCount(count= > count + 1);
        }, 500);
        console.log('@ @ @ @');
        return () = > {
            clearInterval(timer);
        };
    }, [count]);
    // Check for changes in count and output '@@@@' for each change.
    // If [], '@@@@' is printed only once

    const add = () = > {
        setCount(count= > count + 1);
    };

    const unmount = () = > {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'));
    };

    return (
        <Fragment>
            <h2>The current sum is: {count}</h2>
            <button onClick={add}>I + 1 point</button>
            <button onClick={unmount}>Uninstall the component</button>
        </Fragment>
    );
};

export default App;
Copy the code

4. useRef

UseRef can store/find labels or any other data within a function component. Saves the label object in the same way as react.createref ().

const refContainer = useRef(initialValue);
Copy the code

UseRef returns a mutable ref object whose.current property is initialized to the passed parameter (initialValue). The returned REF object remains the same throughout the lifetime of the component.

📌 useRef does not notify you when the content of the ref object changes. Changing the.current property does not cause the component to rerender.

import React, { Fragment, useRef } from 'react';

const Demo = () = > {
    const myRef = useRef();

    // Prompt for input callback
    function show() {
        console.log(myRef.current.value);
    }

    return (
        <Fragment>
            <input type="text" ref={myRef} />
            <button onClick={show}>Click show value</button>
        </Fragment>
    );
};

export default Demo;
Copy the code

5. Hook rules

Hooks are JavaScript functions, but using them comes with two additional rules:

Use hooks only at the top level

Do not call a Hook inside a loop, condition, or nested function. Call the Hook at the top of the React function.

If you want to conditionally execute an effect, you can place the judgment inside the Hook:

useEffect(function persistForm() {
    // 👍 Place the condition in effect
    if(name ! = =' ') {
        localStorage.setItem('formData', name); }});Copy the code

Call hooks only in React functions

Do not call hooks in normal JavaScript functions. You can:

  • Call Hook in React function component
  • Call other hooks in a custom Hook

Reference: React Hook