React-hooks Design Motivation and Working Mode (Part 2)

In previous installments, we explored the origins of react-hooks and understood the “design motivation” behind them. Let’s build on the overall understanding of React-hooks.

In the main part of the chapter, a series of code examples will be provided to help you understand useState and useEffect. This step is intended to help beginners learn to use react-hooks quickly. On this basis, we will re-understand the question “Why React-hooks”. In the end, recognize and explore the limitations of Hooks.

[Note] : In the process of learning this class, I hope to get rid of the wrong learning concept of “the more API names you know, the better”. If want to grasp the usage of the Hook as much as possible, here can be a key to enter the React – ocean zh-hans.reactjs.org/docs/hooks- Hooks API… The introduction to API usage is a “teaching aid”, only to pave the way for further knowledge.

1. Lead knowledge: The basic form of Hooks from the core API

1), useState() : introduces state for function components

Compared with class components, the early functional components lacked the ability to define and maintain state, and state, as the soul of the React component, must not be omitted. So react-hooks made support for state a priority from the start. UseState is just such an API that can introduce state to functional components.

  • Function components, really light

In the past, you might have had to write a class component to use state (here’s a Demo, coded as follows) :

import React, { Component } from "react"; export default class TextButton extends Component { constructor() { super(); This. state = {text: "initial text"}; } changeText = () => {this.setState(() => {return {text: "modified text"}; }); }; render() { const { text } = this.state; Return (<div className="textButton"> <p>{text}</p> <button onClick={this.changeText}> click to modify text </button> </div>); }}Copy the code

With useState, we can introduce state directly into the function component. Here is the TextButton component modified with useState:

import React, { useState } from "react"; Export default function Button() {const [text, setText] = useState(" initial text "); Function changeText() {return setText(" modified text "); } return (<div className="textButton"> <p>{text}</p> <button onClick={changeText}> click to modify text </button> </div>); }Copy the code

The above two sets of code achieve exactly the same interface interaction, and the amount of code in the function component is almost half that in the class component!

If you were more or less puzzled by the idea that class components are too heavy in the last article, this Demo will give you a real sense of the difference in complexity between the two types of components – function components with the same logic are much less complex than class components.

  • UseState get started quickly

In terms of usage, useState returns an array with the first element corresponding to the desired state variable and the second element corresponding to the API that can modify that variable. We can use the syntax of array deconstruction to take these two elements out and name them whatever we want. An example of a typical call is as follows:

const [state, setState] = useState(initialState);
Copy the code

In this example, we name our desired state variable state and the API that modifies state setState. The initialState passed in useState is the initial value of state. You can then change the value of state by calling setState, like this:

setState(newState)
Copy the code

State updates trigger updates at the render level, as with class components.

One important point to emphasize for beginners is that both the state and the API names that modify the state are customizable. For example, in the previous Demo, we defined them as text and setText, respectively:

Const [text, setText] = useState(" initial text ");Copy the code

The naming of “set + concrete variable name” helps us to quickly establish a logical connection between the API and its corresponding state.

When we call react. useState in a function component, we’re actually attaching a state to the component — a state, not a batch of states. This is relative to state in the class component. In a class component, the state we define is usually an object like this:

This. State {text: "xiuyan", Length: 10000, author: [" Xiuyan ", "Cuicui ", "yisi"]}Copy the code

This object is “all-inclusive” : the state of the entire component is converged within the state object, and when a specific state is needed, it is read in the form of access object properties such as this.state. XXX.

In the context of the useState hook, state is a single state that can be any JS type you need. Like this:

// Const [author, setAuthor] = useState(["xiuyan", "cuicui", "yisi"]); Const [length, setLength] = useState(100); // Const [length, setLength] = useState(100); Const [text, setText] = useState(" initial text ") const [text, setText] = useState(" initial text ")Copy the code

You can also define booleans, objects, etc., which is fine. Like a property of the state object in a class component, it corresponds to a separate state and allows you to store any type of value.

UseEffect () : allows function components to perform side effects

The most significant difference between functional components and class components is the absence of state and life cycle. UseState introduces state for function components, and useEffect makes up for the absence of life cycles to some extent.

UseEffect can introduce side effects to function components. UseEffect (componentDidMount, componentDidUpdate, and componentWillUnmount) Such as manipulating DOM, subscribing to events, calling external apis to get data, and so on.

(1) The “replacement” relationship between useEffect and life cycle function

We can understand the substitution relationship between useEffect and lifecycle functions by using the following example. Here is an example of a function component written with useEffect:

Import React, {useState, useEffect} from 'React '; Function IncreasingTodoList() {const [count, setCount] = useState(0); UseEffect (() => {componentDidMount () => { Const todoList = document.getelementById ("todoList"); const newItem = document.createElement("li"); Newitem. innerHTML = 'I'm the first ${count} to-do item'; todoList.append(newItem); }); <div> <p> <ul id="todoList"></ul> <button onClick={() => setCount(count) </button> </div>); }Copy the code

The interface constructed from the above code should look like the following when it is mounted:

IncreasingTodoList is a ToDoList that can only add items. According to useEffect, every time we click on the “I add a to-do item” button, the DOM structure will be appended with a Li element when we drive count+1. Here’s what it looks like when you hit the button three times:

The same effect can be achieved by writing a class component, as indicated in the comments:

import React from 'react'; Class IncreasingTodoList extends React.Component { UseEffect = componentDidMount() {this.addTodoItem()} useEffect = componentDidMount() {this.addTodoItem()} useEffect = componentDidMount() {this.addTodoItem() ComponentDidUpdate () {this.addTodoItem()} AddTodoItem = () => {const {count} = this.state const todoList = document.getelementById ("todoList") const NewItem = document.createElement("li") newitem.innerhtml = 'TOdolist.append (newItem)}' // define the render content Render () {const {count} = this.state return (<div> <p> <ul id="todoList"></ul> <button) OnClick ={() => this.setState({count: this.state.count + 1,})} > </button> </div>)Copy the code

By this comparison, the conversion relationship between the class component lifecycle and the function component useEffect is immediately apparent.

Here, a word of caution: when you are new to useEffect, it is inevitable to derive your understanding of useEffect from an understanding of the lifecycle. But in the long run, sticking to this learning path will prevent you from truly embracing react-hooks on a mental model level.

Sometimes we must learn to forget the old in order to embrace the new. The transition relationship between lifecycle and useEffect is not the most important for anyone learning useEffect, The most important thing is to build a “component has side effects → introduce useEffect” reflex in your mind — once you really get rid of the stereotype of class components and embrace functional programming, you’ll be more likely to agree with the definition of useEffect as a hook for introducing side effects into a function component.

(2) useEffect

UseEffect can take two arguments, a callback function and a dependent array, as shown in the following code:

useEffect(callBack, [])
Copy the code

How useEffect is invoked essentially depends on what effect you want to achieve with it. The following is a brief introduction to useEffect invocation rules.

  • Side effect of executing after every render: passing in a callback function, not passing in a dependent array.

The call form looks like this:

useEffect(callBack)
Copy the code
  • Side effects of executing only once in mount phase: passing in a callback function whose return value is not a function, and passing in an empty array. The call form looks like this:
UseEffect (()=>{// here is business logic}, [])Copy the code
  • Side effects of executing only in mount and unload phases: passing in a callback function whose return value is a function, along with an empty array.

If the callback function itself is called A and the returned function is called B, then A is executed during the mount phase and B is executed during the unload phase. The call form looks like this:

UseEffect (() = > {/ / here is the business logic of A / / return A function to return B () = > {}}, [])Copy the code

Note that the reason why this method triggers the logic of function B in the uninstall phase is determined by the useEffect execution rule: The function returned in the useEffect callback is called the “cleanup function”. When React recognizes the cleanup function, the internal logic of the cleanup function is executed during the uninstall. This rule is not affected by the second argument or other factors, as long as you return a function in the useEffect callback, it will be treated as a cleanup function.

  • The side effect of passing in a callback function that returns a function and does not pass in a second argument. As follows:
UseEffect (()=>{// here is the business logic of A // return A function named B return ()=>{}})Copy the code

React triggers logic A every time it renders and logic B during the uninstall phase.

Just remember that if you have a piece of side effect logic that needs to be executed during the uninstall phase, just put it in the useEffect callback’s return function (function B in the example above). It can also be argued that the role of the B function is similar to the logic in the Life Cycle componentWillUnmount method (although it is not recommended to continue drilling the life cycle).

  • Pass in a callback function (if the return value is a function, it still only affects the handling of the side effects during the unload phase, which won’t be described here) and pass in a non-empty array, as follows:
UseEffect (()=>{return XXX}, [num1, num2, num3])Copy the code

A schematic array given here is [num1, num2, num3]. First of all, variables in an array are usually data from the component itself (props or state). If the array is not empty, React compares the two renderers after a new render to see if any variables in the array have been updated (if one element of the array has changed, the update is considered to have occurred) and triggers the useEffect side effect logic if an update has occurred.

React-hooks: How do Hooks help us update working modes

Function components have a number of features that make React components better than class components. React-hooks are designed to improve the capabilities of function components. Now, based on the react-hooks code level, the understanding of “motivation” has moved on. React-hooks: React-hooks: React-hooks: React-hooks: React-hooks: React-hooks

As many of you with a keen nose have already noticed, here are some tips on why you Need React-hooks. Of course, there is no standard answer to a question that begins with “Why XXX”, but there are some key “points”, and as long as you can answer the key “points”, you can prove that you are thinking in the right direction, which means that this question will earn you points. Here are four ideas:

  • (1) Say goodbye to the incomprehensible Class;

  • (2) Solve the problem that business logic is difficult to split;

  • (3) Make state logic reuse simple and feasible;

  • (3) Function components are more in line with React concept in terms of design.

About the idea (4), the last chapter has been relatively transparent, then by the east wind of the code, spread out the first three points.

(1) Say goodbye to the difficult Class: Grasp the two “pain points” of Class

The word on the street is that Class is “hard to understand,” and the pain points of this and lifecycle lie behind this.

  • Let’s start with this. In the last chapter you can get a sense of how elusive this can be. But that’s a relatively special scenario, and we’re probably more familiar with this in the React custom component method. Take a look at the following code:
Class Example extends Component {state = {name: 'unknown ', age: '99'; }; ChangeAge () {this.setState({age: '100'}); } render() {return <button onClick={this.state.name} >{this.state.name} </button>}}Copy the code

Regardless of the logic of the component, look at the changeAge method: it is an event listener for the Button button. When the button button is clicked, you expect it to modify the state, but in fact, the program will report an error after the click occurs. The reason is simple: You can’t get the this of a component instance in changeAge. I’ll explain why in a future article, but I’ll skip over that for now. This phenomenon should be familiar to anyone with React development experience.

In order to solve the problem of this not meeting the expectation, the front end is also different, the former use of bind, now promote the arrow function. But whatever it is, it is essentially a practical constraint to solve a design-level problem. Now that we have Hooks that do not care about this, we can set ourselves free in function components!

  • As for the life cycle, it causes problems in two main ways:

① Learning cost;

② Unreasonable logical planning method.

The first one is something you’ll understand if you’ve studied the life cycle. Here’s how this “illogical planning” is fixed by Hooks.

(2).hooks how to implement better logic split

How have you organized your business logic in the past? In most cases it would be a matter of figuring out what the business needs are and then splitting the business logic into different lifecycle functions — yes, the logic was once coupled to the lifecycle.

With that in mind, lifecycle functions often do weird things like fetching data in componentDidMount, updating the DOM in componentDidUpdate, etc. It might be acceptable to say that you only do one thing in one lifecycle, but React projects tend to do more than one thing in one lifecycle. The following pseudocode is a good example of this:

ComponentDidMount () {// 1. Here we get some data from props and update DOM // 3 based on this data. Set a subscription // 4. Do something else here //... } componentWillUnMount() {componentDidUpdate() {// 1. Here we update DOM with asynchronous data from DidMount // 2. Here we get some data from props and update the DOM based on this data (as in DidMount step 2)}Copy the code

Life cycle functions like this are too big and do too complicated things, causing a lot of trouble for readers and maintainers. Most importantly, there seems to be no connection between these events, and the logic seems to have been “broken” into the life cycle. For example, the logic for setting up subscriptions and uninstalling subscriptions, although logically strongly related, can only be handled in separate lifecycle functions, which is not a very sound design by any means.

With the help of Hooks, it is possible to logically separate these complex operations into separate functions: a component for managing subscriptions, a component for handling DOM, a component for retrieving data, etc. Hooks help us aggregate business logic and avoid complex components and redundant code.

(3). State reuse: Hooks make complex problems simple

In the past, the reuse of state logic relied on HOC and Render Props for component design patterns, because React didn’t provide a way to do it at the native level. However, these design patterns are not a panacea. While they enable logical reuse, they also destroy the structure of components. One of the most common problems is the phenomenon of “nested hell”.

Hooks can be seen as a native approach to reusing state logic provided by React. Now we can customize hooks to achieve logical reuse without breaking the component structure.

To understand the above two paragraphs, you need a basic understanding and application of component design patterns. Don’t panic if you feel confused. For the problem of component state reuse (including HOC, Render Props, and custom hooks), the current expectation can be “just know it happens.” If you’re in a hurry, you can take a quick look at the documentation. There will be related column modules to put this knowledge in a more appropriate context.

Keep your head: Hooks are not everything

Despite the Hooks’ merits, and the React team’s clear positive attitude toward functional components, there is a basic cognitive lesson to keep in mind: there are no absolutes and there are always two sides to everything. React only promotes function components. It doesn’t “step on” class components, and even states that class and function components will continue to coexist. This is a reminder that, while recognizing the benefits of Hooks, we also need to recognize their limitations.

With regard to the limitations of Hooks, here are some lessons from practice:

  • Hooks do not yet fully complement class components for function components: getSnapshotBeforeUpdate, componentDidCatch and other life cycles are currently heavily dependent on class components. The official Flag is that they will be added as soon as possible, but for now, it remains a Flag.

  • Lightness is almost in the DNA of a functional component, which can make it less able to digest complexity: we sometimes see instances in class components where methods are so numerous that breaking down and organizing business logic can be a challenge if functional components are used to solve the same problem. This leaves one vacillating between “too complex” and “too unbundled”. In fact, the boundary between coupling and cohesion can sometimes be difficult to grasp, and functional components give us a degree of freedom, but also require a higher level of development.

  • Hooks have strict rules on the use level: this is the focus of the next section. It’s easy for today’s React developers to turn their React projects into car crashes if they don’t remember and implement the Hooks principles, if they don’t have a solid grasp of the Hooks’ key principles.

4, summarize

Based on the cognition of coding, this paper dialectically discusses the advantages and limitations brought by Hooks. Now that you have a good understanding of the basic form of react-hooks, and of their former lives and lives, you have learned the true benefits of Hooks. Learn from react-hooks. Learn from react-hooks. So, the next article will answer any remaining suspense, ask questions about the react-hooks implementation rules, and get into the real deep waters of the React-hooks knowledge link.

Learning the source (the article reprinted from) : kaiwu.lagou.com/course/cour…