React.js 工厂模式轻松构建复杂 UI

2025-06-07

React.js 工厂模式轻松构建复杂 UI

客观的:

我们将讨论

  • 什么是工厂模式以及如何使用它来构建复杂、可扩展的 UI?
  • 使用 React 记忆和工厂模式提高应用程序的性能

假设我们正在构建一个仪表板。当用户登录时,我们会从后端获取一个用户对象。基于该对象,我们将确定用户在仪表板上看到的内容。假设这user_a是一个从服务器返回的对象

user_a = {
   name: "jhon doe",
   items: [
     {
       title: "Card 1",
       details: {
         // ...more info
       },
       type: "A"
     },
     {
       title: "Card 2",
       details: {
         // ...more info
       },
       type: "B"
     },
     {
       title: "Card 3",
       details: {
         // ...more info
       },
       type: "C"
     },
     {
       title: "Card 4",
       details: {
         // ...more info
       },
       type: "D"
     }
   ]
 }
Enter fullscreen mode Exit fullscreen mode

根据对象的类型,我们将渲染一个卡片组件。假设用户会看到以下内容:

N|固体

现在看起来相当直观。我们有 4 种类型的卡片组件,因此我们可以像下面这样编写组件,并App用一个简单的 switch 语句将它们渲染到我们的组件(容器组件)中。

function A() {
 return(
   <div>Type A Component</div>
 )
}

function B() {
 return(
   <div>Type B Component</div>
 )
}

function C() {
 ...
}

function D() {
 ...
}

Enter fullscreen mode Exit fullscreen mode

App.jsx

function App() {
  return (
      <div>
        {cards.map(card => {
          switch (card.type) {
            case "A":
              return <A />;
            case 'B':
              return <B />;
            case 'C':
              return <C />;
            case 'D':
              return <D />;
            default:
              return null;
          }
        })}
     </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

然而,随着时间的推移,我们的应用程序变得越来越受欢迎,我们被要求添加 10 种不同类型的卡片组件。所以现在我们有卡片 A、B、C、D、E、F、G、H、I、J、K……等等。当然,现在我们可以选择继续添加 if 语句或 switch 语句。然而,很快就会变得混乱。

而且,我们还会违反单一职责原则。App组件不应该关心根据负载渲染什么。我们需要一个抽象层来保持代码的简洁。

另外:如果您想了解有关 React 中的 SOLID 模式的更多信息,请查看我的另一篇文章。

好了,解决方案如下。我们不用创建许多 if 语句,而是创建一个函数,该函数会根据有效负载动态创建组件。这个函数被称为工厂函数。

function Factory(props) {
  switch (props.component.type) {
    case "A":
      return <A />;
    case "B":
      return <B />;
    case "C":
      return <C />;
    default:
      return <div>Reload...</div>;
  }
}
Enter fullscreen mode Exit fullscreen mode

我们App现在看起来是这样的。

return (
   <div>
     {cards.map(card => (
       <Factory component={card} />
     ))}
   </div>
 );
Enter fullscreen mode Exit fullscreen mode

简单来说,使用工厂函数来动态创建组件就是工厂模式

但是,你不相信!!

好吧,使用工厂函数确实能分离我们的关注点。对于复杂的大型应用程序来说,它有助于我们维护代码,这固然很好,但你仍然不相信。我为什么要费心去写那个额外的函数呢?我需要更多理由。

好吧,这引出了我们的下一个观点,即性能

我们可以有效地使用工厂方法来记忆我们的组件并防止不必要的重新渲染。

使用 React.memo() 钩子进行缓存。

React 中的memo() hook 是一种帮助我们缓存组件的机制。当传入组件的 props 与之前的 props 相同时,我们会返回该组件的缓存版本。这可以防止组件重新渲染,从而提升应用性能。

如果您是钩子新手或者以前从未使用过 memo(),我强烈建议您阅读官方文档。

现在,App当某些道具发生变化时,我们的组件会重新渲染所有卡片组件。

让我们重新审视上面的例子来详细说明

function App() {
  const cards = [
    {
      name: "shadid",
      type: "A"
    },
    {
      name: "shadid",
      type: "C"
    },
    {
      name: "shadid",
      type: "B"
    },
    {
      name: "shadid",
      type: "A"
    }
    .... more of these card objects
  ];
  const [count, setCount] = React.useState(0);
  const doStuff = () => {
    setCount(count + 1);
    console.log(" Click ---> ");
  };
  return (
    <div>
      {cards.map(card => (
        <Factory component={card} />
      ))}
      <button onClick={doStuff}>Click me</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

我们App添加了一个计数器来跟踪状态变化。然后在 A、B、C 组件中添加一个控制台语句来跟踪它们的渲染次数,如下所示

function A() {
 console.log("Rendered A");
 return <div>A</div>;
}

function B() {
 console.log("Rendered B");
 return <div>B</div>;
}

function C() {
 console.log("Rendered C");
 return <div>C</div>;
}
...
Enter fullscreen mode Exit fullscreen mode

现在,每次我们点击按钮时,我们都会看到 A、B、C……组件正在重新渲染。

[ N|固体]

这确实是一个问题。规模化之后,这可能会带来相当糟糕的用户体验,尤其是在我们拥有大约 20 个卡片组件,并且网速很慢的情况下。或许,在某些情况下,我们只想更新 1 或 2 个组件,而将其余组件缓存起来。那么,让我们看看如何使用React.memo()来缓存它们。

A.jsx

function A(props) {
  console.log("Rendered A");
  return <div>A</div>;
}

const areEqual = (prevProps, nextProps) => {
  return prevProps.name === nextProps.name;
};
const Acached = React.memo(A, areEqual);
Enter fullscreen mode Exit fullscreen mode

现在在我们的工厂中,我们可以决定返回组件的缓存版本。


function Factory(props) {
 switch (props.component.type) {
   case "A":
+     return <Acached {...props.component} />;
   case "B":
Enter fullscreen mode Exit fullscreen mode

太棒了!!现在我们的工厂函数不会重新渲染,A而是返回缓存版本。如果使用得当,这将非常强大。
想象一下,有一个每秒刷新一次的实时组件。这可能会导致非常昂贵的重新渲染。我们可以将重新渲染限制为每 5 秒一次,或者根据我们想要的逻辑进行调整。大规模情况下,这将提升性能。我们还将所有逻辑封装在工厂函数中,以便更好地组织代码并分离关注点。

但是,为了这个演示,我们只需添加一个按钮来App模拟硬重新加载。

App.jsx

function App() {
  const [count, setCount] = React.useState(0);
+  const [reload, setReload] = React.useState(false);
  const doStuff = () => {
    setReload(false);
    setCount(count + 1);
    console.log(" Click ---> ");
  };

+  const hardReload = () => {
+    setReload(true);
+    console.log(" Hard Reload ---> ");
+  };
  return (
    <div>
      {cards.map(card => (
+        <Factory component={card} hard={reload} />
      ))}
      <button onClick={doStuff}>Click me</button>
+     <button onClick={hardReload}>Hard Reload</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Factory.jsx

function Factory(props) {
  switch (props.component.type) {
+    case "A":
+      if (props.hard) {
+        return <A {...props.component} />;
+      }
+      return <Acached {...props.component} />;

  }
}
Enter fullscreen mode Exit fullscreen mode

工厂模式的优缺点

像往常一样,任何事情都有其代价:怀疑:

优点 缺点
单一职责原则。您可以将卡片创建代码移动到应用程序的同一位置,从而使代码更易于维护和测试。 ❌ 您需要在开始时编写额外的代码(即工厂函数)来设置工厂模式。
开放/封闭原则。您可以在不破坏现有代码的情况下将新类型的卡片引入应用程序。

想知道如何在 React 中应用 Open/Close 原则吗?
请查看这篇文章。

今天就到这里。想看更多类似的文章,请关注我,有任何反馈或建议,欢迎留言/点赞。

文章来源:https://dev.to/shadid12/react-js-with-factory-pattern-building-complex-ui-with-ease-1ojf
PREV
如何在没有 Expo 的情况下为 Android、iOS 和 Web 创建 React Native App 包名的别名默认将流程指向“模块”字段
NEXT
WASI - 带有 Wasmtime 的 WebAssembly 系统接口