跳转到主要内容

React由于其简单和灵活,近年来已成为最受欢迎的前端库之一。然而,当应用程序的复杂性扩展时,管理状态、处理异步输入和维护可扩展的体系结构可能会变得困难。我们将在本文中介绍四种高级React模式,它们将帮助您克服这些困难以及如何组合它们:

  • 前端模式的后端(BFF)
  • 挂钩模式
  • 高阶组件模式
  • 观察者模式
  • 将其整合在一起

前端模式的后端(BFF)

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

当使用像Next.js这样的框架时,实现这一点就更容易了,因为它有一个集成的API路由系统,使您能够为前端应用程序快速构建API服务。这只是一种可能性,不需要在应用程序中使用它来实现BFF模式。

那么,在React应用程序中使用BFF模式什么时候可能有利呢?一个例子是有一个既大型又复杂的前端应用程序,它有几个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>
  );
}

W/ 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才是有利的。

挂钩模式

React中的hook模式是一个很好的特性,它允许我们在多个组件之间重用有状态行为。挂钩是使我们能够在功能组件中利用React的状态和其他功能的功能。

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

下面是一个示例,说明如何使用功能组件来构造自定义挂钩,以处理从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的自定义挂钩,它将URL作为参数,并从API返回数据。然后,我们在“MyComponent”组件中使用这个钩子来获取和显示我们的数据。通过创建自定义挂钩,我们可以跨多个组件重用有状态逻辑,并创建一个更模块化和可维护的体系结构。

注意:虽然React钩子可以被声明,看起来像正常的函数,但它们是唯一的,因为它们是唯一能够“挂钩”到React的状态和生命周期中的函数类型,它们很容易被发现,因为它们总是以“use”为前缀`

高阶组件模式

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

HOC的一个流行应用程序用例是执行身份验证。例如,我们可能会编写一个功能组件,检查身份验证,然后呈现指定的组件或重定向到登录页面。然后,该组件可以封装在HOC中,以向我们想要保护的任何其他组件添加身份验证功能。

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

最终,HOC可能是提高React组件功能和可重用性的好工具,而且它们在集成身份验证等跨领域问题方面尤其有效。

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,它将一个组件作为输入,并生成一个包含身份验证逻辑的新组件。然后,这个HOC被用于我们的“MyComponent”组件,以保护我们的内容和请求身份验证。

观察者模式

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

我们可以利用内置的“useContext”钩子在React中创建观察者模式,这允许我们在不必显式提供道具的情况下向下传输数据。

以下是我们如何在应用程序中使用功能组件构建观察者的示例:

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”钩子将其发送给其子级。然后使用“MyObserver”组件显示来自提供程序的数据。通过使用观测器模式,我们可以在React应用程序中构建一个更灵活、更可维护的架构来控制状态和数据流。

将其整合在一起

大型应用程序通常将许多模式组合在一起,形成一个更复杂的体系结构。为了构建一个可扩展和健壮的前端架构,我们可以将BFF模式、hooks模式、HOC模式和observer模式等模式集成到一个解决方案中。

以下是我们如何在应用程序中使用功能组件来混合这些模式的示例:

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”组件向“MyComponent”和“MyObserver”组件提供数据。我们还使用“withAuth”HOC来保护MyComponent组件中的内容,并使用“useFetch”钩子从BFF服务中提取数据。所有这些模式结合在一起简化了我们代码库的所有复杂接口,现在我们可以轻松无重复地创建新功能。

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

文章链接