The text /
Deep island

Why know Hooks and functional components? Because it’s becoming more and more important. Since Hooks began, functional components have become more functional. Functional components no longer need to emphasize their Stateless nature, and it is more appropriate to call them Function Component instead of Stateless Component. Use Class or Hooks? There has been a lot of debate in the community. React officially says it is “ready to have Hooks overwrite all Class components and continue to support Class components”. The React component has always been conceptually more like a data-to-view mapping function UI = F(data). Hooks embrace functions and are more declarative and functional. Therefore, I personally believe that Hooks have a broader development space in the future.

How does a functional component differ from a class component

Before Hooks, the typical answer was that class components provided more features (such as life cycles, state, etc.). Or performance issues, are Hooks slow to create functions at render time? Don’t. In modern browsers, the raw performance of closures and classes differs significantly only in extreme scenarios. As React developer Dan Abramov explains on his blog, “Performance is primarily determined by what the code does, not by choosing between functional and class components. In our observations, the performance differences were negligible, although the optimization strategies varied slightly.

More fundamentally, differences in mental models.

Functional components are more “declarative”

In A Class component, the usual way to write this is to check props.A and state.B during the lifecycle and trigger the XXX side effect if it changes or if A condition is met. Using Hooks in A functional component means that the component has A XXX side effect that depends on data like props.A and state.b. Shifting from imperative to declarative, Hooks provide a variety of declarative side effects apis (useEffect, use allback) that make “lifecycle” a “low-level concept” that is insensitive to developers, so developers can focus on higher levels of abstraction.

Functional components are more “functional”

Functional components are certainly more “functional” in a literal sense than class components. The React component has always been more like functions conceptually, whereas Hooks embrace functions without sacrificing the spirit of React.

The essence of React is the ability to map declarative code to imperative DOM operations and data to descriptive UI objects. In order to respond to data changes (at runtime), React initially adopted a class-based component design, which introduced the concept of a lifecycle and provided developers with a series of apis from component creation to destruction. For class components, although the render function is declarative, the class instance itself is mutable (this is mutable). As a result, the data on the class instance (props and state) is not bound to the render result, making the data to the UI render sometimes unpredictable. This may be a bit abstract, but the following example will help you understand. From a functional component from ProfilePage, click button and three seconds later “Followed XXX” will pop up.

function ProfilePage(props) { 
    const showMessage = () => alert('Followed ' + props.user); 
    const handleClick = () => setTimeout(showMessage, 3000); 
    return <button onClick={handleClick}>Follow</button> 
}
Copy the code

Use the Class component to implement the function as follows:

class ProfilePage extends React.Component { 
    showMessage = () => alert('Followed ' + this.props.user); 
    handleClick = () => setTimeout(this.showMessage, 3000); 
    render() { 
      return<button onClick={this.handleClick}>Follow</button>; }}Copy the code

If the current user is Dan, click button and switch user to Sophie within 3s, you will find that the functional component prompt “Followed Dan”, while the class component prompt “Followed Sophie”, obviously the class component prompt is wrong. The state determined when the user triggers should not be affected by subsequent actions.



Isn’t the React documentation describing props Immutable data? Why does it change at run time?

The reason is that while props is immutable, this is mutable in the class component, so a call to this.props causes the latest props to be accessed each time. In fact, that’s why the This class component exists. The React component itself (this) changes over time so that you can get up to date instances in the render and lifecycle methods. React can be thought of as immutable props and state for a given render, but the problem would be solved if JavaScript closures were used to store certain render data. As follows:

class ProfilePage extends React.Component { 
  render() { const props = this.props; Const showMessage = () => {alert();'Followed ' + props.user); 
    }; 
    const handleClick = () => { 
       setTimeout(showMessage, 3000); 
    }; 

    return<button onClick={handleClick}>Follow</button>; }}Copy the code

As you can see, we define various functions in the Render method, rather than using the Class method to get the correct context, which of course is written more like a functional component. For functional components, the ProfilePage function will be called again when the parent component uses different props to render the ProfilePage, but the event handler we clicked on “belongs” to the last render with its own user value, The showMessage callback also reads this value, so the result is correct. In Class components, however, because the component instance (this) is always changing, as in the above example, reading the this.props function on timeout captures the updated state of the data, which results in an incorrect rendering.

In React, we can think of UI as a visual output of data. It uses props to receive external data, uses state to manage internal data (state), processes data, responds to data changes, maps data changes according to user operations, and outputs a visual model. So (props, state) => UI. Functional components can always capture the correct data states (props and state) in each rendering, that is, if the input data states are the same each time, the rendered view is the same, which is more “pure”, while class components rely on the dynamic changes and life cycle of the instance to complete the rendering of data states to the view, which is less “pure”.

Why Hooks are needed

1. State logic reuse is difficult

React does not provide a native way to reuse state logic. Common logic reuse of class components would use a scheme of HOC or Render props, but such scheme traversal requires you to reorganize the component structure, and too many nested abstraction layer components can easily form “nested hell”.

HOC – High order component

Here’s a common use of HOC. Using connect to connect to store and withRouter to get routing parameters is very readable and maintainable (ps). Two levels of nesting is uncomfortable), and while you can use compose to compose higher-order components, or decorator simplification, it’s essentially HOC nesting.

Const App = withRouter(connect(commentSelector)(WrappedComponent)); // Optimization can use a compose function to combine HOC const enhance = compose(withRouter, connect(commentSelector)); Const App = enhance(WrappedComponent); @connect class App extends React.Component {}Copy the code

Each HOC call generates a component instance, and multiple layers of nesting increases the depth of the React virtual Dom and affects performance. In addition, wrapping too many layers may cause problems with props properties. In addition, HOC is more like a black box to the user, and exploration depends on the implementation to use it.

Reder Props

The following is the logic used to listen for changes in window size

<WindowSize> 
  (size) => <OurComponent size={size} /> 
</WindowSize>Copy the code

Then, if you want to reuse the logic that listens for mouse position

<WindowSize> 
(size) => ( 
    <Mouse> 
    (position) => <OurComponent size={size} mouse={position} /> 
 </Mouse> ) 
</WindowSize>
Copy the code

Although render props solves some of hoc’s problems, such as overwriting user black boxes and attribute names, using render props too much logic can still result in too much nesting, creating callback hell.

Hooks – Provide a native way to reuse state logic

Const size = useSize() const position = useMouse()Copy the code

Who wants to go back to writing HOC and render props after using custom Hooks? The way in which custom Hooks reuse state logic is supported by React natures. Unlike the React component, custom Hooks are a function that starts with use and are therefore easier to test and reuse. In addition, you can use other Hooks in the custom Hooks for True Scent.

2. Life cycle

High learning cost

Class components have many lifecycle functions that vary from version to version and are expensive to learn. For example, how does React V16’s life cycle change compared to React V15? And such as componentWillReceiveProps and getDerivedStateFromProps have what differentiation? The lifecycle provides a lot of functionality for development, but it also carries a high cost of learning, often requiring a lot of practice to gain sufficient experience.

The use of useEffect in functional components replaces the invocation of a class component’s declaration cycle, freeing developers from additional concerns about the use of the declaration cycle and focusing on the processing and mapping of data changes into new views. Furthermore, when writing React code using Hooks, the best mental rule is not to map the life cycle to that of Hooks, but to assume that any value can change at any time and needs to be handled accordingly.

Logic scattered

As component complexity increases, internal inspections can become riddled with states and side effects, yet much of the logic is scattered throughout the life cycle of a class component rather than focused together. For example, setting event listeners in componentDidMount also requires clearing them in componentWillUnmount.

componentDidMount() {     
    document.addEventListener('mousedown', this.handleXXX); 
} 
componentWillUnmount() {     
    document.removeEventListener('mousedown', this.handleXXX); 
}
Copy the code

Or we TongZha componentDidMount retrieve data, parameter change to componentWillReceiveProps to initiate the request again.

componentDidMount() { 
   const { code } = this.props; this.getDataSource(code); 
} 
UNSAFE_componentWillReceiveProps(nextProps) { 
  const { code: nextCode } = nextProps; 
  const { code } = this.props; 
  if ( code !== nextCode ) { 
     this.getDataSource(nextCode); 
  } 
}
Copy the code

You can see that in a class component, code that is related and needs to be modified is split up, while completely unrelated code is grouped together in the same method, which is prone to bugs and logic inconsistencies. 8. Using Hooks:

/ / event listeners useEffect (() = > {document. AddEventListener ('mousedown', handleXXX); 
      return () => {             
       document.removeEventListener('mousedown', handleXXX); }}, []) / / get data useEffect (() = > {getDataSource (code); }, [code])Copy the code

In Hooks, there is no need to break down logic by lifecycle, parts of components that are related to each other can be broken down into smaller functions, with more logic aggregation and less code.

3. The problem of a Class

A headache Class

The implementation of JavaScript classes is weak and there is no similar concept of Java/C++ multiple inheritance, so it is difficult to implement complex logic reuse. In addition, using Class often requires understanding how this works, but fully understanding how the this keyword works and how it is used in code is a challenge for many developers. Using the Class component in React requires thinking about event binding all the time, although the arrow function avoids some of the work.

On the other hand, there is a consensus among most frameworks that “composition is better than inheritance for UI frameworks”.

Compilation and optimization

Components that perform the same function will typically use more code in Class components than functional components, and Class components will compile much more code than functional components, making them harder to compress. The use of Class components also presents problems with hot reload and Prepack for front-end build tools.

Bug fixes exist

Does not completely replace Class

Life cycle apis like getSnapshotBeforeUpdate, getDerivedStateFromError, and componentDidCatch are not completely replaced by Hooks. But officials say they “will be added as soon as possible.” .

The loading capacity of complex components is weak

A complex Class business component with an instance that may have many methods mounted on it is a major challenge to keep the business logic of the component clear and to ensure subsequent maintainability if implemented in the manner of functional component Hooks.

Easy to use is difficult

Easy to use means that Hooks only need to learn useState and useEffect to satisfy the basic functionality of most components. However, there will be many problems in the subsequent use, such as the agreement of Hooks Rules, the judgment of Hooks dependencies, Hooks closure problems, Hooks performance optimization, etc., which require developers to have some practical experience to complete.

reference

  • Hooks FAQ
  • Introducing Hooks
  • How Are Function Components Different from Classes?
  • React Hooks Complete tutorial
  • Umi Hooks – Help embrace React Hooks