React Hooks (examples and detailed explanation)

React Hooks are a new feature introduced in React 16.8 that allow the use of state and other React features in functional components. Here are some of the main ones

React Hooks:

  1. useState: is a React Hook that allows adding and managing state in functional components. Before this, only class components could have their own state. useState Returns an array containing the current state value and a function that updates the state.

Basic usage is as follows:

import React, { useState } from 'react';

function Example() {
  // 声明一个新的状态变量,其初始值为 0
  const [count, setCount] = useState(0);

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

In this example:

  • const [count, setCount] = useState(0); creates a new state variable count and a function that updates this state setCount.

  • When the user clicks the button, call setCount(count + 1) to update the status.

  • State changes will trigger the component to re-render, and the new state value will be reflected on the interface.

    Note that the setCount function does not modify the state immediately, but creates a new state object and schedules the component for re-rendering. This is because in React, all state updates are asynchronous.

    You can also pass a function to setCount to calculate the next state value, which ensures that the latest state is always used:

    <button onClick={() => setCount((prevCount) => prevCount + 1)}>
      Click me
    </button>
    

    Additionally, if the state is a complex data structure (such as an object or array) and you need to create a new state based on an existing state, you should use a functional updater to avoid state mutation problems.

  1. useEffect: is a React Hook that allows side-effect operations to be performed in functional components. These side effects may include data fetching, subscribing, manually changing the DOM output of the React component, etc. useEffect can be viewed as a combination of the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods.

Basic usage is as follows:

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

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

  // 使用 Effect Hook 来监听 count 状态的变化
  useEffect(() => {
    document.title = `You clicked ${count} times`;

    return () => {
      // 清理工作(可选)
    };
  }, [count]); // 当依赖项数组中的值改变时,重新运行 effect

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

In this example:

  • useEffectReceives two parameters: a function containing the side-effect code to be executed and an array of dependencies.

  • When the component is rendered and mounted on the DOM, the side effect function in useEffect will be called once. This is similar to the componentDidMount lifecycle methods.

  • When the value in the dependency array changes, the side-effect function in useEffect will be called again. This is similar to the componentDidUpdate lifecycle methods.

  • If a cleanup function is specified to be returned, then when the component is uninstalled or a dependency change causes the effect to be recalculated, this cleanup function will be executed before the next effect is run. This is similar to the componentWillUnmount lifecycle methods.

    Note that if the second parameter (the dependencies array) is omitted, the effect will only run when the component is mounted and unmounted, not after each render. This can be used to simulate the componentDidMount and componentWillUnmount lifecycle methods.

  1. useContext: is a React Hook that allows access to context objects in functional components. The React context API allows data to be passed around the component tree without having to manually go through each level of props.

Basic usage is as follows:

import React, { createContext, useContext } from 'react';

// 创建一个新的 context 对象
const ThemeContext = createContext();

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

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

function ThemedButton() {
  // 使用 useContext 获取当前的 theme 值
  const theme = useContext(ThemeContext);

  return <button style={
   
   { background: theme }}>Click me</button>;
}

In this example:

  • First creates a new context object ThemeContext.

  • Then use in the App component to provide the context value. <ThemeContext.Provider>

  • In the child component ThemedButton, we use useContext(ThemeContext) to get the current context value (i.e. theme).

    useContextWhat is returned is the value of the current context. When the value of context changes, all components subscribed to this context will be re-rendered.

    Note that if you use without wrapping <Provider> , the default value of context will be returned. Additionally, to avoid unnecessary re-rendering, you should place calls to as close as possible to the component that needs to use these values. useContextuseContext

  1. useReducer: is a Hook in React that allows managing complex, reusable logic in functional components. useReducer Receives a reducer function and initial state as parameters, and returns the current state and the dispatch method for updating the state.

Basic usage is as follows:

import React, { useReducer } from 'react';

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, 0);

  return (
    <>
      Count: {state}
      <button onClick={() => dispatch({ type: 'increment' })}>
        +
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        -
      </button>
    </>
  );
}

In this example:

  • We first define a reducer function, which receives two parameters: the current state and an action object. Depending on the action type, we can decide how to update the state.

  • In the Counter component, we use useReducer to create a new state variable (initialized to 0) and a dispatch method. The dispatch method is used to send an action to the reducer to update the state.

  • When the user clicks the button, we send the corresponding increase or decrease action object to the reducer through the dispatch method, and then the state will be updated accordingly.

    useReducer can replace multiple useState calls, especially if your component needs to maintain multiple related state values. Additionally, since reducers are pure functions, this makes testing easier.

  1. useCallback:` is a Hook in React, which is used to optimize performance. When you use inline callbacks (such as event handlers) in a function component, a new function instance is created with each render. This may cause unnecessary re-rendering of child components, even if they don't rely on these callbacks.

    useCallbackReceives two parameters: a callback function that requires memoize and an array of dependencies. The returned memoized callback function is updated when the values ​​in the dependencies array change; otherwise, it returns the previously cached version.

Basic usage is as follows:

import React, { useCallback } from 'react';

function Example({ a, b }) {
  // 使用 useCallback 来记住计算的值
  const memoizedCallback = useCallback(
    () => {
      console.log(a + b);
    },
    [a, b] // 当 a 或 b 变化时,重新计算
  );

  return (
    <button onClick={memoizedCallback}>
      Click me
    </button>
  );
}

In this example:

  • We define a callback function containing the addition operation of a and b, and return a through useCallback memoized version.

  • If the value of a or b changes, useCallback will return a new memoized callback function; otherwise, It will return the previously cached version.

  • When the user clicks the button, we trigger the memoized callback function. This avoids unnecessary re-rendering of child components because the callback function will not change unless the value of a or b changes.

    Note that only when your callback function is passed to the subcomponent and the subcomponent executesshouldComponentUpdate() or uses React.memo(), will not bring any benefit. useCallback can really improve performance. If your callback function is only used for internal operations (such as directly manipulating the DOM), then using useCallback

  1. useMemo: is a Hook of React, which is used to optimize performance. When you perform expensive calculations or create complex objects in functional components, these operations are re-done every time you render. This may cause unnecessary performance overhead.

    useMemoReceives two arguments: a value to memoize (usually the return result) and an array of dependencies. The returned memoized value is updated when the values ​​in the dependencies array change; otherwise, it returns the previously cached version.

    Basic usage is as follows:

import React, { useMemo } from 'react';

function Example({ a, b }) {
  // 使用 useMemo 来记住计算的值
  const memoizedValue = useMemo(() => {
    console.log('Computing expensive value...');
    return a + b;
  }, [a, b]); // 当 a 或 b 变化时,重新计算

  return <div>{memoizedValue}</div>;
}

In this example:

  • We define an addition operation involving a and b, and return a memoized version via useMemo.

  • If the value of a or b changes, useMemo will return a new memoized value; otherwise, it Will return the previously cached version.

  • Each time we render, we use the memoized value instead of the recalculated result.

    Note that only when your calculation result is passed to the subcomponent and the subcomponent executesshouldComponentUpdate() or uses React.memo(), will not bring any benefit. useMemo can really improve performance. If your calculation results are only used for internal operations (such as directly manipulating the DOM), then using useMemo

  1. useRef: is a React Hook that allows the creation of mutable references in function components. This Hook returns a ref object whose .current properties are initialized to the passed argument (initialValue). The returned object remains unchanged throughout the component's lifetime.

Basic usage is as follows:

import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  // 创建一个 ref 来保存文本输入框的 DOM 元素
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // 当按钮被点击时,使文本输入框获取焦点
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

In this example:

  • We create a new ref by calling useRef() and assign it to the inputEl variable.

  • On the <input> tag, we assign inputEl to the ref attribute. In this way, React will bind the corresponding DOM element to inputEl.current.

  • When the user clicks the button, we can make the text input box gain focus by calling inputEl.current.focus().

    Note that useRef() can be used not only on DOM elements, but also on any type of variable. Its main use is when a value or object needs to be preserved between different renderings.

  1. useImperativeHandle: is a Hook in React, which is used to customize the ref value exposed to the parent component. Normally, we can access some instance methods or properties inside the subcomponent through ref. However, in some scenarios, we may want to expose some specific methods or properties instead of the entire instance by default.

Basic usage is as follows:

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

function FancyInput(props, ref) {
  const inputEl = useRef(null);

  // 使用 useImperativeHandle 来暴露 customMethod 给父组件
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputEl.current.focus();
    },
    customMethod: () => {
      console.log('I am a custom method');
    }
  }));

  return <input ref={inputEl} type="text" />;
}

// 使用 forwardRef 将 ref 转发到 FancyInput 中
const WrappedFancyInput = forwardRef(FancyInput);

function ParentComponent() {
  const fancyInputRef = useRef(null);

  const handleClick = () => {
    // 在父组件中调用子组件的 customMethod 方法
    fancyInputRef.current.customMethod();
  };

  return (
    <>
      <WrappedFancyInput ref={fancyInputRef} />
      <button onClick={handleClick}>Call Custom Method</button>
    </>
  );
}

In this example:

  • We create a function component named FancyInput and use the forwardRef higher-order function to receive a ref Parameters.

  • Inside FancyInput we use useImperativeHandle to define the methods and properties to be exposed to the parent component. Here we expose the focus and customMethod methods.

  • The parent component passes the object to the child component through the ref attribute. In this way, the parent component can access the and methods exposed by the child component through . refWrappedFancyInputfancyInputRef.currentfocuscustomMethod

    Note that overusing useImperativeHandle may result in code that is difficult to understand and maintain. Therefore, you should try to avoid directly manipulating the DOM of child components and instead use React's data flow for communication whenever possible.

  1. useLayoutEffect: is a React Hook, it is similar to useEffect, but has two key differences:

    1. useLayoutEffectCalled synchronously after all DOM changes, but before the browser draws. This allows you to read the layout and trigger a re-render synchronously.
    2. In server-side rendering,useLayoutEffect will not be executed.

Basic usage is as follows:

import React, { useLayoutEffect } from 'react';

function Example() {
  useLayoutEffect(() => {
    // 这里可以访问到最新的 DOM 样式和几何信息
    console.log('DOM 已经更新,但我还没有被绘制');
  });

  return <div>Example</div>;
}

In this example:

  • When the component completes rendering, the callback function within useLayoutEffect will be executed. At this point, all DOM updates have been completed, but the browser hasn't started drawing yet.

  • If you need to access or modify the DOM (such as setting styles) before the browser draws, you should use useLayoutEffect.

    Note that overuse useLayoutEffect may cause visual flickering or performance issues. Therefore, you should try to avoid blocking the browser's rendering process and use standard useEffect to handle side effects whenever possible.

  1. useDebugValue: is a React Hook, which is used to display custom hook labels in the React developer tools. This is useful for debugging complex custom hooks.

Basic usage is as follows:

import React, { useDebugValue } from 'react';

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

  // 获取 friendID 对应的好友在线状态
  // ...

  // 使用 useDebugValue 来为这个 hook 显示一个可读的标签
  useDebugValue(isOnline ? '在线' : '离线');

  return isOnline;
}

function FriendStatus() {
  const isOnline = useFriendStatus(123456);

  return (
    <span>{isOnline ? '在线' : '离线'}</span>
  );
}

In this example:

  • We created a custom hook named useFriendStatus that receives a friendID parameter and returns the friend's online status.

  • Inside useFriendStatus, we use useDebugValue to display a human-readable label for this hook. This tag will be displayed in the component tree of the React developer tools.

  • In the FriendStatus component, we called useFriendStatus and rendered the corresponding online state.

    Note thatuseDebugValue only works in development mode and will only affect display in the React Developer Tools. Therefore, in a production environment, you don't need to worry about the additional performance overhead it brings.

These are the core React Hooks. In addition, the community has developed many other custom Hooks, such as useDebounce, useThrottle, etc., to solve specific problems. Questions and Scenarios.

Guess you like

Origin blog.csdn.net/weixin_46002631/article/details/134729728