Hook profile

The problems with React before Hook came along

  1. It is difficult to reuse state logic between components

    React does not provide a way to “attach” reusability behavior to components (e.g., connect components to stores). There are several solutions to this problem, such as Render props and high-level components. But such schemes require reorganizing your component structure, which can be cumbersome and make your code difficult to understand.

  2. Complex components become difficult to understand

    Components often get data in componentDidMount and componentDidUpdate. However, the same componentDidMount may also contain a lot of other logic, such as setting event listeners, which will later be cleared in componentWillUnmount. Code that is related to each other and needs to be modified by comparison is split, while completely unrelated code is grouped together in the same way. This is prone to bugs and leads to inconsistencies in logic.

  3. Unintelligible class

    Class is a big barrier to learning React. You have to understand how this works in JavaScript, which is very different from other languages. Don’t forget to bind the event handler as well. Without a stable syntax proposal, this code is very redundant. You can understand props, state, and the top-down data flow, but you can’t understand class.

The solution brought by Hook

  1. You can use hooks to extract state logic from components so that the logic can be tested separately and reused. Hooks let you reuse state logic without modifying the component structure.
  2. Hooks break down the interrelated parts of a component into smaller functions (such as setting subscriptions or requesting data), rather than forcing a lifecycle partition. You can also use Reducer to manage the internal state of components to make them more predictable.
  3. Hooks allow you to use more React features in non-class situations. Conceptually, React components have always been more like functions. Hook, on the other hand, embraces functions without sacrificing React’s ethos. Hooks provide solutions to problems without the need to learn complex functional or responsive programming techniques.

Hook API

useState

UseState is a hook function in react. It is used to declare state variables. The useState function takes our initial state as an argument and returns an array with the current state value [0] and method functions that can change the state value [1].

Initialize the

// Return a state and update the state function setState(receives a new state value and queues a rerendering of the component)
const [state, setState] = useState(initialState);
Copy the code

Functional update

// If the new state needs to be calculated using the previous state, you can pass the function to setState. This function will take the previous state and return an updated value.
function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={()= > setCount(initialCount)}>Reset</button>
      <button onClick={()= > setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={()= > setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}
Copy the code

Lazy initial state

// If the initial state needs to be calculated by a complex calculation, you can pass in a function that evaluates and returns the initial state. This function is called only for the initial rendering
const [state, setState] = useState(() = > {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
Copy the code

Skip state updates

When the update function of State Hook is called and the current State is passed in, React will skip the rendering and effect execution of the child component. (React uses the object.is comparison algorithm to compare states.)

useEffect

When we write stateful components, there are many side effects, such as making Ajax requests to get data, adding some listener registration and unregistration, manually modifying the DOM, and so on. We used to write these side effects in lifecycle hooks such as componentDidMount, componentDidUpdate, and componentWillUnmount. Now useEffect is a collection of these declarative periodic function hooks. It’s one for three.

A simple example

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() = > {
    // Update the document title
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()= > setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
Copy the code

Clear effect

Typically, resources such as subscription or timer ids created by Effect need to be cleared when a component is unloaded. To do this, the useEffect function returns a clear function. Here is an example of creating a subscription:

useEffect(() = > {
  const subscription = props.source.subscribe();
  return () = > {
    // Clear the subscription
    subscription.unsubscribe();
  };
});
Copy the code

To prevent memory leaks, the cleanup function is executed before the component is unloaded. In addition, if the component is rendered multiple times (as it usually is), the previous effect will be cleared before the next effect is executed.

Timing of effect execution

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect is deferred after the browser has finished laying and drawing. This makes it suitable for many common side effect scenarios, such as setting subscriptions and event handling, so you should not perform operations in functions that block the browser from updating the screen.

Conditional execution of effect

By default, effect is executed after each turn component is rendered. This way, the effect will be recreated once its dependency changes. In some cases, instead of creating a new subscription every time a component is updated, we simply need to recreate it whenever the Source Prop changes. To do this, you pass a second argument to useEffect, which is an array of values on which effect depends.

// At this point, the subscription will be recreated only if the props. (To implement componentDidMount, just set the second parameter to [])
useEffect(
  () = > {
    const subscription = props.source.subscribe();
    return () = > {
      subscription.unsubscribe();
    };
  },
  [props.source],
);
Copy the code

useContext

Values can be passed deep into the component, and the parent component to the descendant component. Receives a context object (the return value of React.createcontext) and returns the current value of the context. The value of the current context is determined by the value prop of < myContext. Provider> nearest to the current component in the upper-layer components.

When the latest < myContext. Provider> update is made to the component’s upper layer, the Hook will trigger a rerender, using the last context value passed to MyContext Provider. Even if the ancestor uses React.memo or shouldComponentUpdate, it will be rerendered when the component itself uses useContext.

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

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

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

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

useReducer

An alternative to useState that can be used for complex state processing. It receives a Reducer of the form (state, action) => newState and returns the current state and its corresponding dispatch method. (If you’re familiar with Redux, you already know how it works.)

Specify initial state

There are two different ways to initialize the useReducer State, and you can choose one of them depending on the usage scenario. It’s easiest to pass the initial state as the second argument to useReducer:

//nst [state, dispatch] = useReducer(reducer, initialArg, init);
 const [state, dispatch] = useReducer(
    reducer,
    {count: initialCount}
 );
Copy the code

In some cases, useReducer may be more applicable than useState, for example, the state logic is complex and contains multiple subvalues, or the next state depends on the previous state. Also, using useReducer allows performance optimization for components that trigger deep updates because you can pass dispatches to child components instead of callbacks.

const initialState = {count: 0};

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
      <button onClick={()= > dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Copy the code

Lazy initialization

You can optionally create the initial state lazily. To do this, you need to pass in the init function as the third argument to useReducer, so that the initial state will be set to init(initialArg).

This allows the logic used to calculate state to be extracted from the reducer outside of the reducer. This also makes it easier to redo the state action in the future:

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={()= > dispatch({type: 'reset', payload: initialCount})}>
        Reset
      </button>
      <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
      <button onClick={()= > dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Copy the code

Skip the dispatch

React will skip renderings of children and side effects if the Reducer Hook returns the same value as the current state. (React uses the object.is comparison algorithm to compare states.)

useMemo

Pass the “create” function and dependency array to useMemo as parameters, and it will only recalcalculate memoized if one of the dependencies changes. This optimization helps to avoid high overhead calculations every time you render. If an array of dependencies is not provided, useMemo evaluates the new values each time it renders. The memo is a shallow comparison, which means that the object only compares the memory address. As long as the memory address does not change, the value in your object will not trigger render.

** You can use useMemo as a performance optimization tool, but don’t use it as a semantic guarantee. ** In the future, React may choose to “forget” some of the previous memoized values and recalculate them on the next render, such as freeing up memory for off-screen components. Write code that can execute without useMemo first — then add useMemo to your code to optimize performance.

const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]); 
Copy the code

useCallback

Passing the inline callback function and the array of dependencies as arguments to useCallback returns a memoized version of the callback function that is updated only if one of the dependencies changes. This is useful when you pass callback functions to child components that have been optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).

UseMemo is similar to useCallback in that it has the function of caching. UseMemo caches values.

UseCallback (fn, deps) equals useMemo(() => fn, deps).

const memoizedCallback = useCallback(
  () = > {
    doSomething(a, b);
  },
  [a, b],
);
Copy the code

useRef

UseRef returns a mutable ref object whose.current property is initialized to the passed parameter (initialValue). The returned REF object remains the same throughout the lifetime of the component.

The value of state in useEffect is fixed. The value of state in useEffect is fixed. The value of state in useEffect is fixed in useRef.

Essentially, useRef is like a “box” that you can hold a mutable value in its.current property. You should be familiar with the REF as the main way to access the DOM. If you pass the ref object into the component as

, React sets the.current property of the ref object to the corresponding DOM node, regardless of how the node changes. However, useRef() is more useful than the ref attribute. It can easily hold any variable value, similar to the way instance fields are used in a class.

Remember, useRef does not notify you when the contents of the ref object change. Changing the.current property does not cause the component to rerender. If you want to run some code when React binds or unbinds a DOM node’s REF, you need to use a callback ref to do so.

const Hook =() = >{
    const [count, setCount] = useState(0)
    const btnRef = useRef(null)

    useEffect(() = > {
        console.log('use effect... ')
        const onClick = () = >{
            setCount(count+1)
        }
        btnRef.current.addEventListener('click',onClick, false)
        return () = > btnRef.current.removeEventListener('click',onClick, false)
    },[count])

    return(
        <div>
            <div>
                {count}
            </div>
            <button ref={btnRef}>click me </button>
        </div>)}Copy the code

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])
Copy the code

UseImperativeHandle lets you customize the instance value exposed to the parent component when using ref. In most cases, imperative code like ref should be avoided. UseImperativeHandle should be used with the forwardRef:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () = > ({
    focus: () = >{ inputRef.current.focus(); }}));return <input ref={inputRef} . />;
}
FancyInput = forwardRef(FancyInput);
Copy the code

In this case, the parent component rendering
can call inputref.current.focus ().

Customize the Hook

A custom Hook is a function named”use“, other hooks can be called from within the function.

For example, the following useFriendStatus is our first custom Hook:

import { useState, useEffect } from 'react';

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

  useEffect(() = > {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () = > {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
Copy the code

Define a custom hook that listens for the width and height of the window when resize

import {useEffect, useState} from "react";

export const useWindowSize = () = > {
    const [width, setWidth] = useState()
    const [height, setHeight] = useState()

    useEffect(() = > {
        const {clientWidth, clientHeight} = document.documentElement
        setWidth(clientWidth)
        setHeight(clientHeight)
    }, [])

    useEffect(() = > {
        const handleWindowSize = () = >{
            const {clientWidth, clientHeight} = document.documentElement
            setWidth(clientWidth)
            setHeight(clientHeight)
        };

        window.addEventListener('resize', handleWindowSize, false)

        return () = > {
            window.removeEventListener('resize',handleWindowSize, false)}})return [width, height]
}
Copy the code

Use:

const [width, height] = useWindowSize()
const isOnline = useFriendStatus(id);
Copy the code

More and more

  • Summary of Web development knowledge

Refer to the article

  • 30 minutes proficiency in React Hooks
  • I finally got the React Hooks
  • React Hooks official document
  • React Hooks FAQ
  • React Hook best practices
  • Customize the Hook
  • About React Hooks use
  • Vue Composition API versus React Hooks
  • Immer.js practical explanation document
  • React Hooks # 2: Send requests