likes
comments
collection
share

译文: 增强你的React技能,你可以掌握的4种高级模式

作者站长头像
站长
· 阅读数 34

前言

近年来,由于React的简单和灵活使其成为最受欢迎的前端框架库之一。然而,当应用程序变得越发复杂,管理状态、处理一步输入和维护可扩展的架构就变得更加困难。本文将介绍四种高级的React模式,帮助你克服这些困难并学习如何组合这四种模式。本文主要包括如下5节内容:

  1. Backend for Frontend Pattern (BFF) 后端服务前端模式
  2. Hooks Pattern 钩子模式
  3. Higher-Order Component Pattern 高阶组件模式
  4. Observer Pattern 观察者模式
  5. Bringing it all together 组合上述四种模式

Backend for Frontend Pattern (BFF)

前端后端分离(BFF)模式允许您使用单独的后端来管理所有 API 查询和数据处理,并开发 React 应用程序。这有助于保持前端代码的简洁性和清晰性,并可以提高应用程序的性能。

如果使用像 Next.js 这样的框架,实现这一点甚至会更容易,因为它具有集成的 API 路由系统,可以让您快速构建前端应用程序的 API 服务。但这只是一种可选项,在应用程序中实现 BFF 模式并不一定需要Next.js,有超多别的选择。

那么,何时使用 BFF 模式对您的 React 应用程序可能是有利的呢?一个例子是具有多个 API 调用、数据处理和聚合责任的大型、复杂的前端应用程序,例如仪表板。通过将繁重的处理逻辑与前端分离,您可以创建一个更可扩展和可维护的架构。

如果您的应用程序有许多可共享的相似数据或 API 查询,使用 BFF 模式也可以帮助您避免重复代码,并促进应用程序之间的代码重用。

无BFF模式

import { useState } from 'react';

function MyComponent() {
  const [data, setData] = useState([]);

  const processData = (rawData) => {
    // super long and complex data processing here
  };

  const loadData = () => {
    const rawData = [
      { id: 1, title: 'Smartphone', category: 'electronics' },
      { id: 2, title: 'Laptop', category: 'electronics' },
      { id: 3, title: 'Chair', category: 'furniture' },
      { id: 4, title: 'Table', category: 'furniture' },
    ];

    const processedData = processData(rawData);
    setData(processedData);
  };

  loadData();

  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}

有BFF模式

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

const API_URL = 'https://my-bff-service.com';

function MyComponent({ data }) {
  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}

function MyBFFComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    axios.get(`${API_URL}/my-data`).then(response => {
      setData(response.data);
    });
  }, []);

  return <MyComponent data={data} />;
}

在这个例子中,我们向 BFF 服务发送请求,该服务预处理我们的数据,这样我们就不需要在前端进行处理,而且可以减轻客户端的负担。然而,使用这种模式并不是只有优点,我们必须承担 API 请求延迟的代价。只有当客户端处理和数据聚合显著减慢我们的前端时,例如太多的网络请求、大型数据集上的复杂转换或其他类似情况时,将我们的数据处理转移到 API 上才是有利的。

Hook 模式

React 中的 hooks 模式是一个很棒的特性,它允许我们在多个组件之间重用有状态行为。Hooks 是函数,它们使我们能够在函数组件中利用 React 的状态管理和其他能力。

我们在前面的例子中使用的 useState hook 是最常用的 hook 之一。然而,还有许多其他可用的 hook,例如 useEffect hook,它使我们能够在组件中执行副作用,以及 useContext hook,它使我们能够访问整个应用程序中的全局数据。

下面这个例子将介绍如何使用自定义hook在函数式组件中处理API请求:

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

function useFetch(url) {
  const [data, setData] = useState([]);
  useEffect(() => {
    axios.get(url).then((response) => {
      setData(response.data);
    });
  }, [url]);
  return data;
}
function MyComponent() {
  const data = useFetch('https://my-api.com/my-data');
  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}

在这个例子中,我们创建了一个名为 useFetch 的自定义 hook,它以 URL 作为参数并返回 API 的数据。然后我们在 MyComponent 组件中使用这个 hook 来获取和显示我们的数据。通过创建自定义 hook,我们可以在多个组件之间重用有状态逻辑,并创建更模块化和可维护的架构。

注意:虽然 React hooks 可以像普通函数一样声明和编写,但它们是独特的,因为它们是唯一能够hooking到 React 状态和生命周期中的特殊函数类型,它们很容易被识别,因为它们的名称前缀总是以 use 开头(自定义hook在实现或使用时会有一些限制,详情可查阅相关资料)。

Higher-Order Component Pattern

高阶组件(HOC)设计模式是 React 开发人员希望重用和组合代码的强大工具。HOC 是接受组件作为输入并返回具有增强功能的新组件的函数。

高阶组件的一个流行的应用场景是执行身份验证。比如,我们可以编写一个函数组件,检查身份验证,然后判断是渲染指定的组件还是重定向到登录页面。然后,可以将此组件包装在 HOC 中,以添加身份验证功能到任何其他要保护的组件中。

这种模式有助于抽象出可能需要在多个组件中使用的常见功能,使我们的代码更模块化且更易于维护。此外,HOC 可以在多个组件中重复使用,避免代码重复。

总之,HOC 可能是改进 React 组件的功能和可重用性的好工具,特别适用于整合横切问题,如身份验证。下面是一个HOC的具体案例:

import React from 'react';

function withAuth(WrappedComponent) {
  return function WithAuth(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    useEffect(() => {
      // Check if the user is logged in
      const isLoggedIn = true; // Replace with actual authentication logic
      setIsLoggedIn(isLoggedIn);
    }, []);
    if (!isLoggedIn) {
      return <p>You must be logged in to access this content.</p>;
    }
    return <WrappedComponent {...props} />;
  };
}
function MyComponent() {
  return <p>This is protected content that requires authentication.</p>;
}
export default withAuth(MyComponent);

在这个例子中,我们编写了一个名为 withAuth 的 HOC,它接受一个组件作为输入,并生成一个包含身份验证逻辑的新组件。然后,在我们的 MyComponent 组件中使用此 HOC 来保护我们的内容并要求身份验证。

Observer Pattern

观察者模式允许我们通过自动将一个对象中的更改传播到所有订阅者来构建对象之间的一对多关系。在 React 应用程序中,这种方法对于控制状态和数据流非常有用。

我们可以利用内置的 useContext hook 在 React 中创建观察者模式,这允许我们在不必显式提供 props 的情况下向组件树传递数据。

以下是一个使用函数组件构建观察者的示例:

import { createContext, useContext, useState, useEffect } from 'react';

const MyContext = createContext([]);

function MyProvider(props) {
  const [data, setData] = useState([]);
  useEffect(() => {
    // Fetch data from the API
    const newData = [...]; // Replace with actual data
    setData(newData);
  }, []);
  return <MyContext.Provider value={data}>{props.children}</MyContext.Provider>;
}

function MyObserver() {
  const data = useContext(MyContext);
  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}

function App() {
  return (
    <MyProvider>
      <MyObserver />
    </MyProvider>
  );
}

在这个例子中,我们构建了一个名为 'MyProvider' 的提供者组件,它从 API 接收数据并使用 useContext hook 将数据传递给其子组件。然后使用 'MyObserver' 组件显示提供者的数据。通过使用观察者模式,我们可以构建一个更灵活、可维护的架构,用于控制 React 应用程序中的状态和数据流。

Bringing it all together

一个大型应用程序通常会结合多种模式形成更复杂的架构。为了构建一个可扩展和健壮的前端架构,我们可以将 BFF 模式、hooks 模式、高阶组件模式和观察者模式等模式集成到一个解决方案中。

以下是一个使用函数组件在我们的应用程序中混合这些模式的示例:

import { createContext, useContext, useState, useEffect, useMemo } from 'react';
import axios from 'axios';

const API_URL = 'https://example.com/my-bff-service';
const MyContext = createContext([]);

function withAuth(WrappedComponent) {
  return function WithAuth(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    useEffect(() => {
      // Check if the user is logged in
      const isLoggedIn = true; // Replace with actual authentication logic
      setIsLoggedIn(isLoggedIn);
    }, []);

    if (!isLoggedIn) {
      return <div>You must be logged in to access this content.</div>;
    }
    
    return <WrappedComponent {...props} />;
  };
}

function useFetch(url) {
  const [data, setData] = useState([]);
  
  useEffect(() => {
    axios.get(url).then((response) => {
      setData(response.data);
    });
  }, [url]);
  
  return data;
}

const AuthenticatedMyComponent = withAuth(function MyComponent() {
  
  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
});

function MyObserver() {
  const data = useContext(MyContext);
  
  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}

function MyProvider({ children }) {
  const data = useFetch(`${API_URL}/my-data`);
  const value = useMemo(() => data), [data])

  return (
    <MyContext.Provider value={value}>
      {children}
    </MyContext.Provider>
  );
}

function App() {
  return (
    <MyProvider>
      <AuthenticatedMyComponent />
      <MyObserver />
    </MyProvider>
  );
}

export default App;

在这个例子中,我们将所有上述的模式结合到一个应用程序中。我们使用 MyProvider 组件来提供数据给我们的 MyComponentMyObserver 组件。我们还使用 withAuth HOC 来保护我们的 MyComponent 组件中的内容,并使用 useFetch hook 来从我们的 BFF 服务中获取数据。所有这些模式的组合简化了代码库中所有复杂的接口,现在我们可以轻松创建新的功能,而不需要重复代码。

总之,我们在本文中讨论的这四种高级 React 模式可以帮助您构建更复杂、更健壮的应用程序。通过利用所有这些模式,您可以更好地管理状态、处理异步数据,并更好地分配处理负载。通过将它们应用于您自己的应用程序,您可以将您的 React 技能提升到一个新的水平,并创建高效、干净、可靠的应用程序。

原文链接: medium.com/@farisaziz1…