Why Hook?

Hook is a new feature in React 16.8 that lets you use state and other React features (such as lifecycles) without writing a class.

First, think about the advantages of class components over functional components. More common are the following advantages:

  • A class component can define its own state, which is used to store its own internal state. Functional components do not, because new temporary variables are created each time a function is called;
  • Class components have their own life cycle and can complete their own logic in the corresponding life cycle; For example, a network request is sent in componentDidMount, and the lifecycle function is executed only once; Before hooks, sending a network request in a function meant resending the network request every time you re-render;
  • The class component can only re-execute the render function and the life cycle function (componentDidUpdate, etc.) when the state changes; When functional components are re-rendered, the whole function is executed, and there seems to be no place for them to be called only once;

So, before hooks, it was common to write class components for these cases.

Problems with the Class component

  • Complex components become difficult to understand:

When we first write a class component, the logic tends to be simple, not very complicated. But as our business grows, our class components become more complex; For example, componentDidMount may contain a lot of logical code, including network requests, some event listening (which needs to be removed in componentWillUnmount); Such classes are actually very difficult to break down: because their logic tends to get mixed up, forcing them to break up can lead to over-design and code complexity;

  • Hard to understand class

Many people find learning ES6 classes a barrier to learning React. For example, in class, we have to figure out who this refers to, so it takes a lot of effort to learn this. Although front-end developers must master this, it can still be cumbersome to deal with;

  • Component reuse state is difficult

In order to reuse some states, we need to use higher-order components or render props; Higher-level components like Connect in Redux or withRouter in React-Router are designed for state reuse; Or like providers and consumers that share some state, but when Consumer is used multiple times, there is a lot of nesting in the code. This code becomes very difficult to write and design;

The emergence of the Hook

The emergence of Hook can solve these problems mentioned above;

To sum up:

  • It allows us to use state and other React features without writing a class;
  • But we can extend this to a lot of uses, such as custom hooks;

Usage scenarios of Hook:

The advent of hooks has replaced almost all of our previous use of class components (except for some very unusual scenarios); But if it is an old project, there is no need to directly refactor all code into Hooks because it is fully backward compatible and can be used incrementally; Hooks can only be used in function components, not in class components, or outside of function components

Rules of the Hook

A Hook is essentially a JavaScript function, but there are two rules to follow when using it.

Use hooks only at the top level

Never call hooks in loops, conditions, or nested functions. Make sure you always call them at the top of your React function. Following this rule ensures that hooks are called in the same order on every render. This allows React to keep the hook state correct between multiple useState and useEffect calls.

Only the React function calls the Hook

Never call a Hook in a normal JavaScript function

Contrast the Class component with the Functional component

Use a counter example to compare class components with functional components that use hooks:

The code above is very different: functional components with hooks make the whole code very concise and never have to worry about this again

useState

The API for State Hook is useState. UseState helps us define a State variable. UseState is a new method that provides exactly the same functionality as this. State in the class. Normally, variables “disappear” after the function exits, while variables in state are retained by React.

UseState takes a single parameter that is used as the initialization value the first time the component is called. (If no argument is passed, the initialization value is undefined). UseState is an array, which can be easily assigned by deconstructing the array.

Return an array with a default state and a function to change the state by passing the useState argument. Change the original state value by passing a new state to the function. It is worth noting that useState does not help you handle state, as compared to setState non-override updates, useState override updates require the developer to handle the logic.

FAQ: Why useState and not createState?

  • “Create” may not be very accurate, since state is created only when the component is first rendered.
  • On the next re-render, useState returns us the current state.
  • If a new variable is created each time, it is no longer “state”.
  • This is one reason Hook’s name always starts with use.

Of course, we can also define multiple variables and complex variables (arrays, objects) in a component

Multiple state usage situations

Use of complex states

Effect Hook

Now that you’ve defined state in functional components via hooks, what about things like life cycles? Effect Hook allows you to perform some of the lifecycle functions of a class; In fact, there are some Side Effects of React updating the DOM, such as network requests, manually updating the DOM, and listening to some events. Therefore, hooks to complete these functions are called Effect hooks.

Now there is a requirement that the title of the page always display the number counter, using the class component and Hook respectively:

The class implementation

Hook to achieve

useEffect

  • The useEffect Hook tells React that something needs to be done after rendering.
  • UseEffect requires passing in a callback function, which is called back after React performs the DOM update operation.
  • By default, this callback is executed either after the first rendering or after every update;

Clear Effect

When writing a class component, some side effects need to be removed in componentWillUnmount: for example, the event bus or Redux manually calls SUBSCRIBE; Both require corresponding unsubscribe at componentWillUnmount;

  • In what way does Effect Hook simulate componentWillUnmount?

UseEffect The callback function A passed in can have A return value of its own, which is another callback function B:

type EffectCallback = () => (void | (() => void | undefined))

  • Why return a function in effect?

This is the optional clearing mechanism for Effect. Each effect can return a cleanup function; This keeps the logic for adding and removing subscriptions together; They’re all part of effect;

  • React when to remove an effect?

React performs cleanup operations when components are updated and uninstalled; Effect is executed every time it is rendered;

Using multiple Effects

One of the purposes of using hooks is to solve the problem of classes where the lifecycle often throws a lot of logic together: network requests, event listening, and manual DOM modification, all of which are often bundled into componentDidMount; Using Effect Hooks, you can separate them into different useeffects: Hooks allow us to separate them according to the purpose of the code, rather than like lifecycle functions: React calls each Effect in the component in the order in which it is declared

Effect performance optimization

By default, the useEffect callback is reexecuted on each render, but this causes two problems:

Some code just wants to be executed once, similar to what componentDidMount and componentWillUnmount do; In addition, multiple executions can cause performance problems;

How do you decide when useEffect should and should not be executed? UseEffect actually takes two parameters:

  • Argument 1: the callback function to execute;
  • Parameter 2: The useEffect is executed again only when the state changes. (Influenced by whom)

However, if a function does not want to depend on any content, we can also pass an empty array [] :

So the two callback functions correspond to componentDidMount and componentWillUnmount life cycle functions

useContext

In previous development, we used shared Context in components in two ways:

  • ContextType = MyContext; contextType = MyContext;
  • Multiple contexts or shared contexts in functional components via myContext.consumer;

But there is a lot of nesting in the way multiple contexts are shared:

Context Hook allows us to retrieve the value of a Context directly through a Hook;

Matters needing attention:

When the most recent <MyContext.Provider> update is made to the component’s upper layer, the Hook triggers a re-rendering and uses the latest context value passed to MyContext Provider

useCallback

In class components, we often make the following mistakes:

What’s the harm in writing that? Any time the props or state of the App component changes, it triggers a rerender, even if it is unrelated to the SomeComponent component, because each render generates a new style and doSomething. Style and doSomething refer to different references), so SomeComponent is re-rendered. If SomeComponent is a large component tree, this Virtual Dom comparison is obviously wasteful. The solution is also very simple, to extract the parameters into variables.

In class components, we can also store functions through the this object, whereas in function components there is no way to mount them. So the function component will re-render its child component every time it renders if there is a transfer function.

Function components re-render child components each time they are rendered if there is a transfer function. With useCallback, however, you can obtain a memorized function through useCallback.

The second argument is passed to an array, and for each item in the array that changes in value or reference, useCallback returns a new memory function for later rendering. As long as the child component inherits PureComponent or uses React. Memo, unnecessary VDOM rendering can be avoided.

useMemo 

The functionality of useCallback can be completely replaced by useMemo if you want to return a memory function using useMemo. The actual purpose of useMemo is also to optimize performance.

So the previous example using useCallback can be overwritten using useMemo:

The only difference is that **useCallback does not execute the first argument function, but returns it to you, whereas useMemo executes the first function and returns the result of its execution to you. ** So in the previous example, you can return handleClick for the purpose of storing the function. So useCallback usually remembers the event function, generates the remembered event function and passes it to the child component for use. UseMemo, on the other hand, is more suitable for calculating a definite value by functions, such as memory components.

Child1 /child2 is rerendered when a/b changes. As you can see from the example, child component updates are triggered only if the value of the second parameter array changes.

Customize the Hook

A custom Hook is essentially an extraction of function code logic. Strictly speaking, it is not a React feature. When we want to share logic between two functions, we extract it into a third function. Components and hooks are functions, so the same applies.

A custom Hook is a function whose name starts with “use” that can call other hooks from within.

Unlike the React component, custom hooks do not need to have a special identity. We are free to decide what its arguments are and what it should return (if necessary).