The title may not be very good, but this article is not a tutorial, and it will not cover too many points. It is recommended that those with more time should read the official website documentation in its entirety.

React Hooks may be foreign to some, but that didn’t stop them from becoming the “biggest” word in the React community right now.

First I learned it was Dan Abramov who tweeted it back in late October. It was a article Making Sense of React Hooks. React was supposed to be like this.

After reading this article, I hope you can have a general understanding of Hooks, and some understanding of their design philosophy. Please take your time and follow my thinking.

If you want to practice with this article, update react and react-dom to 16.7.0-alpha and above. If ESLint is configured, add the Plugin.

episode

For a long time, many people confuse the Stateless Component with the Functional Component. I will try to explain to them that it is not the same thing (not the same dimension). However, there was no use of state in Functional Component at that time, and I couldn’t explain it any better. Does this all portend something like React Hooks?

The essence of React Hooks

A more complex project would be riddled with React lifecycle functions (note that even if you use the state management library, you can’t avoid this), with almost every lifecycle taking on a part of some business logic, or a business logic that is scattered throughout each lifecycle.

The essence of what Hooks are doing is turning this life-cycle oriented programming into business logic oriented programming, so you don’t have to care about life cycles that you shouldn’t care about.

An evolution of Hooks

Let’s first imagine a common requirement, a Modal needs to display some information, which needs to be obtained through the API and is related to Modal strong business, asking us:

  • Because of the simplicity of the business, no additional state management libraries were introduced
  • Because the business is highly relevant, you don’t want to separate data from components
  • API data changes randomly, so you need to open Modal every time to get the latest data
  • No additional components can be created and destroyed for later optimization

Our possible implementations are as follows:

Code integrity demo address

class RandomUserModal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      user: {},
      loading: false};this.fetchData = this.fetchData.bind(this);
  }

  componentDidMount() {
    if (this.props.visible) {
      this.fetchData();
    }
  }

  componentDidUpdate(prevProps) {
    if(! prevProps.visible &&this.props.visible) {
      this.fetchData();
    }
  }

  fetchData() {
    this.setState({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(res= > res.json())
      .then(json= > this.setState({
        user: json.results[0].loading: false,})); } render() {const user = this.state.user;
    return (
      <ReactModal
        isOpen={this.props.visible}
      >
        <button onClick={this.props.handleCloseModal}>Close Modal</button>
        {this.state.loading ?
          <div>loading...</div>
          :
          <ul>
            <li>Name: {`${(user.name || {}).first} ${(user.name || {}).last}`}</li>
            <li>Gender: {user.gender}</li>
            <li>Phone: {user.phone}</li>
          </ul>
        }
      </ReactModal>)}}Copy the code

We abstract a RandomUserModal that contains the business logic. The presentation of this Modal is controlled by the parent component, so the arguments visible and handleCloseModal are passed in (for Modal to close itself).

In order to achieve data acquisition when Modal is turned on, we need to implement data acquisition logic in both componentDidMount and componentDidUpdate life cycles, and some initialization operations in Constructor are also necessary.

In fact, our requirements are simple: get new information through the API when appropriate. This is the business logic we abstracted. In order for this business logic to work correctly in React, we need to disassemble it according to the React component lifecycle. In addition to code redundancy, this disassembly is difficult to reuse.

Let’s see what happens with Hooks:

Full demo address

function RandomUserModal(props) {
  const [user, setUser] = React.useState({});
  const [loading, setLoading] = React.useState(false);

  React.useEffect((a)= > {
    if(! props.visible)return;
    setLoading(true);
    fetch('https://randomuser.me/api/').then(res= > res.json()).then(json= > {
      setUser(json.results[0]);
      setLoading(false);
    });
  }, [props.visible]);
  
  return (
    // The View section is almost the same as above
  );
}
Copy the code

It is obvious that we have changed the Class form to a Function form, using two State hooks for data management (analogous to constructor). What we did in the cDM and cDU life cycles was done directly in an Effect Hook (see here if there is a need to read or modify the DOM). The biggest advantage is that the code is streamlined, the business logic becomes more compact, and the number of lines of code is reduced from 50+ lines to 30+ lines.

That’s not the only thing that Hooks are powerful about. Most importantly, the business logic can be removed at will, just like normal functions (they just look the same), and become custom Hooks that can be reused. Specific can see the following further transformation:

Full demo address

// Custom Hook
function useFetchUser(visible) {
  const [user, setUser] = React.useState({});
  const [loading, setLoading] = React.useState(false);
  
  React.useEffect((a)= > {
    if(! visible)return;
    setLoading(true);
    fetch('https://randomuser.me/api/').then(res= > res.json()).then(json= > {
      setUser(json.results[0]);
      setLoading(false);
    });
  }, [visible]);
  return { user, loading };
}

function RandomUserModal(props) {
  const { user, loading } = useFetchUser(props.visible);
  
  return (
    // Same as above
  );
}
Copy the code

UseFetchUser is a custom Hook. It has the same status as useState, etc. You can use it in other components, even if you use it twice in this component, they will naturally separate.

Business logic reuse

The reuse of business logic here mainly refers to business logic that needs to cross the lifecycle. Simply organizing code in a stack of components can serve a variety of reuse purposes, but can lead to complex components and messy data flows. Component stacking is good for UI layout, but not for logical organization. In order to solve these problems, React produced many solutions during its development. The common ones in my knowledge are as follows:

Mixins

The disadvantages far outweigh the advantages, because now it is no longer supported, not to say more, you can look at this article: Mixins Considered Harmful.

Class Inheritance

It’s not officially recommended, and I don’t actually see anyone doing it.

High-Order Components (HOC)

React high-order components are a proven way to encapsulate business components. Their implementation treats itself as a function that takes a component and returns a component so that it can process some business logic and reuse it.

A common one is the connect function in react-redux:

(Image from here)

But it has also been teased by many for the nested problem:

(Image from here)

Render Props

Render Props are common, such as the React Context API:

class App extends React.Component {
  render() {
    return (
      <ThemeProvider>
        <ThemeContext.Consumer>
          {val => <div>{val}</div>}
        </ThemeContext.Consumer>
      </ThemeProvider>)}}Copy the code

The idea is simple: instead of putting “components” in a callback, the state of the child component can be retrieved from the current component and used.

However, this also creates Wrapper Hell issues:

(Image from here)

Hooks

In essence, the Hooks change lifecycle oriented programming to business logic oriented programming, and the optimizations are only in passing.

To do an analogy here, await/async is essentially changing async thinking from async thinking to async thinking in JS. The writing feature is that the original Callback Hell has been flat.

Summary and comparison:

  • await/asyncWith Callback Hell out of the way, asynchronous programming thinking becomes synchronous
  • Hooks kill Wrapper Hell, life oriented programming becomes business logic oriented programming

HOC and Render Props are necessary to support the React Class. They are not only for logic encapsulation, but also for logic + component encapsulation scenarios. But it’s a little bit wordy. In addition, I personally think the Wrapper Hell, the biggest problem mentioned above, can be basically solved by using Fragment.

State box

First, React Hooks’ design is counter-intuitive. Why? Try asking yourself: Why are Hooks only in other Hooks functions or React Function components?

As far as we know, the React community has been advocating the idea of Functional and pure functions. Functional Component is no longer pure after the introduction of Hooks. UseXxx is more of a statement than an execution statement. The box has inputs and outputs, and the rest of the internal implementation is ignorant. Importantly, the box has memory, and the next time it executes at this location, it has previous context information.

Think of the difference between “code” and “program”. The former is dead and the latter is alive. The expression c = a + b assigns the sum of a and b to c, but if we write c := a + b, we mean that c is the sum of a and b. It may seem like a similar statement, but in fact, the latter hides a temporal dimension, which represents a connection, not just an operation. This is used heavily in libraries such as RxJS.

This declaration is currently identified by the weak use prefix (but much simpler by design). In order not to mistake the relationship between each box and state, Hooks need to start with use and place it in the top-level scope, i.e. do not wrap if/switch/when/try etc. Don’t worry if you introduced the ESLint Plugin at the beginning of this article.

conclusion

This article may not have a very organized table of contents structure, but mostly some personal understanding and related thinking. Therefore, this is no substitute for looking at the actual documentation to learn more. If you’re still left with too much nonsense, I hope you can at least agree with the author on the following points:

  • Hooks essentially change lifecycle oriented programming to business logic oriented programming;
  • Hooks are logical state boxes, inputs and outputs represent connections.
  • Hooks are the future of React, but they don’t completely replace the original Class.

reference

  • reactjs.org

The article can be reproduced at will, but please keep this link to the original text. Please send your resume to caijun. HCJ (at)alibaba-inc.com.