React 基础巩固(四十四)——其他Hooks(useContext、useReducer、useCallback)

一、useContext的使用

在类组件开发时,我们通过 类名.contextType = MyContext的方式,在类中获取context,多个Context或者在函数式组件中通过MyContext.Consumer方式共享context:

import React, { memo } from 'react'
import { UserContext, ThemeContext } from './context'

export default memo(function App() {

// 使用Context

  return (
    <div>
      <UserContext.Consumer>
        {
          value => {
            return (
              <h2>
                <ThemeContext.Consumer>
                  {
                    value => {
                      <span></span>
                    }
                  }
                </ThemeContext.Consumer>
              </h2>
            )
          }
        }
      </UserContext.Consumer>
    </div>
  )
})

可以看到,当我们需要使用多个Context时,存在大量繁琐的嵌套代码;而Context Hook能够让我们通过Hook直接获取某个Context的值,如下:

import React, { memo, useContext } from "react";
import { ThemeContext, UserContext } from "./context";

export default memo(function App() {
  // 使用Context
  const user = useContext(UserContext);
  const theme = useContext(ThemeContext);

  return (
    <div>
      <h2>
        User: {user.name} - {user.age}
      </h2>
      <h2>
        Theme: {theme.color} - {theme.size}
      </h2>
    </div>
  );
});

可以看到,Context Hook仅用了两行代码就替代了上面繁杂的嵌套代码,十分高效简洁。

二、useReducer的使用

useReducer是useState的一种替代方案,当state的处理逻辑比较复杂,可以使用useReducer来进行拆分,或者当修改state时需要依赖之前的state时,也可以使用useReducer。

useReducer使用的场景非常少,通常用于需要统一管理、修改多个数据的场景。例如,当我们需要对多个数据进行统一处理时,若采用useState,则需要多次定义,而reducer可以对其进行统一定义、修改:

import React, { memo, useReducer, useState } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, counter: state.counter + 1 };
    case "decrement":
      return { ...state, counter: state.counter - 1 };
    case "add_number":
      return { ...state, counter: state.counter + action.num };
    case "sub_number":
      return { ...state, counter: state.counter - action.num };
    default:
      return state;
  }
}

export default memo(function App() {
  // const [count, setCount] = useState(0);
  // const [user, setUser] = useState(0);
  // const [list, setList] = useState(0);

  const [state, dispatch] = useReducer(reducer, {
    counter: 0,
    user: {},
    list: [],
  });

  return (
    <div>
      {/* <h2>当前计数:{count}</h2>
      <button onClick={(e) => setCount(count + 1)}>+1</button>
      <button onClick={(e) => setCount(count - 1)}>-1</button>
      <button onClick={(e) => setCount(count + 5)}>+5</button>
      <button onClick={(e) => setCount(count - 5)}>-5</button>
      <button onClick={(e) => setCount(count + 100)}>+100</button> */}

      <h2>当前计数:{state.counter}</h2>
      <button onClick={(e) => dispatch({ type: "increment" })}>+1</button>
      <button onClick={(e) => dispatch({ type: "decrement" })}>-1</button>
      <button onClick={(e) => dispatch({ type: "add_number", num: 5 })}>
        +5
      </button>
      <button onClick={(e) => dispatch({ type: "sub_number", num: 5 })}>
        -5
      </button>
      <button onClick={(e) => dispatch({ type: "add_number", num: 100 })}>
        +100
      </button>
    </div>
  );
});

三、useCallback的使用

useCallback实际的目的是为了进行性能优化,useCallback会返回一个函数的memoized(记忆的)值。在依赖不变的情况下,多次定义的时候,返回的值时相同的。

useCallback的性能优化:

  1. 当需要将一个函数传递给子组件时,可使用useCallback进行优化,将优化之后的函数,传递给子组件

    import React, { memo, useCallback, useState } from "react";
    
    const HYIncrement = memo(function (props) {
      const { increment } = props;
    
      console.log("HYIncrement被渲染");
    
      return (
        <div>
          <button onClick={increment}>increment + 1</button>
        </div>
      );
    });
    
    export default memo(function App() {
      const [count, setCount] = useState(0);
      const [message, setMessage] = useState("hello");
    
      // 使用useCallback
      const increment = useCallback(
        function () {
          setCount(count + 1);
        },
        [count]
      );
    
      // 普通函数
      // const increment = () => {
      //   setCount(count + 1);
      // };
    
      return (
        <div>
          <h2>计数:{count}</h2>
          <button onClick={increment}>+1</button>
    
          <HYIncrement increment={increment} />
    
          <h2>message:{message}</h2>
          <button onClick={(e) => setMessage("world")}>修改 message</button>
        </div>
      );
    });
    
    
  2. 进一步优化

    当count发生改变时,也使用同一个函数

      // 做法一:将count依赖移除掉,缺点:存在闭包陷阱,不依赖count后setCount每次拿到的count并非最新的count
      // const increment = useCallback(function foo() {
      //   console.log("increment");
      //   setCount(count + 1);
      // }, []);
    
      // 做法二:利用useRef,在组件多次渲染时,返回同一个值
      const countRef = useRef();
      countRef.current = count;
      const increment = useCallback(
        function foo() {
          console.log("increment");
          setCount(countRef.current + 1);
        },
        []
      );
    
08-02 14:17