One, foreword

I was twenty-one that day, and in the golden age of my life, I had so many hopes. I wanted to love, to eat, and to be in one moment a half-dark cloud in the sky. I felt that I could go on forever and nothing could hammer me.

Golden Age by Wang Xiaobo

B. Hooks

Hook is a new feature in React 16.8. It lets you use state and other React features without having to write a class. The essence of a Hook is still a JavaScript function, but there are two rules to follow when using it

Use hooks only at the top level

Never call hooks in loops, conditions, or nested functions. Be sure to always call them at the top of your React functions and before any returns.

Only the React function calls the Hook

Instead of calling a Hook in a normal JavaScript function, you can:

  1. Call a Hook in the React function component
  2. Call other hooks in custom hooks

Third, Hooks API

Hooks have the following apis, which are classified according to official documentation.

useState

The simplest hook function is to initialize a value, return the same state as initialState, and update the state function setState.

const [state, setState] = useState(initialState);
Copy the code

Let’s use useState

import React, { useState } from 'react';

const Index = () = > {
  // setNumber updates the number function
  const [number, setNumber] = useState(0);

  return (
    <>
      <div>{number}</div>
      <button onClick={()= > setNumber(number + 1)}>Add Number</button>
      <button onClick={()= > setNumber(number - 1)}>Sub Number</button>
    </>
  );
};

export default Index;
Copy the code

We can see that useState initializes a value of number to zero and returns an array containing the number itself and the function setNumber that changes the number and we change the number by binding the setNumber with two buttons. Implement the value of number plus one minus one.

useEffect

UseEffect is used to emulate the lifecycle functions of class components in function components. Takes two arguments, the first of which is an arrow function. The arrow function returns a function that is triggered when the component is unloaded. (here’s an example 🌰 : when we want to listen to the message of the page, we can listen after the render DOM, and remove the listener when dom remove.)

About the second parameter [DEps]

  1. When there is no second argument, whennumberWhen changes occur, the component will render after remove, sort of likeshouldComponentUpdateLife cycle.
  2. When the second argument is [], only the initial render is executed, which is equivalent to the life cyclecomponentDidMount
  3. When the second parameter is not null, only the parameters in the array are changed.
import React, { useEffect, useState } from 'react';

const Index = () = > {
  // setNumber updates the number function
  const [number, setNumber] = useState(0);
  useEffect(() = > {
    console.log('render dom');
    return () = > {
      console.log('dom remove');
    };
  }, [number]);

  return (
    <>
      <div>{number}</div>
      <button onClick={()= > setNumber(number + 1)}>Add Number</button>
      <button onClick={()= > setNumber(number - 1)}>Sub Number</button>
    </>
  );
};

export default Index;
Copy the code
useContext

UseContext receives a context object (the return value of react. createContext) and returns the current value of the context. The current context value is determined by the < MyContext.provider > value prop of the upper-layer component closest to the current component. In fact, useContext is similar to context.consumer. The use of context.consumer is mentioned here

<MyContext.Consumer>
  {value= >  {/* Render based on context value */}}
</MyContext.Consumer>
Copy the code

We use ThemeContext to receive a context object, using const Theme = useContext(ThemeContext); To subscribe to it, the theme value returned is the value prop of the closest < themecontext.provider value={themes.dark}> to the current component. So the theme rendered below is the dark theme.

import React, { useContext } from 'react';

const themes = {
  light: {
    foreground: '# 000000'.background: '#eeeeee',},dark: {
    foreground: '#ffffff'.background: '# 222222',}};const ThemeContext = React.createContext();

function Index() {
  return (
    // Pass the light theme here
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background.color: theme.foreground}} >
      I am styled by theme context!
    </button>
  );
}

export default Index;
Copy the code
useReducer

UseReducer is an alternative to useState, which receives a reducer of the form (state, action) => newState (like redux). In some cases, useReducer is more useful than useState. For example, the state logic is complex and contains multiple subvalues. The following is an example of a counter that was rewritten using reducer in the useState section.

import React, { useReducer } from 'react';

function reducer(state, action) {
  console.log(state, action);
  switch (action.type) {
    case 'add':
      return { count: state.count + 1 };
    case 'sub':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Index() {
  // return 

1

;
const [state, dispatch] = useReducer(reducer, { count: 0 }); return ( <> <h1>count:{state.count}</h1> <button onClick={()= > dispatch({ type: 'add' })}>+</button> <button onClick={()= > dispatch({ type: 'sub' })}>-</button> </> ); } export default Index; Copy the code
useCallback

UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps). It can be seen that it is used for caching functions. The following are the optimization suggestions (marked with CR) put forward by my mentor in reviewcode.

const TreeDemo = () = > {
  // when functions are props for subcomponents, use useCallback;
  // const onSelect = (selectedKeys) => {
  // console.log('selected', selectedKeys);
  / /};

  // const onCheck = (checkedKeys) => {
  // console.log('onCheck', checkedKeys);
  / /};

  //reviewcode
  const onSelect = useCallback(selectedKeys= > {
    console.log('selected', selectedKeys);
  });

  const onCheck = useCallback(checkedKeys= > {
    console.log('onCheck', checkedKeys);
  });

  // cr: The default value is controlled by state. If there is an external value, the external value is preferred
  return (
    <Tree
      checkable// Expands the specified tree node by defaultdefaultExpandedKeys={['0-0', '0-0-0', '0-0 to 1']} // The tree node selected by defaultdefaultSelectedKeys={['0-0']} // The tree node of the checkbox is selected by defaultdefaultCheckedKeys={['0-0 to 1', '0-0-0 to 1']}
      onSelect={onSelect}
      onCheck={onCheck}
      treeData={treeData}
    />
  );
};
Copy the code
useMemo

UseMemo is used to cache data. For example, sum is a parameter that needs to be calculated. If we do not cache the memo, then we will recalculate sum when updating the rank. This optimization helps avoid costly calculations every time you render.

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

function Index() {
  const [count, setCount] = useState(100);
  const [rank, setRank] = useState(1);

  let sum = useMemo(() = > {
    console.log('calculate');
    let sum = 0;
    for (let i = 0; i <= count; i++) {
      sum += i;
    }
    return sum;
  }, [count]);

  return (
    <>
      <h1>{rank}</h1>
      <button onClick={()= > setRank(rank + 1)}>Add Rank</button>
      <h5>{sum}</h5>
      <button onClick={()= > setCount(count + 1)}>Add Count</button>
    </>
  );
}

export default Index;
Copy the code
useRef

UseRef is used to get element DOM nodes, and there are other methods

  1. Ref ={node => this.node = node}
  2. Use the React. CreateRef () API
import React, { useRef } from 'react';

function Index() {
  const node = useRef(null);
  const getNode = () = > {
    // Prints the node
    console.log(node.current);
  };
  return (
    <div>{/* Notice the binding */}<h1 ref={node}>Node</h1>
      <button onClick={()= > getNode()}>Get Node</button>
    </div>
  );
}

export default Index;
Copy the code
useImperativeHandle

UseImperativeHandle allows you to customize the instance value exposed to the parent component when using ref. Let’s look at a parent component calling the child’s Focus function

import React, { forwardRef, useImperativeHandle, useRef } from 'react';

function Input(props, ref) {
  console.log(props, ref);
  const inputRef = useRef(null);


  useImperativeHandle(
    ref,
    () = > {
      const handleFn = {
        focus(){ inputRef.current.focus(); }};returnhandleFn; } []);return <input type='text' ref={inputRef} />;
}

const UseInput = forwardRef(Input);

function Index() {
  const current = useRef(null);
  // console.log(current);
  const handleClick = () = > {
    console.log(current);
    const { focus } = current.current;
    focus();
  };

  return (
    <div>
      <UseInput ref={current} />
      <button onClick={handleClick}>Get focus</button>
    </div>
  );
}

export default Index;
Copy the code

In the example above, React passes the current of the

element as the second argument to the render Input of the react. forwardRef function. Input passes current to the first parameter ref of useImperativeHandle. Note: instead of passing current to
, we use inputRef to take the DOM instance of
and operate it in useImperativeHandle. Instead, useImperativeHandle is used to broker the operation. The useImperativeHandle exposes methods for our use.

useLayoutEffect

Its function signature is the same as useEffect, but it calls Effect synchronously after all DOM changes. You can use it to read DOM layouts and trigger rerenders synchronously. The update schedule inside useLayoutEffect is refreshed synchronously before the browser performs the drawing. This means that the useLayoutEffect callback is executed before the browser draws, so the useLayoutEffect callback code may prevent the browser from rendering.

import React, { useEffect, useLayoutEffect, useState } from 'react';

function Index() {
  const [color, setColor] = useState(0);

  useLayoutEffect(() = > {
    console.log('render');
    if (color === 0) {
      setColor(color + 1);
    }
  }, [color]);

  const colorStyle = {
    color: color ? 'yellow' : 'red'};return (
    <>
      <div style={colorStyle}>color text {color}</div>
      <button onClick={()= > setColor(0)}>Click</button>
    </>
  );
}

export default Index;
Copy the code

As we can see from the example, when the Click button is clicked, the view stays yellow because the useLayoutEffect callback blocks the browser rendering. It’s only after the callback is done, setColor(color + 1), that the browser renders and the view stays yellow. When we change useLayoutEffect to useEffect, The flash occurs when we quickly Click on the Click button, because useEffect does not block the browser rendering, and when we Click serColor(0), the view is updated to red. After the useEffect callback setColor(color + 1) is executed, it turns yellow again, so a flicker occurs.

useDebugValue
useDebugValue(value)
Copy the code

UseDebugValue can be used to display tags for custom hooks in the React developer tool

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // Display the label next to the Hook in the developer tools
  // e.g. "FriendStatus: Online"
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}
Copy the code

prompt

We do not recommend that you add debug values to every custom Hook. It is most valuable when it is part of a shared library.

useTransition

The API is in an experimental phase and will be updated later.