React设计模式

318 阅读4分钟

介绍

React 开发人员可以通过使用设计模式来节省时间和精力,设计模式提供了一种使用经过测试和可信的解决方案解决问题的快速方法。它们支持低耦合的内聚模块,从而帮助 React 开发人员创建可维护、可扩展且高效的应用程序。在本文中,我们将探索 React 设计模式并研究它们如何改进 React 应用程序的开发。

我们将介绍的步骤

搭建 Vite 项目

cn.vitejs.dev/guide/

pnpm create vite

cd design-patterns
pnpm install
pnpm run dev

容器和表示模式

容器和表示模式是一种旨在将反应代码中的表示逻辑与业务逻辑分离的模式,从而使其模块化、可测试并且遵循关注点分离原则。大多数情况下,在 React 应用程序中,我们需要从后端/存储获取数据或计算逻辑并在 React 组件上表示该计算的结果。在这些情况下,容器和表示模式大放异彩,因为它可用于将组件分为两类,即:

  • 容器组件,充当负责数据获取或计算的组件。
  • 表示组件,其工作是将获取的数据或计算值呈现在 UI(用户界面)上。

容器和表示模式的示例如下所示:

import React, { useEffect, useState } from "react";
import { Character } from "./types";
import CharacterList from "./CharacterList";

const StarWarsCharactersContainer: React.FC = () => {
  const [characters, setCharacters] = useState<Character[]>([]);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [error, setError] = useState<boolean>(false);

  const getCharacters = async () => {
    setIsLoading(true);
    try {
      const response = await fetch(
        "https://akabab.github.io/starwars-api/api/all.json",
      );
      const data = await response.json();
      // setIsLoading(false);
      if (!data) return;
      setCharacters(data);
      console.log('data', data)
    } catch (err) {
      setError(true);
      console.log("err", err)
    } finally {
      setIsLoading(false);
    }
  };

  useEffect(() => {
    getCharacters();
  }, []);

  return (
    <CharacterList loading={isLoading} error={error} characters={characters} />
  );
};

export default StarWarsCharactersContainer;

// the component is responsible for displaying the characters

import React from "react";
import { Character } from "./types";

interface CharacterListProps {
  loading: boolean;
  error: boolean;
  characters: Character[];
}

const CharacterList: React.FC<CharacterListProps> = ({
  loading,
  error,
  characters,
}) => {
  if (loading && !error) return <div>Loading...</div>;
  if (!loading && error)
    return <div>error occurred.unable to load characters</div>;
  if (!characters) return null;

  return (
    <ul>
      {characters.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

export default CharacterList;

Hooks 的组件组合

Hooks 是 React 16.8 中首次推出的一项全新功能。从那时起,他们在开发 React 应用程序中发挥了至关重要的作用。挂钩是基本函数,可授予功能组件访问状态和生命周期方法(以前仅可用于类组件)的功能。另一方面,挂钩可以专门设计来满足组件要求并具有其他用例。

我们现在可以隔离所有状态逻辑(一种需要反应性状态变量的逻辑),并使用自定义挂钩在组件中组合或使用它。因此,代码更加模块化和可测试,因为钩子松散地绑定到组件,因此可以单独测试。

带有钩子的组件组合示例如下所示:

// creating a custom hook that fetches star wars characters

import { useEffect, useState } from "react";
import { Character } from "../types";

export const useFetchStarWarsCharacters = () => {
  const [characters, setCharacters] = useState<Character[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(false);
  const controller = new AbortController();

  const getCharacters = async () => {
    setIsLoading(true);
    try {
      const response = await fetch(
        "https://akabab.github.io/starwars-api/api/all.json",
        {
          method: "GET",
          credentials: "include",
          mode: "cors",
          headers: {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*",
          },
          signal: controller.signal,
        },
      );
      const data = await response.json();
      setIsLoading(false);
      if (!data) return;
      setCharacters(data);
    } catch (err) {
      setError(true);
      console.log("err:", err)
    } finally {
      setIsLoading(true);
    }
  };

  useEffect(() => {
    getCharacters();
    return () => {
      controller.abort();
    };
  }, []);

  return {characters, isLoading, error};
};

创建自定义钩子后,我们将其导入到StarWarsCharactersContainer组件中并使用它;

import React from "react";
import { useFetchStarWarsCharacters } from "./useFetchStarWarsCharacters";
import CharacterList from "./CharacterList";


const StarWarsCharactersContainer: React.FC = () => {
  const { characters, isLoading, error } = useFetchStarWarsCharacters();

  return (
    <CharacterList loading={isLoading} error={error} characters={characters} />
  );

}

export default StarWarsCharactersContainer;

使用 Reducers 进行状态管理

大多数情况下,处理组件中的许多状态会导致许多未分组状态的问题,这可能是处理起来很麻烦且具有挑战性的。在这种情况下,Reducers 模式可能是一个有用的选择。我们可以使用 Reducers 将状态分类为某些操作,这些操作在执行时可以更改分组的状态。

此模式允许使用它的开发人员控制组件和/或挂钩的状态管理,让他们在发送事件时管理状态更改。

使用 Reducers 模式的示例如下所示:

使用Providers进行数据管理

提供者模式对于数据管理非常有用,因为它利用上下文 API 通过应用程序的组件树传递数据。这种模式是一种有效的解决支柱钻井问题的方法,这一直是 React 开发中普遍关注的问题。

为了实现提供者模式,我们首先创建一个提供者组件。 Provider 是 Context 对象提供给我们的一个高阶组件。我们可以利用React提供的createContext方法来构造一个Context对象。

createContext

zh-hans.react.dev/reference/r…

使用 createContext 创建组件能够提供与读取的 上下文(context)

从一个文件导入和导出上下文

通常,来自不同文件的组件都会需要读取同一个上下文。因此,在一个单独的文件内定义上下文便成了常见做法。以使用 export 语句 将其导出,以便其他文件读取使用:

// Contexts.js
import { createContext } from 'react';

export const ThemeContext = createContext('light');
export const AuthContext = createContext(null);

在其他文件中定义的组件可以使用 import 语句读取或提供该 context:

// Button.js
import { ThemeContext } from './Contexts.js';

function Button() {
  const theme = useContext(ThemeContext);
  // ...
}
// App.js
import { ThemeContext, AuthContext } from './Contexts.js';

function App() {
  // ...
  return (
    <ThemeContext.Provider value={theme}>
      <AuthContext.Provider value={currentUser}>
        <Page />
      </AuthContext.Provider>
    </ThemeContext.Provider>
  );
}

原文地址: refine.dev/blog/react-…