Original is not easy, I hope you can pay attention to us, and then easily point a praise ~~

React Hooks: You can also play with them after reading this

Partial name mappings that appear in this article:

Function Component => Function Component

Class Component => Class Component

Utility functions => Util Function

React Hook => React Hook

InitialValue => initialValue

First the concept

React V16.7.0-Alpha introduced the concept of Hooks for the first time in v16.8.0. React Hooks. React Hooks. React Hooks.

React Hook is a special function that can be either a functional component (returning the Dom or Dom and State) or a utility function (passing in configuration items and returning encapsulated data processing logic).

To summarize

Function components have been transformed by the introduction of React Hooks. The biggest change is that they are given the concept of a life cycle similar to that of a class component, extending the scope of functional components.

The basic functional components used for pure components, once the functional components coupled with business logic, will need to pass through the Props, triggered by subcomponents parent component approach to implement the business logic, the emergence of the Hooks make function components also has its own state and business logic, simple logic in their internal processing. This method eliminates the need to pass Props to enable simple logic components to be removed, and enables users to care less about the internal logic of the component and only about the result returned by the Hooks component.

In my view, the goal of the Hooks component is not to replace class components, but to increase the use of functional components, to clarify the boundary between generic utility functions and business utility functions, and to encourage developers to encapsulate business generic logic as React Hooks instead of utility functions.

The reason for putting this summary in front of you is to give you a general idea of what the React Hooks specifically do to functional components.

Hooks first

Official hooks

Currently, the official hooks are divided into two types: basic hooks and extended hooks

The basic hooks are useState, useEffect, and useContext

Additional hooks are: useCallback, useReducer, useMemo, useRef, useLayoutEffect, useImperativeHandle, useDebugValue

Different hook usage

useState

This hook is used to create a new state that takes either a fixed value or a method that returns a value. The result of the hook execution is an array of generated states and methods to change that state. The corresponding values and methods are retrieved by deconstructing the assignment method.

The usage method is as follows:

export default function HookDemo() {
  const [count, changeCount] = useState(0);

  return (
    <div>
      {count}
      <button onClick={()= >{ changeCount(Math.ceil(Math.random() * 1000)); }} > change the count</button>
    </div>
  );
}
Copy the code

useEffect

As the name implies, the side effect hooks are implemented. It is mainly used in the following two situations:

  1. Functional components do not have the concept of a traditional class component lifecycle, which we must use if we need to do something after some particular lifecycle or value changeuseEffectSome of the features to implement.
  2. useStateThe resulting changeState method does not provide similarsetStateSo if you need to execute some method after State changes, you must passuseEffectThe implementation.

The hook takes two arguments. The first argument is the callback that the side effect needs to execute. The generated callback method can return a function that will be run when the component is unloaded. The second is the array of states that are listened for for the side effect, which is executed when the corresponding State changes or, if the second parameter is empty, for each State change.

The usage method is as follows:

const [count, changeCount] = useState(0);

// The latest count data will be printed when count changes
useEffect((a)= > {
  message.info('count has changed. The latest value is${count}`);
}, [count])
Copy the code

In the code above, we implemented the second case of the useEffect hook. How can we use this hook to implement something similar to the lifecycle of a class component? Since the first argument is the callback performed by the side effect, it is the second argument that should be the focus of implementing our desired functionality.

ComponentDidMount && componentWillUnmout: These two life cycles are executed only once after the page is mounted/unmounted. As mentioned earlier, all side effects are executed once after the component is mounted, and if there is a return function for the side effect, the return function will be run at unmount time. All we need to do with this feature is make it so that the target side effect is never called again after the initial execution, so as long as the state associated with the side effect is empty, no matter how the other states change, the side effect will never be executed again. Implement componentDidMount and componentWillUnmout.

import React, { useState, useEffect } from 'react';
import { message } from 'antd';

function Child({ visible }) {
  useEffect((a)= > {
    message.info('I only print when the page is mounted.');
    return (a)= > {
      message.info('I only print when the page unloads'); }; } []);return visible ? 'true' : 'false';
}

export default function HookDemo() {
  const [visible, changeVisible] = useState(true);


  return (
    <div>
      {
        visible && <Child visible={visible} />
      }
      <button onClick={()= >{ changeVisible(! visible); }} > change the visible</button>
    </div>
  );
}
Copy the code

ComponentDidUpdate: This life cycle is called after every page update. According to the previous logic, all states should be placed in the second state, but adding/removing a state requires changing the second parameter. In fact, if the second argument is null and the side effect is executed on every State change, it’s pretty easy to implement componentDidUpdate.

useEffect((a)= > {
  / /... Side effect logic
}) // Note that the empty state of the association does not mean that the second argument is not passed, but that the second argument should be an empty array
Copy the code

In a class component, if setState is called multiple times in componentDidMount to set a value (which is certainly not recommended) and printed in a successful callback, the end result is likely to print many of the same last-set values. Because the setState of a class is the result of a class asynchracy, they collect all changes and assign them uniformly at the appropriate time.

In useEffect, the values of all variables are retained at the time the side effect is executed. This is similar to the let or closure of a for loop. All variables are retained at the time the side effect is executed.

useCallback

The hook that generates the Callback. It is used to encapsulate the same logic in different useeffects to reduce code redundancy and cooperate with useEffect.

This hook can be better understood by looking at an example:

const [count1, changeCount1] = useState(0);
const [count2, changeCount2] = useState(10);

const calculateCount = useCallback((a)= > {
  if (count1 && count2) {
    return count1 * count2;
  }
  return count1 + count2;
}, [count1, count2])

useEffect((a)= > {
    const result = calculateCount(count, count2);
    message.info('Execute side effect, latest value is${result}`);
}, [calculateCount])
Copy the code

In the example above, we generate a callback by using useCallback. The use method of useCallback is the same as useEffect. The first parameter is the generated callback method, and the second parameter is the state associated with the method.

Using the code above, we associate the value of count1 / count2 with a method called calculateCount, which is called if a component’s side effect is to calculate the value of count1 and count2.

The difference between using useEffect directly is that after generating a calculated callback using useCallback, the second argument in the side effect of using the callback should be the generated callback. We use useCallback to generate a callback method associated with count1 / count2. When the state of the association changes, a new callback is generated. The side effect listens for the change of the callback and then re-executes the side effect. At this point, the use allback and useEffect are executed sequentially, thus separating the side effect logic.

useRef

UseRef takes a single parameter, the initial value of ref. Similar to the createRef method in a class component, this hook returns an object whose current field is the instance/saved variable we point to, and can retrieve the target node instance or save the state.

useRefSaved variables are not regenerated with each change in data, but remain as they were when we last assigneduseCabllbackuseEffectWe can achievepreProps/preStateThe function.

const [count, changeCount] = useState(0);
const [count1, changeCount1] = useState(0);
// Create a prestate for an object whose initial value is null
const preState = useRef({});
// You can use preState first and save the latest state data last
useEffect((a)= > {
  const{... } = preState.current;if (// select ();
    / / logic
  }
  // Save the latest state
  preState.current = {
    count,
    count1,
  }
});
Copy the code

In addition, when we assign the state created using useState to useRef for initialization, manually changing the value of Ref does not cause the associated state to change. It appears that useRef simply creates a heap space in memory to store the initialized value in a different memory space from the initialized value, and changing the Ref value does not change the view.

export default function HookDemo() {
  const [count] = useState({ count: 1 });
  
  const countRef = useRef(count);

  return (
    <div>
      {count.count}
      <button onClick={()= >{ countRef.current.count = 10; }} > change the ref</button>
    </div>
  );
}
Copy the code

useMemo

Memo is short for Memory. UseMemo uses the contents of Memory. This hook is mainly used for performance optimization.

We said earlier that useeffects without the associated state are all executed when the state changes. Similarly, a value computed or an imported component is recalculated/mounted, even if its associated state has not changed.

We have shouldComponetUpdate and React.memo to help us optimize performance. If we don’t have a similar function in a function, it’s against the official purpose, hence the useMemo hook.

In business, we can use useMemo to handle caching of computed results or to introduce anti-remount optimizations for components. It takes two arguments. The first is a Getter method that returns the data or component to be cached, and the second is the state associated with the return value. When either state changes, the Getter method is called again to generate a new return value.

The specific code is as follows:

import React, { useState, useMemo } from 'react';
import { message } from 'antd';

export default function HookDemo() {
  const [count1, changeCount1] = useState(0);
  const [count2, changeCount2] = useState(10);

  const calculateCount = useMemo((a)= > {
    message.info('Regenerate results');
    return count1 * 10;
  }, [count1]);
  return (
    <div>
      {calculateCount}
      <button onClick={()= >{ changeCount1(count1 + 1); Count1}} > change</button>
      <button onClick={()= >{ changeCount2(count2 + 1); Count2}} > change</button>
    </div>
  );
}

Copy the code

When we first accepted useMemo, we might have thought that the hook was just a cache of computed results, returning only a number or string. UseMemo doesn’t care about the type of our return value. It just calls the Getter we passed to generate a new return value when the state of the association changes. In other words, useMemo generates the relationship between the Getter and the dependent array. Therefore, if we replace the return value of the function with a component, then we can optimize the performance of mounting/remounting the component.

The code is as follows:

import React, { useState, useMemo } from 'react';
import { message } from 'antd';

function Child({ count }) {
  return <p>The current pass count is :{count}</p>;
}

export default function HookDemo() {
  const [count1, changeCount1] = useState(0);
  const [count2, changeCount2] = useState(10);

  const child = useMemo((a)= > {
    message.info('Regenerate Child component');
    return <Child count={count1} />;
  }, [count1]);
  return (
    <div>
      {child}
      <button onClick={()= >{ changeCount1(count1 + 1); Count1}} > change</button>
      <button onClick={()= >{ changeCount2(count2 + 1); Count2}} > change</button>
    </div>
  );
}
Copy the code

Other hooks

Today, we mainly talk about several commonly used hooks in components. Among the remaining hooks, such as useLayoutEffect useImperativeHandle useDebugValue, their functions are relatively simple and will not be described here.

Another important hook, useContext, is the implementation of the createContext function in a functional component. There are a lot of powerful things that can be done with this feature, which is the official Redux, and many of you are probably familiar with it. This hook is too much and is described in a separate section.

Write your own hooks

In fact, from the above explained content, hooks are not mysterious things, it is just some of our common logic encapsulation, next through the specific code to teach you to write your own hook.

The basic hook

The most basic hooks are those that return more logical State and change the State method. Take the counter for example, its most basic is to return the current number and reduce/increase/reset functions, after the function is clear, you can start to do.

import React, { useState } from 'react';

// Write our own hook with a name beginning with use
function useCounter(initialValue) {
  // Take the initialized value and generate state
  const [count, changeCount] = useState(initialValue);
  // Declare the reduction method
  const decrease = (a)= > {
    changeCount(count - 1);
  }
  // Declare the increment method
  const increase = (a)= > {
    changeCount(count + 1);
  }
  // Declare the reset counter method
  const resetCounter = (a)= > {
    changeCount(0);
  }
  // Return the count number and method
  return [count, { decrease, increase, resetCounter }]
}

export default function myHooksView() {
  // Use our own hook in the function component to generate a counter and get the objects of all operation methods
  const [count, controlCount] = useCounter(10);
  return (
  	<div>Current quantity: {count}<button onClick={controlCount.decrease}>To reduce</button>
			<button onClick={controlCount.increase}>increase</button>
			<button onClick={controlCount.resetCounter}>reset</button>
    </div>)}Copy the code

In the example above, we will create a state associated with the initialValue in the useCounter hook, create methods to reduce/increase/reset the initialValue, and finally return it back. In this way, the useCounter component packaging logic can be reused by calling this method to get the return value where other components need to use this function.

The demo effect is shown as follows:

Returns a hook for the DOM

Returning the DOM is actually the same as the most basic Hook logic, but there are some differences in the data content returned. Specifically, look at the code, take a Modal box as an example.

import React, { useState } from 'react';
import { Modal } from 'antd';

function useModal() {
  const [visible, changeVisible] = useState(false);

  const toggleModalVisible = (a)= >{ changeVisible(! visible); };return [(
    <Modal
      visible={visible}
      onOk={toggleModalVisible}
      onCancel={toggleModalVisible}
    >Popup window content</Modal>
  ), toggleModalVisible];
}

export default function HookDemo() {
  const [modal, toggleModal] = useModal();
  return (
    <div>
      {modal}
      <button onClick={toggleModal}>Open the popup window</button>
    </div>
  );
}

Copy the code

In this way, we have realized a Hook that returns the popover content and changes the popover display state. In fact, there are many more contents that can be encapsulated, and richer encapsulation can be achieved through the Settings of configuration items.

The demo effect is shown as follows:

Hook/Final summary

Hook to summarize

hook usage role
useState const [state, changeState] = useState(initialValue) Used to generateStates and ways to change them
useEffect useEffect(fn, [...relativeState]) Used to generateSide effects with state binding
useCallback useCallback(fn, [...relativeState]) Used to generateA state-bound callback function
useMemo useMemo(fn, [...relativeState]) Used to generateState-bound components/computed results
useRef const newRef = useRef(initialValue) Used to get node instance/data save

As you can see from the table above, except for the basic useState and useRef hooks, all hooks have a second parameter, and the execution of the first method is related to the second parameter. Therefore, we can conclude that in functional components that use hooks, we need to always pay attention to the performance optimization of the code when using side effects/reference subcomponents.

Final summary

Here’s what I said about React Hooks in my previous summary:

The goal of the Hooks component is not to replace class Component components, but to increase the use of functional components, to clarify the boundary between generic utility functions and business utility functions, and to encourage developers to encapsulate business generic logic as React Hooks instead of utility functions.

Hopefully, after reading this article, you have some ideas of your own.

, recruiting

Recruitment, front-end, belonging to the ZooTeam, more than 50 partners are waiting for you to join the wave. If you want to change what’s been bothering you, you want to start bothering you. If you want to change, you’ve been told you need more ideas, but you don’t have a solution. If you want change, you have the power to make it happen, but you don’t need it. If you want to change what you want to accomplish, you need a team to support you, but you don’t have the position to lead people. If you want to change the pace, it will be “5 years and 3 years of experience”; If you want to change the original savvy is good, but there is always a layer of fuzzy window… If you believe in the power of believing, believing that ordinary people can achieve extraordinary things, believing that you can meet a better version of yourself. If you want to be a part of the growth of a front end team that has deep business understanding, technology systems, value creation, and impact spillover as the business takes off, I think we should talk. Any time, waiting for you to write something and send it to [email protected]

Recommended reading

Vue component data communication scheme summary

Automated Web performance optimization analysis solution

CSS Stacking Context