Handwritten React Hooks

  • Hooks are new in React 16.8 that allow you to use state and other React features without writing a class
  • The React API that starts with use is Hooks

What is the Hook

Hook is a special function that lets you “Hook” into the React feature. For example, useState is a Hook that allows you to add state to the React function component.

Why use Hooks

Quote official website description

  • Reusing state logic between components is difficult

    You might want to use render props or HOC, but both render properties and higher-order components have a parent container (typically div elements) wrapped around the original component. If you look at React apps in React DevTools, you’ll see that components made up of providers, consumers, high-order components, render props, and other abstraction layers form a nested hell.

  • Complex components become difficult to understand

    Components usually get data in componentDidMount and componentDidUpdate. However, the same componentDidMount may also contain a lot of other logic, such as setting up event listeners that need to be cleared later in componentWillUnmount. Code that is related and needs to be modified against each other is split, while completely unrelated code is grouped together in the same method. It’s easy to bug

  • Hard to understand class

    This points to the problem: when a parent passes a function to a child, this must be bound

Rules of the Hook

  • Call a Hook only from the outermost layer inside a function, never from a loop, conditional judgment, or child function
  • Only use the React function to call a Hook. Use the React function component to call other custom hooks

Use ESLint to do hooks rule checking

Use eslint-plugin-react-hooks to check for code errors

{
  "plugins": ["react-hooks"].// ...
  "rules": {
    "react-hooks/rules-of-hooks": 'error'.// Check the Hook rules
    "react-hooks/exhaustive-deps": 'warn' // Check for effect dependencies}}Copy the code

useState

UseState returns an array: a state, and a function to update the state.

This is similar to the class component’s this.setstate, but it does not merge the new state with the old state, instead replacing it directly

// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;

function useState(initialState) {
  hookStates[hookIndex] = hookStates[hookIndex] || initialState;
  // Use closures to maintain function call locations
  let currentIndex = hookIndex;
  function setState(newState) {
    // Check whether the passed state is a function, if prevState is passed
    if (typeof newState === "function") {
      // copy it again to newState
      newState = newState(hookStates[hookIndex]);
    }
    / / update the state
    hookStates[currentIndex] = newState;
    // Triggers a view update
    render();
  }
  // Return an array. Destruct can be written as any variable
  return [hookStates[hookIndex++], setState];
}
Copy the code

useEffect

UseEffect is an Effect Hook that gives function components the ability to manipulate side effects. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in the Class component, but has been consolidated into an API

Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect do not block the browser update view, making your application seem more responsive. In special cases (such as measuring layout), there is a separate useLayoutEffect Hook, used in the same way as useEffect

// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;

function useEffect(callback, dependencies) {
  if (hookStates[hookIndex]) {
    // Non-initial call
    let lastDependencies = hookStates[hookIndex];
    // Determine whether the incoming dependency is the same as the last one
    let same = dependencies.every(
      (item, index) = > item === lastDependencies[index]
    );
    if (same) {
      hookIndex++;
    } else{ hookStates[hookIndex++] = dependencies; callback(); }}else {
    // Initial callhookStates[hookIndex++] = dependencies; callback(); }}Copy the code

useMemo

Allows you to cache calculations between multiple renders by “remembering” the last one

It makes it easier to control when specific child nodes are updated, reducing the need for pure components

// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;

function useMemo(factory, dependencies) {
  if (hookStates[hookIndex]) {
    / / not for the first time
    let [lastMemo, lastDependencies] = hookStates[hookIndex];

    // Determine whether the incoming dependency is the same as the last one
    let same = dependencies.every(
      (item, index) = > item === lastDependencies[index]
    );
    if (same) {
      hookIndex++;
      return lastMemo;
    } else {
      // If only one dependent variable is different
      let newMemo = factory();
      hookStates[hookIndex++] = [newMemo, dependencies];
      returnnewMemo; }}else {
    // First call
    let newMemo = factory();
    hookStates[hookIndex++] = [newMemo, dependencies];
    returnnewMemo; }}Copy the code

useCallback

ShouldComponentUpdate allows you to keep the same callback reference between rerenders to keep shouldComponentUpdate working

// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;

function useCallback(callback, dependencies) {
  if (hookStates[hookIndex]) {
    / / not for the first time
    let [lastCallback, lastDependencies] = hookStates[hookIndex];

    let same = dependencies.every(
      (item, index) = > item === lastDependencies[index]
    );
    if (same) {
      hookIndex++;
      return lastCallback;
    } else {
      // If only one dependent variable is different
      hookStates[hookIndex++] = [callback, dependencies];
      returncallback; }}else {
    // First call
    hookStates[hookIndex++] = [callback, dependencies];
    returncallback; }}Copy the code

memo

function memo(OldFunctionComponent) {
  return class extends React.PureComponent {
    render() {
      return <OldFunctionComponent {. this.props} / >; }}; }Copy the code

useContext

Receiving a context object (the return value of react.createcontext) and returning the current value of that context useContext(MyContext) simply allows you to read the value of the context and subscribe to changes to the context. You still need to use < myContext.provider > in the upper component tree to provide context for the lower component

function useContext(context) {
  return context._currentValue;
}

/ / the parent component
const CountCtx = React.createContext();
function ParentComp() {
  const [state, setState] = React.useState({ number: 0 });
  return (
    <CountCtx.Provider value={{ state.setState}} >
      <Child />
    </CountCtx.Provider>
  );
}

/ / child component
function Child() {
  let { state, setState } = useContext(CountCtx);
  return (
    <div>
      <p>{state.number}</p>
      <button onClick={()= > setState({ number: state.number + 1 })}>
        add
      </button>
    </div>
  );
}
Copy the code

useRef

UseRef returns a mutable ref object whose current property is initialized as the parameter passed in. The ref object returned by useRef remains constant throughout the life of the component, that is, every time a function component is rerendered, The ref object returned is the same (note the use of react. createRef, which recreates the ref each time the component is rerendered)

let lastRef;

function useRef(value) {
  lastRef = lastRef || { current: value };
  return lastRef;
}
Copy the code

useReducer

The Reducer in useReducer and Redux is like the reducer in useState

// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;

function useReducer(reducer, initialState) {
  hookStates[hookIndex] = hookStates[hookIndex] || initialState;

  let currentIndex = hookIndex;
  function dispatch(action) {
    hookStates[currentIndex] = reducer
      ? reducer(hookStates[currentIndex], action)
      : action;
    // Triggers a view update
    render();
  }
  return [hookStates[hookIndex++], dispatch];
}

// useState can be overwritten using useReducer
function useState(initialState) {
  return useReducer(null, initialState);
}
Copy the code

reference

Rules of the Hook

React Hooks 【 nearly 1W words 】+ project combat

recommended

Get child component instance values from the React Hooks parent component

Elegant use of useRef in React Hooks