Skip to main content

Introduction to React Hooks

Free2019-06-08#Front-End#react hooks target#react hooks motivation#React Hooks评价#React Hooks意义

What are Hooks? What are they used for?

I. Motivation

Under React's existing component model, there are many difficult problems to solve:

  • Difficult to reuse stateful logic across components

  • Component complexity makes them hard to understand

  • Many problems with Classes

  • ...

And Hooks, shoulder the mission to break the deadlock

Logic Reuse Between Components

Logic reuse between components has always been a problem. Render Props, Higher-Order Components and other common patterns patterns are all for separating cross-cutting concerns (Cross-cutting concern), reusing things like:

  • Logging

  • Caching/synchronization/persistence

  • Data validation

  • Error capture/exception handling

The purpose is to separate cross-cutting concerns from core business logic, so as to focus on business logic

P.S. For more information about aspects, concerns and other AOP concepts, see AOP(Aspect-Oriented Programming)

However, although HOC and Render Props can separate cross-cutting concerns in component form, they also bring some new problems:

  • Extensibility limitations

  • Ref passing problems

  • Wrapper Hell

The root cause of these problems is:

Fine-grained code reuse should not be bundled with component reuse

And there has always been a lack of a simple and direct way to extend component behavior:

React doesn't offer a way to "attach" reusable behavior to a component (for example, connecting it to a store).

The main purpose of proposing Hooks is to solve this problem:

React needs a better primitive for sharing stateful logic.

P.S. For more information about logic reuse methods between components, see [React Component Logic Reuse](/articles/react 组件间逻辑复用/)

Component Complexity Problem

As you can see, React components are becoming increasingly complex:

Provider, Consumer, Higher-order Component, Render Props
// with Redux
Action, Reducer, Container
// with Reselect
Selector
// with xxx ...

These many abstraction layers alleviate the organization and reuse problems of stateful logic, but the resulting problem is that component reuse costs are higher. It's no longer simply introducing a component to get complete business functionality

Admittedly, these subdivided abstraction layers can make code responsibilities clearer, but stateful logic is also scattered and difficult to reuse. Therefore, component reuse mostly stays at the View Component (View + UI State Logic) level, and cannot further reuse Business Component (View + Business State Logic). Unless all these business state logic (requesting data, subscribing to other data sources, timers, etc.) are gathered into a single component (for example, using mobxjs/mobx to manage state). Even so, it's impossible to avoid side effects mixed in components, and unrelated logic mixed in lifecycle methods. For example, componentDidMount contains data requests, event listeners, etc... We found that truly internally related code is split apart by lifecycle, and completely unrelated code ends up combined in a single method:

Mutually related code that changes together gets split apart, but completely unrelated code ends up combined in a single method.

Splitting logic according to component lifecycle makes component size expand rapidly, and such huge components are not easy to split into a bunch of small components, because stateful logic is everywhere, and it's difficult to test

Therefore, a more reasonable and easier to reuse stateful logic organization method is needed, so that internally related code stays together, rather than being forcibly split apart by lifecycle methods

P.S. For more information about MobX, see MobX

Class Problems

As a mold for objects, Class is a very important part of OOP. However, using Class to define components (combining view and logic) is not so ideal:

  • Makes code difficult to organize/reuse

  • Brings higher learning costs

  • Hinders compilation optimization

In terms of code organization/reuse, Class lifecycle methods are a typical example. A component can only have one specific lifecycle method, so some unrelated logic can only be put together, and this template-style division splits apart code with internal relationships, which is not conducive to code reuse

In terms of learning costs, it's mainly reflected in two points:

  • Need to understand this in JavaScript (different from other languages), and remember to bind(this)

  • Understand the differences between functional components and Class components, and their respective application scenarios

In terms of compilation optimization, Class makes some tool optimization effects greatly reduced. For example:

  • Component ahead-of-time compilation effects are not ideal (React team has already made some attempts in this area, finding that Class components are not conducive to compilation optimization)

  • Class is not conducive to code compression

  • Difficult to correctly hot reload

P.S. Component ahead-of-time compilation is similar to [GCC's advanced mode](/articles/react 背后的工具化体系/#articleHeader11), inlining optimizations for defaultProps constants, etc., and removing useless code

Therefore, hope to provide a set of compilation optimization-friendly APIs:

We want to present an API that makes it more likely for code to stay on the optimizable path.

So abandon Class, embrace functions:

Hooks let you use more of React's features without classes.

P.S. Not switching to functional programming, fully introducing FP concepts, but providing an "escape hatch" to imperative programming, without having to master functional programming, reactive programming and other technologies:

Hooks provide access to imperative escape hatches and don't require you to learn complex functional or reactive programming techniques.

II. Goals

To solve the above problems, Hooks came into being, with the goals:

  • Provide a simple and direct code reuse method

  • Provide a more reasonable code organization method

  • Provide an alternative to Class

On one hand, solve the problems of code organization and reuse. On the other hand, the new component definition method is also part of React's future vision:

Hooks represent our vision for the future of React.

So, what exactly are Hooks?

III. Positioning

Hooks are functions that let function components access React State and lifecycle features:

Hooks are functions that let you "hook into" React state and lifecycle features from function components.

On one hand, use Hooks to split/organize code more reasonably, solving reuse problems. On the other hand, enhance functional components through Hooks, giving them the same expressiveness as Class components, thereby becoming an alternative option, and ultimately replacing them

IV. Functions

Hooks mainly solve problems in code organization and logic reuse, for example:

  • Organize related logic split apart by lifecycle, such as data source subscription/unsubscription, event listener registration/deregistration, etc.

  • Reuse repeated logic scattered in lifecycle across components, while solving the component nesting problem (Wrapper Hell) brought by component composition-based reuse patterns like HOC and Render Props

In addition, for React itself, Hooks also solve obstacles in large-scale optimization, such as the compilation difficulty of inline components

Code Organization

Under the Hooks solution, the biggest difference is that components can be split into smaller functions based on the internal relationships of code blocks, rather than being forcibly split according to lifecycle methods:

Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data), rather than forcing a split based on lifecycle methods.

For example:

// Custom Hook
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  // Register/unregister external data source
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

Code for registering/unregistering external data sources is tightly linked together, without having to worry about differences in timing (component lifecycle)

Logic Reuse

At the same time, as in the example above, these stateful logic and side effects can be easily extracted into Hooks, and combined into Custom Hooks, beautifully solving the stateful logic reuse problem:

With Hooks, you can extract stateful logic from a component so it can be tested independently and reused.

For example:

// View Component 1
function FriendStatus(props) {
  // Use custom Hook
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
// View Component 2
function FriendListItem(props) {
  // Use custom Hook
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

On the other hand, this reuse method is "non-destructive", no need to adjust the component tree hierarchy, just import and use:

Hooks allow you to reuse stateful logic without changing your component hierarchy. This makes it easy to share Hooks among many components or with the community.

V. Summary

From a formal perspective alone, Hooks are an enhancement to functional components, making them equal to class components, and even (expected) to replace them. The substantive significance lies in further introducing more functional ideas into the frontend field, such as Effect, Monad, etc. It's considered further exploration on this path after proposing the UI layer functional idea of v = f(d)

(Excerpted from React 16 Roadmap)

To some extent, this ideological storm is more exciting than core features like Concurrent Mode

Reference Materials

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment