Basic use of React-hooks

React Hooks Basics

  • Introduction to React Hooks
  • React Hooks Basics

Introduction to React Hooks

  1. What are Hooks?
  2. Why Hooks

What are Hooks

  • Hooks: hook, hook, hook
  • Hooks are new in React v16.8
  • Role: Provide Function Component with React functions provided in original class components such as state and life cycle
    • It can be understood as hooking into the features of class components for function components through Hooks
  • Note: Hooks can only be used in function components, since then, function components have become the new darling of React

Comparison of component development models before and after React v16.8:

  • Before React v16.8: class component (provide state) + function component (display content)
  • React v16.8 and later:
    1. class component (provide state) + function component (display content)
    2. Hooks (provide status) + function components (display content)
    3. Mix the above two methods: use class components for some functions, and use Hooks + function components for some functions

Note 1: Although there are Hooks, React officials have no plans to remove classes from the React library.

Note 2: With Hooks, function components can no longer be called stateless components, because Hooks provide state for function components.

Why Hooks

Two perspectives: 1. State logic reuse of components 2. Problems with class components themselves

  1. Component state logic multiplexing:

    • Before Hooks, the state logic reuse of components has gone through: mixins (mix-in), HOCs (higher-order components), render-props and other modes.
    • (Abandoned long ago) Problems with mixins: 1. The data source is unclear 2. Naming conflicts.
    • The problem of HOCs and render-props: refactoring the component structure, causing the component to form a JSX nesting hell problem.
  2. The problem of the class component itself:

    • Choice: the difference between function components and class components and which one is more appropriate to use
    • Need to understand how this in class works
    • Interrelated code that needs to be modified against each other is split into different lifecycle functions
      • componentDidMount -> window.addEventListener(‘resize’, this.fn)
      • componentWillUnmount -> window.addEventListener(‘resize’, this.fn)
  • Compared with function components, it is not conducive to code compression and optimization, and is not conducive to TS type deduction

It is precisely because of the original problems of React that Hooks are used to solve these problems

Advantages of hooks

Due to the problems in the original React, React needs a better built-in mechanism to realize the reuse of component state logic.

  1. Hooks can only be used in function components, avoiding the problem of class components
  2. Reuse component state logic without changing component hierarchy
  3. Enforce code splitting based on functionality rather than lifecycle methods
  4. Apart from the concept given by React, Hooks are some ordinary functions
  5. Has better TS type deduction
  6. tree- – shaking friendly, remove unreferenced code when packaging
  7. better compression

In project development, the adoption strategy of Hooks:

  • It is not recommended to use Hooks directly to refactor existing components on a large scale
  • Recommendation: Use Hooks for new functions, if complex functions cannot be realized, you can continue to use class
  • Find a component with simple functions and non-core functions to start using hooks

React knowledge learned earlier is useful

APIs related to class components are no longer used, such as:

  • class Hello extends Component
  • componentDidMount, componentDidUpdate, componentWillUnmount
  • this related usage

The original learning content still needs to be used, such as:

  • JSX: {}, onClick={handleClick}, conditional rendering, list rendering, style processing, etc.
  • Components: function components, component communication
  • routing
  • React development philosophy: one-way data flow, state promotion, etc.
  • Problem-solving ideas, skills, analysis of common mistakes, etc.

useState Hook

Overview

Question: What is a Hook? A Hook is a special function that allows you to get React features such as state in function components
Usage pattern: function component + Hooks
Features: From the name point of view, Hooks all start with use

Basic use of useState Hook

  • Usage scenario: When you want to use component state in a function component, you need to use useState Hook
  • Role: Provide state for function components
  • Steps for usage:
    1. Import the useState function
    2. Call the useState function and pass in the initial value of the state
    3. From the return value of the useState function, the function to get the state and modify the state
    4. Display state in JSX
    5. Call the function that modifies the state in the click event of the button to update the state
import {<!-- --> useState } from 'react'

const Count = () => {<!-- -->
  // return value is an array
  const stateArray = useState(0)

  // status value -> 0
  const state = stateArray[0]
  // function to modify state
  const setState = stateArray[1]

  return (
    <div>
      {<!-- -->/* display state value */}
      <h1>useState Hook -> {<!-- -->state}</h1>
      {<!-- -->/* Click the button to make the state value + 1 */}
      <button onClick={<!-- -->() => setState(state + 1)}> + 1</button>
    </div>
  )
}
  • Parameters: State initial value. For example, passing in 0 means that the initial value of the state is 0
    • Note: The state here can be any value (for example, value, string, etc.), and the state in the class component must be an object
  • Return value: array, containing two values: 1 state value (state) 2 function to modify the state (setState)

Simplify with array destructuring

For example, to get the elements of an array:

  1. Raw way: indexed access
const arr = ['aaa', 'bbb']

const a = arr[0] // get the element with index 0
const b = arr[1] // get the element with index 1
  1. Simplified way: array destructuring
    • It is equivalent to creating two variables (which can be any variable name) to obtain the array elements of the corresponding indexes respectively
const arr = ['aaa', 'bbb']

const [a, b] = arr
// a => arr[0]
// b => arr[1]

const [state, setState] = arr
  • Use array destructuring to simplify the use of useState
    • Convention: The name of the function that modifies the state starts with set, followed by the name of the state
// The destructured name can be any name

const [state, setState] = useState(0)
const [age, setAge] = useState(0)
const [count, setCount] = useState(0)

Read and modify status

Use of state: 1 read state 2 modify state

  1. Read status: The status provided by this method is a local variable inside the function, which can be used anywhere in the function

  2. Modify status:

  • setCount(newValue) is a function, the parameter means: new state value
  • After calling this function, the new state valuereplaces the old value
  • After modifying the state, the component will re-render because the state has changed

Component update process

The execution process of the function component after using the useState hook, and the change of the state value:

  • Component renders for the first time:

    1. Execute the code logic in this component from scratch
    2. Call useState(0) to use the incoming parameter as the initial value of the state, namely: 0
    3. Render the component. At this time, the obtained state count value is: 0
  • The second rendering of the component:

    1. Click the button, call setCount(count + 1) to modify the state, because the state changes, so the component will re-render
    2. When the component re-renders, the code logic in the component will be executed again
    3. Call useState(0) again, at this time React will get the latest state value instead of the initial value, for example, the latest state value in this case is 1
    4. Render the component again. At this time, the obtained state count value is: 1

Note: The initial value (parameter) of useState will only take effect when the component is rendered for the first time.

That is to say, for each rendering in the future, useState will get the latest state value. React components remember the latest state value each time!

Add multiple states for function components

Question: What if a function component requires multiple states?
Answer: You can call useState Hook multiple times, and each call of useState Hook can provide a state.
Note: The [state, setState] returned by multiple calls of useState Hook do not affect each other.

Rules for using hooks

Note: React Hooks can only appear directly in function components, and cannot be nested in if/for/other functions!

Otherwise, an error will be reported: React Hook “useState” is called conditionally. React Hooks must be called in the exact same order in every component render

React’s useState Hook is called conditionally (in a conditional judgment).

React Hooks must call all Hooks in the same order each time the component renders.

  • Why is there such a rule? Because React identifies each Hook in the order in which it is called, if the order of each call is different, React cannot know which Hook it is
  • It can be viewed through developer tools.

useEffect Hook

  1. side effect – side effect
  2. Basic use of useEffect
  3. dependencies of useEffect
  4. useEffect sends the request

side effect – side effect

Usage scenario: When you want to handle side effects in function components, you need to use useEffect Hook
Role: Handling side effects in function components

Question: What are the side effects?
Answer: In computer science, a function or other operation is said to have side effects if it modifies the value of a state variable outside its local environment
By analogy, for 999 Ganmaoling cold medicine:

  • (Main) Function: For headache, fever, nasal congestion, runny nose, sore throat, etc. caused by colds
  • Side effects: Visible drowsiness, lethargy, thirst, weakness

Understanding: Side effects are relative to the main function. A function (for example, a function) other than the main function is a side effect
For React components, The main function is to render the UI according to the data (state/props), other than that are side effects (for example, manually modifying the DOM)

Formula for React components: UI = f(state)

Common side effects

  • Data (Ajax) requests, manual modification of DOM, localStorage operations, etc.

Basic use of useEffect

Usage scenario: When you want to handle side effects in function components, use useEffect Hook
Role: Handle side effects in function components (side effect)
Note: In actual development, side effects are unavoidable. Therefore, react specifically provides useEffect Hook to handle side effects in function components

import {<!-- --> useEffect } from 'react'

useEffect(function effect() {<!-- -->
  document.title = `Currently clicked ${<!-- -->count} times`
})

useEffect(() => {<!-- -->
  document.title = `Currently clicked ${<!-- -->count} times`
})

explain:

  • Parameters: callback function (called effect), that is, write side effect code in this function
  • Execution timing: The effect will be executed after the component is rendered and after the component is updated
  • Equivalent to componentDidMount + componentDidUpdate

dependency of useEffect

  • Problem: If there is another state in the component, when the other state is updated, the effect callback just now will also be executed
  • Performance optimization: Skip unnecessary execution, only execute the corresponding effect when the count changes
useEffect(() => {<!-- -->
  document.title = `Currently clicked ${<!-- -->count} times`
}, [count])

explain:

  • The second parameter: optional, can be omitted; you can also pass an array, and the elements in the array can become dependencies (deps)
  • This example means: the effect will be re-executed only when the count changes

The dependency of useEffect is an empty array

The second parameter of useEffect can also be an empty array ([]), indicating that the effect is only executed after the component is rendered for the first time
Usage scenario: 1 event binding 2 send request to get data, etc.

useEffect(() => {<!-- -->
  const handleResize = () => {<!-- -->}
  window. addEventListener('resize', handleResize)
}, [])

explain:

  • The effect will only be executed after the component is rendered for the first time, so you can perform operations such as event binding that only need to be executed once
    • At this point, it is equivalent to the function of the componentDidMount hook function of the class component
  • Like useState Hook, useEffect Hook can be called multiple times in a component
  • Recommendation: one useEffect only handles one function, when there are multiple functions, use multiple useEffect

Summary of the use of useEffect

//Trigger timing: 1 the first rendering will be executed 2 every time the component re-renders will be executed again
// componentDidMount + ComponentDidUpdate
useEffect(() => {<!-- -->})

// componentDidMount
// Trigger timing: only executed when the component is rendered for the first time
useEffect(() => {<!-- -->}, [])

// componentDidMount + componentDidUpdate (judgment)
// Trigger timing: 1 will be executed for the first rendering 2 will be executed again when the count changes
useEffect(() => {<!-- -->}, [count])

Don’t lie about useEffect’s dependencies

const App = () => {
  const [count, setCount] = useState(0)
  useEffect(() => {
    document.title = 'clicked' + count + 'times'
  }, [])
  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => setCount(count + 1)}> + 1</button>
      <hr />
    </div>
  )
}

UseEffect complete guide: https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/

useEffect cleans up side effects

Sometimes, we just want to run some extra code after React has updated the DOM. **Such as sending network requests, manually changing DOM, and recording logs, these are common operations that do not need to be cleared.

There are still some side effects that need to be cleared. For example, subscribe to external data sources, start timers, and register events. In this case, cleaning work is very important to prevent memory leaks!

Question: How to unbind the event when the component is uninstalled? At this point, the return value of effect is used

useEffect(() => {<!-- -->
  const handleResize = () => {<!-- -->}
  window. addEventListener('resize', handleResize)
  return () => window. removeEventListener('resize', handleResize)
}, [])

explain:

  • The return value of effect is also optional and can be omitted. It can also return a cleanup function to perform cleanup operations such as event unbinding
  • Execution timing of the cleanup function: 1 when the component is unloaded 2 before the effect is re-executed
    • At this point, it is equivalent to the function of the componentWillUnmount hook function of the class component
  • Recommendation: one useEffect only handles one function, when there are multiple functions, use multiple useEffect
  • Advantages: split according to business logic, put business logic of the same function together instead of splitting code according to life cycle method name
  • When writing code, focus; instead of scrolling up and down to view code

Place event handlers inside useEffect

// 1 Put the resize event handler in the effect callback, the current code is no problem
useEffect(() => {<!-- -->
  const handleResize = () => {<!-- -->
    console.log('window window size changed')
  }
  window. addEventListener('resize', handleResize)

  return () => {<!-- -->
    window. removeEventListener('resize', handleResize)
  }
}, [])

// 2 Get the resize event handler outside useEffect, the current code is no problem
const handleResize = () => {<!-- -->
  console.log('window window size changed')
}

useEffect(() => {<!-- -->
  window. addEventListener('resize', handleResize)

  return () => {<!-- -->
    window. removeEventListener('resize', handleResize)
  }
}, [])

// 3 cases with dependencies:
useEffect(() => {<!-- -->
  // handler for the resize event
  const handleResize = () => {<!-- -->
    console.log('window window size changed', count)
  }

  window. addEventListener('resize', handleResize)

  return () => {<!-- -->
    window. removeEventListener('resize', handleResize)
  }
}, [count])


// Note: the code here will give some warnings! ! ! Don't write code this way! ! !
// 4 If handleResize is placed outside useEffect, React will warn:
// Either put handleResize in useEffect
// Either use the useCallback hook to wrap handleResize
// handler for the resize event
const handleResize = () => {<!-- -->
  console.log('window window size changed', count)
}
useEffect(() => {<!-- -->
  console.log('useeffect executed')
  window. addEventListener('resize', handleResize)

  return () => {<!-- -->
    window. removeEventListener('resize', handleResize)
  }
}, [handleResize])

// To sum up the above situations, it is recommended: when binding events to window, put the event handler inside useEffect.

useEffect send request

In the component, use the useEffect Hook to send a request to get data (side effect):

useEffect(() => {<!-- -->
  const loadData = async () => {<!-- -->}
  loadData()
}, [])

explain:

  • Note: effect can only be a synchronous function, async cannot be used
  • Because the return value of the effect should be a cleanup function, React will re-execute it when the component is unmounted or the dependency of the effect changes
  • But if the effect is async, the return value is a Promise object. In this case, there is no guarantee that the cleanup function will be called immediately
  • If the cleanup function is called lazily, there is no chance to ignore stale request results or cancel the request
  • In order to use async/await syntax, you can create an async function inside effect and call
// error demonstration:

// don't add async to effect
useEffect(async () => {<!-- -->}, [])
// https://github.com/facebook/react/issues/14326#issuecomment-441680293

useEffect(() => {<!-- -->
  // Whether to cancel this request
  let didCancel = false

  async function fetchMyAPI() {<!-- -->
    let url = 'http://something/' + productId
    let config = {<!-- -->}
    const response = await myFetch(url)
    // If other requests are enabled, ignore this (outdated) request result
    if (!didCancel) {<!-- -->
      console. log(response)
    }
  }

  fetchMyAPI()
  return () => {<!-- --> didCancel = true } // cancel this request
}, [productId])

useEffect cycle error reporting problem

useEffect(() => {<!-- -->
  const getList = async () => {<!-- -->
    const res = await axios.get('http://geek.itheima.net/v1_0/channels')
    setList(res.data.data.channels)
  }
  console. log(list)
  getList()
}, [list])

Custom hooks

In addition to using the built-in Hooks, you can also create your own Hooks (custom Hooks).

Usage scenario:Extract component state logic into reusable functions (custom Hooks) to achieve state logic reuse.

Built-in Hooks endow function components with the functions of class components; on top of this, custom Hooks realize logic reuse of different states for different components.

  • Custom Hooks is a function. It is agreed that the function name must start with use. React judges whether it is a Hooks by whether the function name starts with use

  • Hooks can only be used in function components or other custom Hooks, otherwise, an error will be reported!

  • Custom Hooks are used to extract the state logic of components, which can have different parameters and return values according to different functions (just like using ordinary functions)