React Hook

Directory:

  • Lead – why Hook
  • The basic use
  • Custom implementation hooks
  • The real implementation of hook-react
  • Class and Hook
  • Summary – problem thinking

Lead – why Hook

In the past, we had to use class components of special functions for lifecycle methods (such as componentDidUpdate) and special state-handling methods to handle state changes. React Class, especially the JavaScript object this.context, is hard for humans and machines to read and understand because it always refers to different things, so sometimes (for example, in event handlers) we need to manually rebind it to the class object. The computer does not know which methods in the class will be called and how to modify them, making performance and code optimization difficult. In addition, CLSSS sometimes requires us to write code in more than one place at a time. For example, if we want to get data during component initialization or data update, here’s an example:

First, we define our class component by extending the React.component class:

class Example extends React.Component {
Copy the code

We then define the componentDidMount lifecycle method, in which we extract data from an API

  componentDidMount () {
    fetch(`http://my.api/The ${this.props.name}`)
    .then(...)
  }
Copy the code

We also need to define the componentDidUpdate lifecycle method to determine whether to update the state when a prop changes.

  componentDidUpdate (prevProps) {
    if (this.props.name ! == prevProps.name) { fetch(`http://my.api/The ${this.props.name}`) .then(...) }}}Copy the code

To reduce code duplication, we can define a separate method called fetchData to get the data, as follows:

  fetchData () {
    fetch(`http://my.api/The ${this.props.name}`)
    .then(...)
  }
Copy the code

Finally, we call the methods in componentDidMount and ComponentDidUpdate

  componentDidMount () {
    this.fetchData()
  }
  componentDidUpdate (prevProps) {
    if (this.props.name ! == prevProps.name) {this.fetchData()
  }
}
Copy the code

However, even then, we still need to call fetchData in two places. Whenever we update the parameters passed to the method, we need to update them in two places, making this pattern prone to bugs and future bugs.

Before hooks, if we wanted to encapsulate state management logic, we had to use higher-order components and presentation items. For example, we create a React component that handles user authentication using context, as follows:

We first import the authenticateUser function to wrap the component with a context, and then import the AuthenticationContext component to access the context:

import authenticateUser, { AuthenticationContext } from './auth'
Copy the code

Then, we define the app components, in which we use AuthenticationContext. Consumer component

const App = () = > (
  <AuthenticationContext.Consumer>
    {user= >
Copy the code

Now, we display different text depending on whether the user is logged in or not

      user ? `${user} logged in` : 'not logged in'
Copy the code

Finally, we add context

      }

    </AuthenticationContext.Consumer>
  )

export default authenticateUser(App)

Copy the code

In the previous example, we used the high-level authenticateUser component to add authentication logic to the existing component. Then we use a authenticationcontext. Consumer will the user object into components. As you can imagine, using many contexts would result in a large component with many child zu’jian. For example, when we want to use three contexts, wrapper hell looks like this:

<AuthenticationContext.Consumer>
  {user= > (
    <LanguageContext.Consumer>
      {language => (
        <StatusContext.Consumer>{status => ( ... ) }</StatusContext.Consumer>
      )}
    </LanguageContext.Consumer>
  )}
</AuthenticationContext.Consumer>

Copy the code

It’s not very easy to read and modify, and it’s error prone if we need to change something later. Furthermore, if we look at a large tree of components, many of which are just wrappers, this traditional approach makes debugging difficult.

React Hook Based on React fundamentals, Hook attempts to encapsulate state management by using existing JavaScript features. Therefore, we no longer need to learn and understand the React feature specifically; We can simply use our existing JavaScript knowledge to use hooks.

We can use hooks to solve all of the problems mentioned earlier. We no longer need to use a class component because a Hook is just a function that can be called in a function component. We also no longer need to use higher-order components and rendering props for the context because we can simply use the Hook context to get the data we need. In addition, hooks allow us to reuse stateful logic between components without creating higher-order components.

For example, the aforementioned lifecycle method problem can be solved using hooks:

function Example ({ name }) {
  useEffect(() = > {
    fetch(`http://my.api/The ${this.props.name}`)
    .then(...)
}, [ name ])
// ...
}
Copy the code

The effect achieved here is that the hooks will fire automatically when the component is mounted and when the prop changes. In addition, the wrapper hell mentioned earlier can also be handled using hooks, as shown below

const user = useContext(AuthenticationContext)
const language = useContext(LanguageContext)
const status = useContext(StatusContext)
Copy the code

Now that we know what problems Hook can solve, let’s get started.

Basic use of Hook:

React components can be roughly divided into class components and function components. If you need to change the state of a component in React, the component must be a class component. Can function components have the functions of class components? At this point we need to use hooks to make our function components have component-like characteristics. Hooks are new to Act 16.8. They allow us to use states and other React functions without writing classes. Hook also provides a method to write components, making it easier and more convenient to write a component. At the same time, you can customize Hook to extract the common logic, so that the logic is shared between multiple components.

Let’s start with a code sample, demo, that requests data:

import React, { useState } from 'react';
import "./Welcome.scss";

function Welcome() {
  const [data, setData] = useState({ hits: [{
    objectID:"001".url:"https://www.jd.com/".title:"JD"}}]);return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default Welcome;
Copy the code

This component is a list of items. The data and status update functions are initialized from the useState Hook to create the internal state of the App component by calling useState. The initial state is an Object, where hits is an empty array. If we want to add the call back end data, we can use AXIos to initiate the request, as well as fetch.

function Welcome() {
  const [data, setData] = useState({ hits: [{
    objectID:"001".url:"https://www.jd.com/".title:"JD"}}]); useEffect(async() = > {const result = await axios(
      'http://localhost/api/v1/search? query=redux'
    );
    setData(result.data);
  });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
Copy the code

In useEffect, we request data from the back end and update the local state by calling setData, which triggers an interface update. However, when running this program, an infinite loop will appear. Assuming that we only want to request data when the component is mounted, we can pass an empty array as the second argument to useEffect, which avoids useEffect being executed when the component is updated and only when the component is mounted. The second argument to useEffect is used to define all the variables on which it depends. If one of the variables changes, useEffect is run again. If the array containing the variables is empty, useEffect is not executed when the component is updated because it does not listen for any variable changes.

function Welcome() {
  const [data, setData] = useState({ hits: [{
    objectID:"001".url:"https://www.jd.com/".title:"JD"}}]); useEffect(async() = > {const result = await axios(
      'http://localhost/api/v1/search? query=redux'
    );
    setData(result.data);
  },[]);

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
Copy the code

In the code, we use async/await to get data from third-party apis, since each async function returns an implicit promise by default. However, useEffect does not want to return anything, which is why async cannot be used directly in useEffect. Therefore, instead of calling async directly, we can do something like this:

  useEffect(() = > {
    const fetchData = async() = > {const result = await axios(
        'http://localhost/api/v1/search? query=redux',); setData(result.data); }; fetchData(); } []);Copy the code

In useEffect, loading can be set to true before the request and false after the request is completed.

After loading, errors need to be handled. The logic is the same here. Use useState to create a new state, and then useEffect to update the state at a specific location. Since we are using async/await, we can use a try-catch that will reset the error every time useEffect is executed; Set error to true when errors occur; After a normal request completes, set Error to false.

function Welcome() {
  const [data, setData] = useState({ hits: []});const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'http://localhost/api/v1/search? query=redux',);const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() = > {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(url);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };

    fetchData();
  }, [url]);

}
Copy the code

A Hook is a function that can be called in a function component. We also no longer need to use high-level components and the traditional class approach for the context because we can simply use the Hook context to get the data we need. In addition, hooks allow us to reuse stateful logic between components without creating higher-order components. Let’s take a quick look at the other methods that Hook provides:

The method name usage The sample thinking
useRef This method returns a mutable ref object where the.current property is initialized to the initialValue passed as the parameter import { useRef } from ‘react’; const refContainer = useRef(initialValue) UseRef handles references to elements and components in React. We can set the reference by passing the REF attribute to an element or component.
useReducer This is an alternative to useState and works in a similar way to the Redux library import { useReducer } from ‘react’;

const [ state, dispatch ] = useReducer(reducer, initialArg, init)
UseReducer is often used to handle complex state logic.
useMemo Memoization is an optimization technique that caches the results of function calls, and useMemo allows us to compute a value and record it import { useMemo } from ‘react’;

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
UseMemo is useful for performance optimization when we want to avoid re-performing time-consuming operations.
useCallback This method allows us to pass an inline callback function and a set of dependencies, and will return the memorized version of the callback function. import { useCallback } from ‘react’; const memoizedCallback = useCallback(() => {doSomething(a, b) }, [a, b]) UseCallback is useful when passing callback data to child components. It works like useMemo, but is used for callback functions.
useLayoutEffect UseLayoutEffect is the same as USEffect, but it only fires after all the Document Object Model (DOM) has changed. import { useLayoutEffect } from ‘react’; useLayoutEffect(didUpdate) UseLayoutEffect can be used to read information from the DOM. (It is best to use usEffect, useLayoutEffect will block attempts to update and slow down the application’s rendering speed)
useDebugValue UseDebugValue can be used to display labels in React DevTools when creating custom hooks. import { useDebugValue } from ‘react’; useDebugValue(value) You can use useDebugValue in custom hooks to display the current state of the Hook, which makes it easier to debug components.

In addition to all the syntax candy that React officially provides, the community has published a number of libraries. These libraries also provide some methods, and we can take a look at some of the most popular ones:

useInput

UseInput is used to easily implement input processing and synchronize the state of input fields with variables. It can be used as follows:

import { useInput } from 'react-hookedup'
function App () {
  const { value, onChange } = useInput(' ')
  return <input value={value} onChange={onChange} />
}
Copy the code

As we can see, useInput greatly simplifies the handling of input fields in React.

useResource

UseResource can be used for asynchronous data loading through requests in your application. We can use it as follows

import { useResource } from 'react-request-hook'
  const [profile, getProfile] = useResource(id= > ({ url: `/user/${id}`.method: 'GET'
})
Copy the code

As we can see, using useResource to handle fetching data is straightforward.

Navigation Hooks

Navigation is part of the Navi library and is used to implement routing functionality via React hooks. The Navi library provides more routing-related hooks. We can use them as follows

import { useCurrentRoute, useNavigation } from 'react-navi'
const { views, url, data, status } = useCurrentRoute()
const { navigate } = useNavigation()
Copy the code

Navigation Hooks make routing easier to handle.

Life cycle Hooks

The React HookedUp library provides various Hooks, including all the life cycle listeners for React. (Note that it is not recommended to consider the component lifecycle when developing with hooks. These hooks simply provide a way to refactor existing components into hooks. Here, we list two of them, as follows

import { useOnMount, useOnUnmount } from 'react-hookedup'
useOnMount(() = >{... }) useOnUnmount(() = >{... })Copy the code

React HookedUp directly replaces the lifecycle methods in class components.

Timer Hooks

The React HookedUp library also provides methods for setInterval and setTimeout. This works like calling setTimeout or setInterval directly. But as a React Hook, it will keep executing at the cost of re-rendering. If we define the timer directly in the function component without using the Hook, we will reset the timer every time the component is re-rendering. We can pass the time in milliseconds as the second argument. We can use it as follows:

import { useInterval, useTimeout } from 'react-hookedup'
useInterval(() = >{... },1000)
useTimeout(() = >{... },1000)
Copy the code

Implementing a Hook

Before implementing a Hook, let’s have an in-depth understanding of State Hook. We will start with how State Hook works internally and re-implement it ourselves. Next, we’ll look at some of the limitations of hooks and why they exist. Then, we’ll look at possible alternative Hook apis and related issues. Finally, we’ll learn how to solve common problems caused by Hook limitations. Finally, we’ll look at using hooks to implement the React stateful function component.

We will need the ReactDOM to render the component in the re-implementation of the useState Hook. If we use the actual React Hook, this will be handled internally.

import React from 'react'
import ReactDOM from 'react-dom'
Copy the code

Now let’s define our useState function. The useState function takes initialState as an argument:

function useState (initialState) {
Copy the code

We then define a value in which to store our state. First, the value is set to initialState, which is passed to the function as an argument:

  let value = initialState
Copy the code

Next, we define the setState function, where we will set the value to a different value and render our MyName component

  function setState (nextValue) {
    value = nextValue
    ReactDOM.render(<MyName />.document.getElementById('root'))}Copy the code

Finally, we return the value and setState functions as arrays:

  return [ value, setState ]
}
Copy the code

The reason we use arrays instead of objects is that we usually want to rename value and setState variables. Using arrays makes it easy to rename variables by destructuring them.

const [ name, setName ] = useState(' ')
Copy the code

Our Hook function uses closures to store the current value. Closures are environments in which variables exist and are stored. In our example, the function provides a closure in which the value variable is stored. The setState function is also defined in the same closure, which is why we can access the value variable in the function. Outside of the useState function, you cannot access the value variable directly unless it is returned from the function. So what’s wrong with the simple Hook we implemented?

If we run our Hook demo now, we’ll notice that the state is reset when our component is re-rendered. This is due to reinitializing the value variable each time the component is rendered, because the useState method is called each time the component is rendered. Next, we’ll use a global variable to solve this problem, then put value into an array, and then we’ll define multiple hooks. As we learned, the value is stored in a closure defined by the useState function. Each time a component is resubmitted, the closure is reinitialized, which means our value will be reset. To solve this problem, we need to store the value in a global variable outside the function. This way, the value variable will be inside the closure outside the function, which means that the closure will not be reinitialized when the function is called again. We can define global variables as follows:

First, we add the following line above the useState function definition

let value
function useState (initialState) {
Copy the code

Then, replace the first line in the function with the following code

  if (typeof value === 'undefined') value = initialState
Copy the code

Now, our useState function uses global value variables instead of defining value variables in its closure, so it is not reinitialized when the function is called again.

Our Hook functionality is available, but if we want to add another Hook, we run into another problem: all hooks write to the same global value variable. Let’s examine this in more detail by adding a second Hook to the component.

Suppose we want to add the lastName state as follows:

We first create a new Hook after the current Hook,

const [ name, setName ] = useState(' ')
const [ lastName, setLastName ] = useState(' ')
Copy the code

Then, we define another handleChange function

function handleLastNameChange (evt) {
  setLastName(evt.target.value)
}
Copy the code

Next, we place the lastName variable after the name:

<h1>My name is: {name} {lastName}</h1>
Copy the code

Finally, we add another input field:

<input type="text" value={lastName} onChange= 
{handleLastNameChange}
/>
Copy the code

When we write like this, we’ll notice that our re-implemented Hook function uses the same value for both states, so we always change both fields at the same time. To implement multiple hooks, rather than just one global variable, we should have an array of hooks. We will now refactor the value variable into a value array so that we can define multiple hooks.

We delete the following lines of code

let value
Copy the code

Replace it with the following code snippet

let values = []
let currentHook = 0
Copy the code

Then, edit the first line of the useState function, where we now initialize the value at the currentHook index of the values array:

if (typeof values[currentHook] === 'undefined')
values[currentHook] = initialState
Copy the code

We also need to update the setter functions to update the corresponding state values. In this case, we need to store the currentHook value in a separate hookIndex variable because the currentHook value will change later. This ensures that a copy of the currentHook variable is created in the closure of the useState function. Otherwise, the useState function will access the currentHook variable from an external closure that is modified each time useState is called.

let hookIndex = currentHook
function setState (nextValue) {
  values[hookIndex] = nextValue
  ReactDOM.render(<MyName />.document.getElementById('root'))}Copy the code

Edit the last line of the useState function as shown below

return [ values[currentHook++], setState ]

Copy the code

Using values[currentHook++], we pass the current value of currentHook as an index to the values array and increment the currentHook by 1. This means that currentHook will increase upon return from the function. The currentHook counter still needs to be reset when you start rendering the component. Add the following after the component definition:

function Name () {
  currentHook = 0
Copy the code

Finally, we simply re-implement the useState Hook. As we have seen, using a global array to store Hook state solves the problem we encountered when defining multiple hooks. What if we wanted to add a checkbox to toggle the use of the first Name field?

First, we add a new Hook to store the state of the check box:

const [ enableFirstName, setEnableFirstName ] = useState(false)
Copy the code

Then, we define a handler function

function handleEnableChange (evt) { setEnableFirstName(! enableFirstName) }Copy the code

Next, we render a check box

<input type="checkbox" value={enableFirstName} onChange= {handleEnableChange} />
Copy the code

Add a check on the enableFirstName variable

<h1>My name is: {enableFirstName ? name : ' '} {lastName}
</h1>
Copy the code

Can we put Hook definitions in if conditions or ternary expressions, as we do in the code snippet below?

const [ name, setName ] = enableFirstName ? useState(' ') :' '.() = >{}]Copy the code

The latest version of React-scripts actually throws an error when it defines conditional Hooks, so we need to degrade the library in this example by running the following command:

>  npm install --save react-scripts@^2.18.
Copy the code

Here, if the name is disabled, we return the initial state and an empty setter function so that editing the input field does not work. Notice that editing last Name still works, but editing first name doesn’t. As you can see in the screenshot below, you can only edit Last Name now.

The program does the following when we click the check box:

  1. The check box is selected
  2. Enable the name input field
  3. The last Name field value is now the first Name field value

We can see the result of clicking the check box in the following screen capture:

We can see that the last Name state is now in the First Name field. These values are swapped because the order of the hooks is important. As we know from the implementation, we use the currentHook index to know where the state of each Hook is stored. However, when we insert an additional Hook between two existing hooks, the order is out of order.

Before the check box is selected, the values array looks like this:

  • [false, ”]
  • Hook: enableFirstName, lastName

We then enter some text in the lastName field:

  • [false, ‘Hook’]
  • Hook: enableFirstName, lastName

Next, we toggle the checkbox, which activates our new Hook

  • [true, ‘Hook’, ”]
  • Hook order: enableFirstName, name, lastName

As we have seen, inserting a new Hook between two existing hooks causes the Name Hook to get the state of the next Hook (lastName) because it now has the same index as the lastName Hook did before. Now, lastName Hook has no value, which causes it to set the initial value to an empty string. Therefore, toggling the checkbox puts the value of the lastName field into the Name field.

The real implementation of hook-react

Our simple Hook implementation has given us an idea of how hooks work internally. Hooks, however, do not use global variables. Instead, they store the state in the React Component. They also handle Hook counters internally, so we don’t need to manually reset the count in the function component. In addition, real hooks automatically trigger component re-rendering when state changes. However, to do this, you need to call the Hook from the React function component. React Hook cannot be called outside React or inside React Class components. We should always define hooks at the beginning of function components and never nest them in if or other constructors. We should call the React Hook component inside the React function. React Hooks cannot be defined conditionally or in a loop.

So, how do we implement conditional hooks? Instead of making a Hook conditional, we can define a Hook and use it as needed. We can regroup our components. Another approach to conditional hooks is to split a component into multiple components and then conditionally render those components. For example, suppose we want to retrieve user information from the database after the user logs in.

We cannot do the following because the order of the hooks can be changed using the if condition

function UserInfo ({ username }) {
  if (username) {
    const info = useFetchUserInfo(username)
    return <div>{info}</div>
  }
  return <div>Not logged in</div>
}

Copy the code

We must create a separate component for user login, as follows:

function LoggedInUserInfo ({ username }) { const info = useFetchUserInfo(username) 
  return <div>{info}</div>
}

function UserInfo ({ username }) {
  if (username) {
    return <LoggedInUserInfo username={username} />
  }
  return <div>Not logged in</div>
}
Copy the code

It makes sense to use two separate components for non-logged and logged states, because we want each component to have a single function. For hooks in loops, we can use a single state Hook that contains an array, or we can split components. For example, suppose we want to display all online users.

We can use arrays to contain all user data, as follows:

function OnlineUsers ({ users }) {
  const [ userInfos, setUserInfos ] = useState([])
  //	... fetch & keep userInfos up to date ...
  return ( <div>
  {users.map(username => {
    const user = userInfos.find(u => u.username === username)
    return <UserInfo {. user} / >
  })}
  </div>)}Copy the code

However, this could be problematic. For example, we might not want to update all user states through the OnlineUsers component because we would have to select the state of the user to modify from the array and then modify the array. A better solution is to use hooks in the UserInfo component. This way, we can keep each user’s state up to date without having to deal with array logic:

function OnlineUsers ({ users }) {
  return (
    <div>
    {users.map(username => <UserInfo username={username} />)} </div>)}function UserInfo ({ username }) {
  const info = useFetchUserInfo(username)
  //	... keep user info up to date ...
Copy the code

As we’ve seen, using a separate component for each functional module keeps the code simple and concise while avoiding the constraints of React Hook. Let’s start by re-implementing the useState function, using global state and closures. Then we learned that to implement multiple hooks, we needed to use a state array instead. However, by using state arrays, we must maintain consistency in hook order between function calls. This restriction prevents us from using hooks in conditions and loops. Then, we learned about possible alternatives to hooks. React Hook implementation React Hook implementation

In React, the implementation is a little different. React replaces arrays with something like a single linked list. As you can see below, React generates a tree of components (or Fiber singly linked list). Fiber is essentially a virtual stack frame that the new scheduler schedules freely according to priority, changing the previously synchronous rendering to an asynchronous one and counting updates in segments without affecting the experience. Each node in the tree corresponds to a component. The hooks data is stored on these nodes as component information, and is born and dies with the component. The memoizedState array places the data in the order defined by the hooks, and If the hook order changes, memoizedState is not aware of it. We can only custom share the same memoizedState by calling Hook in the outermost layer of the function, and share the same order. Every time you re-render, the function component is re-executed, and nothing is done to the function component that has already been executed.

type Hooks = {
  memoizedState: any, // Point to the current render node Fiber
  baseState: any, // Initialize initialState, already newState after each dispatch
  baseUpdate: Update<any> | null.// The current Update is assigned after each UpdateUpdate, easy react render error edge, data backtracequeue: UpdateQueue<any> | null./ / UpdateQueue through
  next: Hook | null.// link to the next hooks, concatenate each hooks with next
}
type Effect = {
  tag: HookEffectTag, // effectTag marks which phase of life-Cycles the current hook is used in
  create: () = > mixed, // Initialize callback
  destroy: (() = > mixed) | null./ / uninstall the callback
  deps: Array<mixed> | null.next: Effect, / / same as above
};
Copy the code

A Hook function component is implemented differently the first time it is rendered and the next time it is rendered, and the hooks that the component calls actually point to different hooks. The Hook used by the function component for the first rendering points to the corresponding mountXXX, while the Hook used for the update points to the corresponding updateXXX, as shown below:

Class and Hook

In terms of the life cycle

Let’s put together a table for comparison

The class components Hooks components
constructor useState
getDerivedStateFromProps Update function in useState
shouldComponentUpdate useMemo
render The function itself
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount UseEffect is the function returned in useEffect
componentDidCatch There is no
getDerivedStateFromError There is no

In terms of code

The class components Hooks components
Clean code logic (constructors, componentDidMount, etc.) You need comments and variable names
Less prone to memory leaks Prone to memory leaks

Summary – Problem thinking:

  • React relates calls to hooks to components.

Hooks are essentially JavaScript functions. Never call hooks in loops, conditions, or nested functions. Make sure you always call them at the top of your React function.

  • How does React know which state corresponds to which useState?

React relies on the sequence of Hook calls. React correctly associates the internal state with the corresponding Hook as long as the order of Hook calls remains consistent across multiple renders.

  • Are hooks slowed down by creating functions at render time

Don’t. In modern browsers, the raw performance of closures and classes differs significantly only in extreme scenarios. In addition, you can argue that the Hook design is more efficient in some ways: Hooks avoid the extra overhead required by classes, such as the cost of creating class instances and binding event handlers into constructors. Language-compliant code does not require deep tree nesting of components when using hooks. This is common in code bases that use higher-order components, render props, and context. The smaller the component tree, the smaller the React workload.

  • Use useMemo?

This line of code calls computeExpensiveValue(a, b). But if the dependency array [a, b] hasn’t changed since the last assignment, useMemo skips the second call and simply reuses the value it returned last time. Think of useMemo as a means of performance optimization, but not as a semantic guarantee. In the future, React may choose to “forget” some previously remembered values and recalculate them at the next rendering, such as freeing memory for off-screen components. I recommend writing your own code to work without useMemo — and then adding it to performance tuning.

  • How to implement shouldComponentUpdate

A component can be wrapped with react. Memo to perform a shallow comparison of its props. This is not a Hook because it’s written differently than Hook. React.memo is equivalent to PureComponent, but it only compares props.

  • How to deal with frequent changes in effect dependencies?

Passing an empty dependency array [] means that the hook is run only once when the component is mounted, not re-rendered. The problem is that the value of count does not change during setInterval callbacks. Because when effect is executed, we create a closure and store the value of count in that closure with an initial value of 0. Every second, the callback executes setCount(0 + 1), so count never exceeds 1.

Specifying [count] as a dependency list fixes this Bug, but causes the timer to be reset every time a change occurs. In fact, each setInterval (similar to setTimeout) is called once before it is cleared. But that’s not what we want. To solve this problem, we can use the functional update form of setState. It allows us to specify how state should change without referring to the current state.

  • Interaction with the DOM

The basic way to get the position or size of a DOM node is to use a callback ref. React calls the callback whenever a ref is attached to another node. In this case, we chose not to use useRef because ref does not notify us of changes in the value of the current ref when ref is an object. Using callback ref ensures that even if the child component delays displaying the measured node (for example, in response to a click), we still receive relevant information in the parent component to update the measurement.

  • How do I get the props or state of the previous round

This can be implemented manually via ref, and given that this is a relatively common usage scenario, it is likely that React will come with a usePrevious Hook in the future.

  • Can a Hook cover all usage scenarios of a class

The official goal for Hooks is to cover all usage scenarios of class as soon as possible. There are currently no Hook equivalents for the less-used getSnapshotBeforeUpdate, getDerivedStateFromError, and componentDidCatch life cycles, but officials plan to add them soon. At present Hook is still in the early stage, some third-party libraries may not be compatible with Hook for the time being.

  • Hook, class, both?

We cannot use hooks inside a class component, but we can mix class components and function components that use hooks in the component tree. Whether a component is a class or a function that uses a Hook is just an implementation detail of that component. In the long run, officials expect Hooks to become the primary way we write React components.

reference

1. Official documents

2. Complete guide to useEffect

React Advanced components

4, Jane Hooks

5. Explore React — Fiber & Algebraic Effects

Use React. Memo to optimize the React application