preface

A recent refactoring of older projects has unified the use of the new React technology stack. We also decided to try to use React hooks for development, but because React hooks favor (and only use)function component development, not class component, The whole way of development will also be quite different from before. So, HERE I have accumulated the problems and thinking encountered in the actual project, to see if it can help you avoid detours.

The body of the

Let’s get right into the text. I will list the problems encountered in the project one by one and give solutions.

The timing of the initialization operation

When I turn to React hooks, I first run into this problem:

In general, business components often encounter scenarios where they need to initiate Ajax requests to retrieve business data and perform initialization operations. When programming with Class Component, we can do this in the lifecycle hook functions provided by class Component (e.g. ComponentDidMount, Constructor, etc.). Function Component does not have the life cycle hook function after switching to React hooks. You can’t use class Component every time you encounter this scenario, can you?

Solution: Use useEffect(to find out what useEffect is, click here)

UseEffect, as the name suggests, is to do something that has side effects, and you can think of it as a set of componentDidMount, componentDidUpdate, and componentWillUnmount. Its function declaration is as follows

useEffect(effect: React.EffectCallback, inputs? : ReadonlyArray<any> |undefined)
Copy the code

So, in practice, we can use this to do initialization. For example

import React, { useEffect } from 'react'

export function BusinessComponent() {
  const initData = async() = > {// Initiate the request and perform the initialization
  }
  The second parameter must be passed to the empty array if you only want to initialize the data once at render time.
  useEffect((a)= >{ initData(); } []);return (<div></div>);
}
Copy the code

Note that the second argument to useEffect here must be passed to the empty array so that it is equivalent to executing only when componentDidMount. If you don’t pass the second argument, it’s equivalent to componentDidMount and componentDidUpdate

Do some cleanup

Because in the actual development process, we often meet need to do some side effects scenarios, such as polling operation (timer, polling requests, etc.), use the browser native events to monitor events rather than react mechanism (in this case, the component destroyed, requires the user to take the initiative to go to cancel the event listeners), etc. ComponentWillUnmount or componentDidUnmount. How do you fix react hooks?

Solution: useEffect with the return value of the first argument

If useEffect returns the function as the first argument, React will execute the function to do some cleanup before executing a new effect. Therefore, we can use it to perform some cleanup operations.

Example: For example, if we want to make a TWO-DIMENSIONAL code component, we need to constantly poll the background to query the status of scanning the two-dimensional code based on the incoming userId. In this case, we need to clean up the polling operation when the component is unmounted. The code is as follows:

import React, { useEffect } from 'react'

export function QRCode(url, userId) {
  // Query scan status according to userId
  const pollingQueryingStatus = async () => {
  }
  // Cancel polling
  const stopPollingQueryStatus = async() => {
  }

  useEffect((a)= > {
    pollingQueryingStatus();

    returnstopPollingQueryStatus; } []);// Generate a QR code based on the URL
  return (<div></div>)}Copy the code

This is equivalent to cleaning up componentWillUnmount.

However, sometimes we may need to perform multiple cleanup operations. Using the above example, we need to execute a new query when the user passes in a new userId, and we also need to clean up the old polling operation. Let’s think about what we can do.

UseEffect’s second parameter is the key to triggering effects. If the user passes in a second parameter, effects will only be triggered when the value of the second parameter changes (and the first rendering). Therefore, we just need to change the above code:

import React, { useEffect } from 'react'

export function QRCode(url, userId) {
  // Query scan status according to userId
  const pollingQueryingStatus = async () => {
  }

  const stopPollingQueryStatus = async() => {
  }
  // We just add a userId to the second argument to useEffect
  useEffect((a)= > {
    pollingQueryingStatus();

    return stopPollingQueryStatus;
  }, [userId]);

  // Generate a QR code based on the URL
  return (<div></div>)}Copy the code

We simply add a userId to the second parameter array for useEffect. This way, every change to the userId will trigger stopPollingQueryStatus before executing Effects, which will do the trick.

Difference between useState and setState

React hooks use useState instead of state in class Component. However, I also found some differences in the specific development process. An introduction to useState can be found here

When we setState, we can modify only local variables in state without passing in the entire modified state, for example

import React, { PureComponent } from 'react';

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0.name: 'cjg'.age: 18,
    }
  }

  handleClick = (a)= > {
    const { count } = this.state;
    // We just need to pass in the modified local variable
    this.setState({
      count: count + 1}); } render() {return (
      <button onClick={this.handleClick}></button>)}}Copy the code

With useState, we have to pass in the entire modified state because it overwrites the previous state rather than merging the previous state object.

import React, { useState } from 'react';

export function Count() {
  const [data, setData] = useState({
    count: 0.name: 'cjg'.age: 18});const handleClick = (a)= > {
    const { count } = data;
    // The complete state object must be passed insetData({ ... data,count: count + 1,})};return (<button onClick={handleClick}></button>)}Copy the code

Reduce unnecessary rendering

ShouldComponentUpdate should be used to reduce unnecessary rendering when developing with Class Component. How do we implement this after using React hooks?

Solutions: React. Memo and useMemo

React, of course, provides an official solution to this problem, which is to use react. Memo and useMemo.

React.memo

Momo isn’t really a hook, it’s equivalent to PureComponent, but it only compares props. It can be used as follows (using the example above):

import React, { useState } from 'react';

export const Count = React.memo((props) = > {
  const [data, setData] = useState({
    count: 0.name: 'cjg'.age: 18});const handleClick = (a)= > {
    const{ count } = data; setData({ ... data,count: count + 1,})};return (<button onClick={handleClick}>count:{data.count}</button>)});Copy the code

useMemo

The use of useMemo is actually a little bit like useEffects, so let’s go straight to the official example

function Parent({ a, b }) {
  // Only re-rendered if `a` changes:
  const child1 = useMemo((a)= > <Child1 a={a} />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = useMemo((a)= > <Child2 b={b} />, [b]);
  return (
    <>
      {child1}
      {child2}
    </>)}Copy the code

As you can see from the example, its second argument is the same as the second argument to useEffect, and the child component will only be updated if the value of the second argument array changes.

conclusion

It was awkward at first when transitioning from class Component to React hooks. But when I got used to it, my mood was as follows:

Of course, right now react hooks are still in alpha, if you’re worried, you can wait. I’m going to play with it anyway.

This article address in -> my blog address, welcome to give a start or follow