πŸŽ™ ️ preface

React 16.8 has a very powerful feature called React hooks. In fact, React Hooks are similar to vue3’s Composition API in that they are designed to improve development efficiency.

So, in the following article, we will take you through 0 to 1 to learn about React hooks and some of the commonly used apis.

Without further ado, react-Hook is on its way

1. πŸ“» Overview

1. React Hooks

  • React HooksIs an optional feature, usually usedclassComponent to compare to it;
  • 100%Backward compatibility, noDestructive modification;
  • Won’t replaceclassComponent, no plans to removeclassComponents.

Remember remember remember Hooks

(1) Review React functional components

Let’s start with a review of class and function components as follows:

The class components:

/ / class components
class List extends React.Component {
    constructor() {
        super(props)
    }
    render() {
        const { List } = this.props
        
        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                	<span>{item.title}</span>
                </li>
        })}</ul>}}Copy the code

Function components:

// Function components
function List(props) {
    const { list } = props
    
    return <ul>{list.map((item, index) => {
        return <li key={item.id}>
            	<span>{item.title}</span>
            </li>
    })}</ul>
}
Copy the code

(2) Features of function components

Function components are characterized by:

  • No component instance;
  • No life cycle;
  • There is nostate ε’Œ setState, can only receiveprops 。

(3) Class component problems

Above we said that the function component is a pure function that only accepts props and does nothing else. The class component does this, but the class component has the following problems:

  • Large components are difficult to break down and refactor, and difficult to test (i.eclassNot easy to split);
  • The same business logic is scattered into various methods, resulting in logic chaos.
  • Reuse logic becomes complex, such asMixins 、 HOC 、 Render Props 。

So, with these issues, there are React Hooks.

(4) React component

  • The React component is easier to express as a function:

  • React advocates functional programming, that is, view=fn(props);

  • Functions are more flexible, easier to split, and easier to test;

  • But the function component was too simple and needed more power — hence React Hooks.

πŸͺ• several Hooks

1, the State Hook πŸ—ž ️

(1) Let function components implement state and setState

  • The default function component is nonestate;
  • The function component is a pure function that is destroyed upon execution and cannot be storedstore οΌ›
  • Need to beState Hook, i.e., thestoreFunctions “hook” into pure functions.

(2) Illustrate with examples

Suppose we now want to modify some value by clicking on a button. Now let’s deal with useState in hooks. The specific code is as follows:

import React, { useState } from 'react'

function ClickCounter() {
    // Array destruct
    // useState is a Hook
    const [count, setCount] = useState(0) // Pass in an initial value. The initial value can be a number/string/array/object, etc

    const [name, setName] = useState(Monday Lab)

    // const arr = useState(0)
    // const count = arr[0]
    // const setCount = arr[1]

    function clickHandler() {
        setCount(count + 1)
        setName(name + '2021')}return <div>
        <p>You clicked {count} times {name}</p>
        <button onClick={clickHandler}>Click on the</button>
    </div>
}

export default ClickCounter

Copy the code

The browser displays the following information:

In the above code, count is a value of state, and setCount is a function that modifies state. Similarly, name is a state value, and setName is a function that changes the name value.

If we use hooks to fix state, we need to fix state in the form of const [count, setCount] = useState(0), without having to use them in the class component.


For the above functionality, if implemented using the class component, the code is as follows:

import React from 'react'

class ClickCounter extends React.Component {
    constructor() {
        super(a)/ / define the state
        this.state = {
            count: 0.name: Monday Lab
        }

        this.clickHandler = this.clickHandler.bind(this)}render() {
        return <div>
            <p>You clicked {this.state.count} times {this.state.name}</p>
            <button onClick={this.clickHandler}>Click on the</button>
        </div>
    }
    clickHandler() {
        / / modify state
        this.setState({
            count: this.state.count + 1.name: this.state.name + '2021'}}})export default ClickCounter

Copy the code

As you can see, if you use the class component, you need to define state first, then define a function, and then use setState to change the value, which seems a bit cumbersome.

I believe that by now you have sensed the delight of hooks.

Below, let’s summarize some knowledge points about useState.

(3) useState use summary

  • useState(xxx)Pass in the initial value and return the array[state, setState] οΌ›
  • throughstateGet the value;
  • throughsetState(xxx)Modify the value.

(4) Hooks naming specification

  • Stipulate allHooksuseuseAt the beginning, such asuseXxx οΌ›
  • The customHookAlso want touseAt the beginning.
  • nonHooksTry not to use ituseXxxIt’s easy to misunderstand.

2, Effect Hook πŸ—ž ️

(1) Let the function component simulate the life cycle

  • The default function component has no life cycle;
  • The function component is a pure function, which is destroyed immediately after execution and cannot realize its own life cycle.
  • useEffect HookHook the lifecycle into a pure function.

(2) Illustrate with examples

Similarly, let’s use the same example with useState to experience the useEffect.

We first in SRC | components to set up a file, named LiftCycles. Js. The specific code is as follows:

import React, { useState, useEffect } from 'react'

function LifeCycles() {
    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    // Simulate DidMount and DidUpdate for class components
    useEffect(() = > {
        console.log('Send an Ajax request here')})function clickHandler() {
        setCount(count + 1)
        setName(name + '2020')}return <div>
        <p>You clicked {count} times {name}</p>
        <button onClick={clickHandler}>Click on the</button>
    </div>
}

export default LifeCycles

Copy the code

Now we register the component in app.js. The specific code is as follows:

import React, { useState } from 'react';
import LifeCycles from './components/LifeCycles'

function App() {
  const [flag, setFlag] = useState(true)

  return (
    <div
      {flag && <LifeCycles/>}
    </div>
  );
}

export default App;

Copy the code

The browser displays the following information:

As you can see, useEffect emulates both DidMount and DidUpdate lifecycle functions if passed in only one function. Every time we click, the browser prints once, meaning that component loading and component updating are done together.


If we want to separate component loading and component updating, we can do that. Let’s change the LiftCycles. Js code. The specific code is as follows:

import React, { useState, useEffect } from 'react'

function LifeCycles() {
    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    // Simulate DidMount of the class component
    useEffect(() = > {
        console.log('Load done')}, [])// The second argument is [] (independent of any state)

    // Simulate the DidUpdate of the class component
    useEffect(() = > {
        console.log('Updated')
    }, [count, name]) // The second argument is the state of the dependency

    function clickHandler() {
        setCount(count + 1)
        setName(name + '2020')}return <div>
        <p>You clicked {count} times {name}</p>
        <button onClick={clickHandler}>Click on the</button>
    </div>
}

export default LifeCycles

Copy the code

The browser displays the following information:

As you can see, the first time I loaded it and updated it once. By the time we click, it’s already loaded, so it’s an update.

UseEffect is used when loading and updating.

UseEffect can also take a second parameter, which is null and does not depend on any state, indicating the lifetime of componentDidMount. Conversely, the second parameter emulates the componentDidUpdate life cycle if it does not depend on a value or receives a value dependent on state.


At this point, some of you may also want to ask, what about the life cycle related to destruction? Let’s go ahead and modify the LiftCycles. Js code. Details are as follows:

import React, { useState, useEffect } from 'react'

function LifeCycles() {
    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    // Simulate DidMount of the class component
    useEffect(() = > {
        let timerId = window.setInterval(() = > {
            console.log(Date.now())
        }, 1000)

        // Return a function
        / / simulation WillUnMount
        return () = > {
            window.clearInterval(timerId)
        }
    }, [])

    function clickHandler() {
        setCount(count + 1)
        setName(name + '2020')}return <div>
        <p>You clicked {count} times {name}</p>
        <button onClick={clickHandler}>Click on the</button>
    </div>
}

export default LifeCycles

Copy the code

You can see the above code, where we return a function to simulate the life cycle of componentWillUnMount, to destroy the task executed in each timer.

Here, I believe that you have a certain understanding of the useEffect. Now, let’s summarize the useEffect.

(3) useEffect summary

  • simulationcomponentDidMount - useEffectRely on[] οΌ›
  • simulationcomponentDidUpdate - useEffectNo dependency, or dependence[a, b] οΌ›
  • simulationcomponentWillUnMount - useEffect δΈ­Return a function.

(4) useEffect makes the pure function have side effects

  • By default, when a pure function is executed, it only needs to enter parameters and return the result, with no side effects.
  • Side effects are caused outside the function, such as setting a global scheduled task.
  • Components require side effects, so they douseEffect“Hook” into pure functions;
  • As a result,useEffectThis side effect is not a bad thing.

(5) useEffect returns function fn

One notable case is that we simulated WillUnMount by returning a function above, but the simulated result is not quite equal to WillUnMount. Now, we are in the SRC | components to set up a file, named FriendStatus. Js. The specific code is as follows:

import React, { useState, useEffect } from 'react'

function FriendStatus({ friendId }) {
    const [status, setStatus] = useState(false)

    / / DidMount and DidUpdate
    useEffect(() = > {
        console.log('Start listening${friendId}Online status')

        // 【 special attention 】
        // This is not exactly the same as WillUnMount
        // A change in props, that is, an update, is also performed to end the listen
        // To be exact: the returned function will be executed before the next effect execution
        return () = > {
            console.log('End monitoring${friendId}Online status')}})return <div>Friend {friendId} Online status: {status.tostring ()}</div>
}

export default FriendStatus

Copy the code

The code for app.js is as follows:

import React, { useState } from 'react';
import FriendStatus from './components/FriendStatus'

function App() {
  const [flag, setFlag] = useState(true)
  const [id, setId] = useState(1)

  return (
    <div>
      <div>
        <button onClick={()= > setFlag(false)}>flag = false</button>
        <button onClick={()= > setId(id + 1)}>id++</button>
      </div>

      {flag && <FriendStatus friendId={id}/>}
    </div>
  );
}

export default App;

Copy the code

The browser displays the following information:

And as you can see, when you start the next listener, you end the previous listener before you start the next one. As you can see in the picture, at first we are listening to friend 1, then when we want to click id++ button to listen to friend 2, useEffect will end the status of friend 1 first, and then let friend 2 online.

One point to note at this point is that executing the function that ends the listening executes the DidUpdate life cycle, not the WillUnMount life cycle. The function is in the update state, not the destroy state.

Based on the above, let’s make a summary. Details are as follows:

  • whenuseEffectDepends on the[]Is executed when the component is destroyedreturnThe function offnIs equal toWillUnMountLife cycle;
  • whenuseEffectTo be free from or dependent on[a, b]The component is executed at update timereturnThe function offn; That is, at the next executionuseEffectBefore, it will be executed firstfn, and the simulation isDidUpdateLife cycle.

3. HooksπŸ—žοΈ

Hooks β†’ useState and useEffect. Next, let’s look at a few other hooks that are less commonly used.

(1) useRef

Ref is used for modifying values and retrieving DOM elements. Let’s start with some code:

import React, { useRef, useEffect } from 'react'

function UseRef() {
    const btnRef = useRef(null) / / initial value

    const numRef = useRef(0)
    numRef.current = 2;

    useEffect(() = > {
        console.log(btnRef.current) / / the DOM node
        console.log(numRef.current); / / value
    }, [])

    return <div>
        <button ref={btnRef}>click</button>
    </div>
}

export default UseRef

Copy the code

In this case, the browser displays:

As you can see, if you bind btnRef to ref={btnRef}, then btnref. current gets the current DOM node.

The other case is where you locate numRef, and if we assign and modify it, numref.current will end up with the modified value.

(2) useContext

Most of the time, we often have some static properties need to be switched, such as: theme color switch. That’s when you need to use the context context. In a hook, there is useContext that can handle this. Let’s start with some demo code:

import React, { useContext } from 'react'

// Theme color
const themes = {
    light: {
        foreground: '# 000'.background: '#eee'
    },
    dark: {
        foreground: '#fff'.background: '# 222'}}/ / create the Context
const ThemeContext = React.createContext(themes.light) / / initial value

function ThemeButton() {
    const theme = useContext(ThemeContext)

    return <button style={{ background: theme.background.color: theme.foreground}} >
        hello world
    </button>
}

function Toolbar() {
    return <div>
        <ThemeButton></ThemeButton>
    </div>
}

function App() {
    return <ThemeContext.Provider value={themes.dark}>
        <Toolbar></Toolbar>
    </ThemeContext.Provider>
}

export default App

Copy the code

The browser displays the following information:

Now it’s a theme on a black background. If we want to switch to a theme with a white background, we can just change the bottom code value={themes.dark} to value={themes.light}.

(3) the useReducer

In redux, we useReducer, but there is a difference between useReducer and redux. UseReducer states individual components

Let’s start with some code:

import React, { useReducer } from 'react'

const initialState = { count: 0 }

const reducer = (state, action) = > {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 }
        case 'decrement':
            return { count: state.count - 1 }
        default:
            return state
    }
}

function App() {
    Const [count, setCount] = useState(0)
    const [state, dispatch] = useReducer(reducer, initialState)

    return <div>
        count: {state.count}
        <button onClick={()= > dispatch({ type: 'increment' })}>increment</button>
        <button onClick={()= > dispatch({ type: 'decrement' })}>decrement</button>
    </div>
}

export default App

Copy the code

The browser displays the following information:

The values we will change are put into the Reducer, which we then pass to the Reducer to useRouter. Finally, the values are bound through dispatch.

Let’s tease out the differences between useRuducer and redux:

  • useReducer 是 useStateFor processingstateComplex changes of;
  • useReducer 是Individual component state managementComponent communication is also requiredprops οΌ›
  • reduxIs a globalState management.Multiple components share data.

(4) useMemo

UseMemo is part of the performance tuning in hooks. When we’re not using useMemo, the state looks like this. Take a look at this code:

import React, { useState } from 'react'

/ / child component
function Child({ userInfo }) {
    console.log('Child render... ', userInfo)

    return <div>
        <p>This is Child {userInfo.name} {userInfo.age}</p>
    </div>
}

/ / the parent component
function App() {
    console.log('Parent render... ')

    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    const userInfo = { name, age: 20 }

    return <div>
        <p>
            count is {count}
            <button onClick={()= > setCount(count + 1)}>click</button>
        </p>
        <Child userInfo={userInfo}></Child>
    </div>
}

export default App

Copy the code

At this point, the console prints as follows:

As you can see, the Child component is reprinted every time we click, regardless of whether the value of state is updated. This is performance – intensive for the program. Therefore, we use useMemo to prevent Child events from being printed frequently and improve the performance of the program. The code modification is as follows:

import React, { useState, memo, useMemo } from 'react'

/ / child component
// Similar to class PureComponent, shallow comparison of props
const Child = memo(({ userInfo }) = > {
    console.log('Child render... ', userInfo)

    return <div>
        <p>This is Child {userInfo.name} {userInfo.age}</p>
    </div>
})

/ / the parent component
function App() {
    console.log('Parent render... ')

    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    // Use useMemo to cache data depending on [name]
    {name, age: 18} the cache is invalidated when name is updated
    const userInfo = useMemo(() = > {
        return { name, age: 18 }
    }, [name])

    return <div>
        <p>
            count is {count}
            <button onClick={()= > setCount(count + 1)}>click</button>
        </p>
        <Child userInfo={userInfo}></Child>
    </div>
}

export default App

Copy the code

The browser displays the following information:

As you can see, the Child component will not print unless it is updated for the first time. First, we use the memo to wrap the subcomponents. Then, we use useMemo to cache the data, and this data depends on [name]. It is worth noting that this does not work if you do not rely on [name].

Now, let’s summarize useMemo as follows:

  • ReactIt’s updated by defaultAll child components;
  • classComponents useshouldComponentUpdate ε’Œ PureComponentDo optimization;
  • HooksThe use ofuseMemoDo optimization, but optimization principle andclassThe components are the same.

(5) useCallback

We talked about using useMemo to cache data. Now let’s talk about useCallback. UseCallback is mainly used to cache functions.

Assuming we don’t use useCallback now, run the following code:

import React, { useState, memo, useMemo, useCallback } from 'react'

// The memo equals PureComponent
const Child = memo(({ userInfo, onChange }) = > {
    console.log('Child render... ', userInfo)

    return <div>
        <p>This is Child {userInfo.name} {userInfo.age}</p>
        <input onChange={onChange}></input>
    </div>
})

/ / the parent component
function App() {
    console.log('Parent render... ')

    const [count, setCount] = useState(0)
    const [name, setName] = useState(Monday Lab)

    // Use useMemo to cache data
    const userInfo = useMemo(() = > {
        return { name, age: 21 }
    }, [name])

    function onChange(e) {
        console.log(e.target.value)
    }

    return <div>
        <p>
            count is {count}
            <button onClick={()= > setCount(count + 1)}>click</button>
        </p>
        <Child userInfo={userInfo} onChange={onChange}></Child>
    </div>
}

export default App

Copy the code

The browser displays the following output:

As you can see, if useCallback is not used, it will always print out the child components, that is, the onChange event will always run. Now, let’s transform the onChange event as follows:

// Use the useCallback cache function
const onChange = useCallback(e= > {
     console.log(e.target.value)
}, [])
Copy the code

Now let’s take a look at what the browser looks like:

Add the useCallback and the Child is not updated.

Here’s a summary of useMemo and useCallback:

  • useMemoCache data;
  • useCallbackCache function;
  • Both of themReact HooksIs a common optimization strategy.

4. Custom HookπŸ—žοΈ

(1) Why use custom Hook

  • Used to encapsulate common functions;
  • canThe development ofanduseThe third partyHooks οΌ›
  • The customHookBrings infinite extensibility and decouples code.

(2) Illustrate with examples

Suppose we now wanted to encapsulate a useAxios, how would we do that?

First, we need to install AXIOS in our project as follows:

npm i axios --save
Copy the code

Then, we in the project of SRC | customHooks folder to create a new file, named useAxios. Js. The specific code is as follows:

import { useState, useEffect } from 'react'
import axios from 'axios'

// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url) {
    // Loading Simulates whether to wait
    const [loading, setLoading] = useState(false)
    // The data returned when the request succeeds
    const [data, setData] = useState()
    // Some information returned when the request failed
    const [error, setError] = useState()

    useEffect(() = > {
        // Use AXIos to send network requests
        setLoading(true)
        axios.get(url) // Send a get request
            .then(res= > setData(res))
            .catch(err= > setError(err))
            .finally(() = > setLoading(false))
    }, [url])

    return [loading, data, error]
}

export default useAxios

Copy the code

Go on, we in the project of SRC | components folder, create a new file, named CustomHookUsage. Js. This component is mainly used to use the useAxios hook as defined above. The specific code is as follows:

import React from 'react'
import useAxios from '.. /customHooks/useAxios'

function App() {
    const url = 'http://localhost:3000/'
    // Array destruct
    const [loading, data, error] = useAxios(url)

    if (loading) return <div>loading...</div>

    return error
        ? <div>{JSON.stringify(error)}</div>
        : <div>{JSON.stringify(data)}</div>
}

export default App

Copy the code

Finally, we’ll use it in our project’s app.js. The specific code is as follows:

import React, { useState } from 'react';
import CustomHookUsage from './components/CustomHookUsage'

function App() {

  return (
    <div>
      {<CustomHookUsage/>}
    </div>
  );
}

export default App;

Copy the code

At this point, let’s take a look at the browser display:

As you can see, loading occurs first while waiting. After that, if the wait is over and the request is successful, data is returned. Of course, we are not demonstrating the error case here. You can test the error case by changing the URL to a request address that does not exist.

(3) Summary

After looking at the demo code above, let’s make a summary:

  • The customhookIs essentially afunctionIn order touseAt the beginning.
  • The interior can be used normallyuseState 、 useEffectOr otherHooks οΌ›
  • Custom return result, format is unlimited;

Here we recommend two third-party custom Hook libraries:

  • The react – hooks: nikgraf. Making. IO/react – hooks…
  • hooks: github.com/umijs/hooks

5. Two important rules of Hooks πŸ—žοΈ

(1) Hooks use specification

  • Hooks only work with react function components and custom Hooks, not elsewhere. For example, class components and ordinary functions;

  • Can only be used for top-level code, cannot use Hooks in loops, judgments;

  • React-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks The configuration is as follows:

    // ESLint configuration files
    {
        "plugins": [
            / /... N lines omitted here...
            "react-hooks"]."rules": {
            / /... N lines omitted here...
            "react-hooks/rules-of-hooks": "error".// Check the Hook rules
            "react-hooks/exhaustive-deps": "warn" // Check for effect dependencies}}Copy the code

(2) The sequence of calls must be consistent

In react-hooks, call order is a key issue. Why is that?

Let’s use a piece of code to demonstrate:

import React, { useState, useEffect } from 'react'

function Teach({ couseName }) {
    // Function components, pure functions, are destroyed upon execution
    // So, whether the component is initialized or re-render
    // this function is re-executed to get the latest component
    // This is different from class components

    // render: initializes state value 'zhang SAN'
    // re-render: render state
    const [studentName, setStudentName] = useState('Joe')

    // if (couseName) {
    // const [studentName, setStudentName] = useState(' studentName ')
    // }

    // render: initializes state 'double'
    // re-render: render state
    const [teacherName, setTeacherName] = useState(Monday Lab)

    // if (couseName) {
    // useEffect(() => {
    // // Simulate student check-in
    // localStorage.setItem('name', studentName)
    / /})
    // }

    // render: add effect function
    // re-render: replace effect function (internal function will also be redefined)
    useEffect(() = > {
        // Simulate student check-in
        localStorage.setItem('name', studentName)
    })

    // render: add effect function
    // re-render: replace effect function (internal function will also be redefined)
    useEffect(() = > {
        // Start the class
        console.log(`${teacherName}Class begins, students${studentName}`)})return <div>TeacherName: {couseName}, teacherName: {studentName}</div>
}

export default Teach

Copy the code

The above code demonstrates a function component. In the case of a function component, it is a pure function that is usually destroyed after execution. Therefore, either component initialization or component re-render will re-execute this function to get the latest component. This is different from class components.

Therefore, if we wrap const [studentName, setStudentName] = useState(‘ studentName ‘) in an if statement, it will be blocked. At the same time, if you follow the following order, it is possible to assign the teacherName value to teacherName. Therefore, in react-hooks, we cannot use react-hooks in loops and judgments, which could easily result in out-of-order calls.

Based on the above analysis, let’s summarize this rule:

  • Whether it isrenderorre-render , HooksThe order of calls must be consistent;
  • ifHooksAppear in thecycle,judgeIn,There is no guarantee of a consistent order;
  • HooksHeavily dependent on call order! Important!

⌨️ react-hooks component logic reuse

1. Logical reuse of class components

Class components come in three forms of logical reuse. Respectively is:

  • Mixin
  • High order componentHOC
  • Render Prop

Here are the disadvantages of each of them.

(1) a Mixin

  • The source of the variable scope is unclear
  • Attribute name repetition
  • mixinsToo many introductions can cause order conflicts

(2) High-order component HOC

  • Component hierarchies are too nested to render and debug easily
  • HOCWill be hijackedpropsMust be strictly regulated.Prone to omissions

(3) Render Prop

  • Learning is expensive and difficult to understand
  • Only pure functions can be passed, and by default pure functions are limited

Now that you’ve seen the shortcomings of the three class components, let’s look at how to reuse component logic using Hooks.

2. Use Hooks for component logic reuse

The essence of using hooks to make components logically reusable is this: custom hooks. Let’s show this with an example.

(1) Example illustration

First of all, we in the project of SRC | customHooks folder to create a new file, named useMousePosition. Js. The specific code is as follows:

import { useState, useEffect } from 'react'

function useMousePosition() {
    const [x, setX] = useState(0)
    const [y, setY] = useState(0)

    useEffect(() = > {
        function mouseMoveHandler(event) {
            // event.clientX can get the cursor position
            setX(event.clientX)
            setY(event.clientY)
        }

        // Bind events
        document.body.addEventListener('mousemove', mouseMoveHandler)

        // Unbind events
        return () = > document.body.removeEventListener('mousemove', mouseMoveHandler)
    }, [])

    return [x, y]
}

export default useMousePosition

Copy the code

Go on, we in the project of SRC | components folder, create a new file, named CustomHookUsage. Js. This component is primarily used to use the logic in the useMousePosition above. The specific code is as follows:

import React from 'react'
import useMousePosition from '.. /customHooks/useMousePosition'

function App() {

    const [x, y] = useMousePosition()
    return <div style={{ height: '500px', backgroundColor: '#ccc' }}>
        <p>Mouse position {x} {y}</p>
    </div>
}

export default App

Copy the code

Finally, we’ll use it in our project’s app.js. The specific code is as follows:

import React, { useState } from 'react';
import CustomHookUsage from './components/CustomHookUsage'

function App() {

  return (
    <div>
      {<CustomHookUsage/>}
    </div>
  );
}

export default App;

Copy the code

At this point, let’s take a look at the browser display:

Using hooks for component logic reuse is much simpler and more flexible than using class components. Let’s review the benefits of hooks for component logic reuse.

(2) Benefits

Benefits of reusing component logic:

  • In full compliance withHooksOriginal rules, no other requirements, easy to understand and remember;
  • The scope of variables is clear;
  • No component nesting occurs.

πŸ“€React Hooks

1. UseState initializes the value, valid only for the first time

Let’s start with a piece of code that looks like this:

import React, { useState } from 'react'

/ / child component
function Child({ userInfo }) {
    // render: initializes state
    // re-render: only the initialized state value is restored, no new value is set
    // Only setName can be used
    const [ name, setName ] = useState(userInfo.name)

    return <div>
        <p>Child, props name: {userInfo.name}</p>
        <p>Child, state name: {name}</p>
    </div>
}


function App() {
    const [name, setName] = useState('Monday')
    const userInfo = { name }

    return <div>
        <div>
            Parent &nbsp;
            <button onClick={()= >SetName (' Tuesday ')}>setName</button>
        </div>
        <Child userInfo={userInfo}/>
    </div>
}

export default App

Copy the code

The browser displays the following information:

As you can see, when we click, we only change the value of useinfo.name, which is the value of the first Child. Useinfo. name is assigned to name, so why does the second Child not change?

The reason is that, with useState, we always set an initial value to state, and this initial value, even if the data is updated, is only valid for the first time, and the updated value is not assigned to it. So the value of name in the second Child below is always Monday.

2. UseEffect cannot modify state internally

Let’s simulate a piece of code:

import React, { useState, useEffect } from 'react'

function UseEffectChangeState() {
    const [count, setCount] = useState(0)

    / / simulation DidMount
    useEffect(() = > {
        console.log('useEffect... ', count)

        // Scheduled task
        const timer = setInterval(() = > {
            console.log('setInterval... ', count)
            setCount(count + 1)},1000)

        // Clear a scheduled task
        return () = > clearTimeout(timer)
    }, []) // Dependency is []

    // dependency [] : re-render does not re-execute effect function
    // Without dependencies: re-render re-executes effect

    return <div>count: {count}</div>
}

export default UseEffectChangeState

Copy the code

Now let’s take a look at the browser:

As you can see, if we had done what we expected, we would have incrementally printed 1234, but it didn’t. In the end, the timer was always printed with an initial value of 0, that is, the state is not used in useEffect.

Therefore, this point in our daily development needs to pay special attention to, it is possible to write the code while writing the bug appears in this.


Now that the problem has arisen, we need to think about its solution. The transformation is as follows:

import React, { useState, useRef, useEffect } from 'react'

function UseEffectChangeState() {
    const [count, setCount] = useState(0)

    / / simulation DidMount
    const countRef = useRef(0)
    useEffect(() = > {
        console.log('useEffect... ', count)

        // Scheduled task
        const timer = setInterval(() = > {
            console.log('setInterval... ', countRef.current)
            // setCount(count + 1)
            setCount(++countRef.current)
        }, 1000)

        // Clear a scheduled task
        return () = > clearTimeout(timer)
    }, []) // Dependency is []

    // dependency [] : re-render does not re-execute effect function
    // No dependencies: re-render will re-execute effect

    return <div>count: {count}</div>
}

export default UseEffectChangeState

Copy the code

The browser displays the following information:

If we look at the code above, we can see that we can change the value of state by placing countRef outside useEffect and using useRef.

3. UseEffect may have an infinite loop

Suppose we want to send a network request using AXIos, and we want to add some configuration to the URL, we might do something like this:

import { useState, useEffect } from 'react'
import axios from 'axios'

// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url, config = {}) {
    const [loading, setLoading] = useState(false)
    const [data, setData] = useState()
    const [error, setError] = useState()

    useEffect(() = > {
        // Use AXIos to send network requests
        setLoading(true)
        axios(url, config) // Send a get request
            .then(res= > setData(res))
            .catch(err= > setError(err))
            .finally(() = > setLoading(false))
    }, [url, config])

    return [loading, data, error]
}

export default useAxios

Copy the code

This seems reasonable, but when config uses a reference data type, i.e. Config ={} or ConfGi =[], useEffect is used in an infinite loop, sending requests indefinitely. Therefore, when we want to pass values to Axios, we can only pass values of primitive data types. The transformation code is as follows:

import { useState, useEffect } from 'react'
import axios from 'axios'

// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url) {
    const [loading, setLoading] = useState(false
    const [data, setData] = useState()
    const [error, setError] = useState()

    useEffect(() = > {
        // Use AXIos to send network requests
        setLoading(true)
        axios.get(url) // Send a get request
            .then(res= > setData(res))
            .catch(err= > setError(err))
            .finally(() = > setLoading(false))
    }, [url])

    return [loading, data, error]
}

export default useAxios

Copy the code

In this way, data requests to the interface are not sent in an endless loop.

V. πŸ” Conclusion

In this article, we first looked at the basic concepts of hooks, then we focused on state and Effect hooks, and we briefly looked at several other hooks. Finally, we talked about the react-hooks contribution to component logic reuse and some things to be aware of.

That concludes my introduction to react-hooks! React-hooks: React-hooks: react-hooks: react-hooks

🐣 Egg One More Thing

Previous recommendation

React column here juejin.cn/column/7018…

(: ding

  • Pay attention to the public account Monday laboratory, the first time to pay attention to quality articles, more “offer to come” interview column for you to unlock ~
  • If you think this article is helpful to you, remember to leave a footprint jio then go yo ~~πŸ˜‰
  • That’s all for this article! See you next time! πŸ‘‹ πŸ‘‹ πŸ‘‹