React 设计模式 0x3:Ract Hooks

2023-05-17 21:06:42 浏览数 (1)

学习如何轻松构建可伸缩的 React 应用程序:Ract Hooks

# React Hooks

React Hooks 是在函数式组件中使用的生命周期方法,React Hooks 在 React 16.8 中被引入。在类组件中的生命周期方法已被合并成 React Hooks,React Hooks 无法在类组件中使用。

其中一些内置的 React Hooks 包括以下几个:

  • useState
  • useReducer
  • useEffect
  • useLayoutEffect
  • useMemo
  • useCallback
  • useRef
  • useContext

在使用 React Hooks 时,需要遵循一些规则:

  • Hooks 只能在函数式组件中调用
  • Hooks 必须从顶层调用,不能在循环、条件语句等内部调用
  • 可以创建自己的 Hooks,但必须遵循前面两条规则

# useState

useState 方法是常用的 React Hooks 之一。该 Hook 被归类为 React 中的受控组件中,useState 方法设置了一个初始值,可以随着用户执行操作而更新。

代码语言:javascript复制
import React, { useState } from "react";

function Example() {
  // 声明一个新的叫做 "count" 的 state 变量
  const [count, setCount] = useState(0);

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

export default Example;

# useReducer

useReducer 方法是常用的 React Hooks 之一。当应用程序中存在复杂的状态更改时,可以使用此 Hook,类似于 useState,但是需要发送 action 来更新状态:

代码语言:javascript复制
import React, { useReducer } from "react";

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();
  }
}

const initialState = { count: 0 };

function Example() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "increment" })}> </button>
    </>
  );
}

export default Example;

# useEffect

useEffect 方法是常用的 React Hooks 之一。useEffect 有两个参数(箭头函数和可选的依赖项数组),用于异步操作。

依赖项数组是可选的,不传入数组时,回调函数会在每次渲染后执行,传入空数组时,回调函数只会在组件挂载和卸载时执行。依赖项数组可以接受任意数量的值,这意味着对于依赖项数组中更改的任何值,useEffect 方法将再次运行。

useEffect 箭头函数支持返回一个函数,该函数会在组件卸载时执行,用于清理定时器、取消事件监听等。

通常在组件挂载之前进行 API 调用时,会使用 useEffect

代码语言:javascript复制
import React, { useState, useEffect } from "react";

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

  useEffect(() => {
    // 使用浏览器的 API 更新页面标题
    document.title = `You clicked ${count} times`;
  }, [count]);

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

export default Example;

# useLayoutEffect

useLayoutEffect 是 React Hooks 提供的一个用于执行副作用操作的 Hook,它与 useEffect 相似,但有一些区别。

useEffect 一样,useLayoutEffect 也会在组件渲染之后执行,但是它会在浏览器 layoutpaint 之前同步执行。这意味着 useLayoutEffect 中的任何操作都将在浏览器更新 DOM 之前执行,这使得它适用于需要精确控制渲染结果的情况。

useEffect 不同的是,useLayoutEffect 不会异步执行,这意味着它会阻塞渲染过程,直到它完成。因此,它的性能比 useEffect 差,特别是在执行昂贵操作的情况下。

使用 useLayoutEffect 的场景通常是需要在浏览器更新 DOM 前同步计算布局或者执行某些 DOM 操作。如果没有必要进行同步的操作,建议使用 useEffect 来代替,以获得更好的性能和更流畅的用户体验。

代码语言:javascript复制
import React, { useState, useLayoutEffect } from "react";

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

  useLayoutEffect(() => {
    // 使用浏览器的 API 更新页面标题
    document.title = `You clicked ${count} times`;
  }, [count]);

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

export default Example;

# useMemo

useMemo 用于在组件重新渲染时缓存计算结果,它会缓存一个计算的返回值。可用于性能优化,因为它会缓存计算出的值,并在依赖项数组中的值不改变时返回该值。如果这些值发生变化,那么 useMemo 就会重新运行,然后返回新计算出的值。

代码语言:javascript复制
import React, { useState, useMemo } from "react";

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

  const expensive = useMemo(() => {
    let sum = 0;
    for (let i = 0; i < count; i  ) {
      sum  = i;
    }
    return sum;
  }, [count]);

  return (
    <div>
      <p>
        You clicked {count} times, and sum is {expensive}
      </p>
      <button onClick={() => setCount(count   1)}>Click me</button>
    </div>
  );
}

export default Example;

# useCallback

useCallback 主要用于避免在每次渲染时都重新创建函数。

在 React 中,当父组件重新渲染时,所有的子组件也会重新渲染。如果子组件的某个函数作为 props 传递给子组件,而父组件重新渲染时,这个函数会被重新创建。这可能会导致不必要的渲染,因为即使没有必要更新组件,子组件也会重新渲染。这时就可以使用 useCallback 来优化性能。

useCallback 接收两个参数:回调函数和一个依赖项数组。当依赖项数组中的任何一个值发生变化时,回调函数就会重新生成。这意味着当 useCallback 返回的函数被传递给子组件时,只有在依赖项变化时才会重新生成。

代码语言:javascript复制
import React, { useState, useCallback } from "react";

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

  const handleClick = useCallback(() => {
    console.log(count);
  }, [count]);

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

function Child({ onClick }) {
  console.log("Child render");
  return <button onClick={onClick}>Click me</button>;
}

export default Example;

# useRef

useRef 用于在函数组件中创建一个持久化的引用变量,该变量的值在组件重新渲染时不会被重置。useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数(即初始值),可以通过对 current 属性的修改来更新其值。与 useState 的主要区别在于,useState 的状态更新会触发组件重新渲染,而 useRef 的引用更新不会。

例如,可以使用 useRef 存储上一次的状态值,以便在下一次状态更新时进行比较,从而避免不必要的副作用。

useRef 方法主要用于以下两个方面:

指向 DOM 中的一个元素

代码语言:javascript复制
import React, { useRef } from "react";

function Example() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={handleClick}>Focus the input</button>
    </div>
  );
}

export default Example;

存储一些不需要触发重新渲染的变量或状态值

代码语言:javascript复制
import React, { useState, useRef } from "react";

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

  const handleClick = () => {
    prevCountRef.current = count;
    setCount(count   1);
  };

  return (
    <div>
      <p>
        Now: {count}, before: {prevCountRef.current}
      </p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

export default Example;

# useContext

useContext 用于访问在 React.createContext 中创建的上下文对象。它允许在 React 组件之间共享数据,而不需要通过多层逐层 props 传递数据。

useContext 接受一个上下文对象(通过 React.createContext 创建),并返回该上下文的当前值。在组件渲染期间,当上下文的值发生更改时,React 将重新渲染组件。

代码语言:javascript复制
import React, { useContext, createContext } from "react";

const ThemeContext = createContext("light");

const Comp1 = () => {
  const theme = useContext(ThemeContext);
  return <div>{theme}</div>;
};

const Comp2 = () => {
  const theme = useContext(ThemeContext);
  return <div>{theme}</div>;
};

function Example() {
  return (
    <ThemeContext.Provider value="dark">
      <Comp1 />
      <Comp2 />
    </ThemeContext.Provider>
  );
}

export default Example;

# 自定义 Hooks

可以编写自己的 Hooks,这些 Hooks 是以 use 开头的函数,并且遵循之前提到的 React Hooks 的相同原则。

代码语言:javascript复制
import React, { 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;
}

function Example() {
  const isOnline = useFriendStatus(0);

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

export default Example;

0 人点赞