与基于角色和权限的渲染组件相关的设计模式或架构?

A design pattern or architecture related to rendering components based on roles and permissions?

我想知道是否有一种软件设计模式可以解决我面临的挑战。我有一个 Web 应用程序可以使用多种不同的用户角色类型,例如 SuperAdminAdminResellerDealerCustomerVolunteer、等...

我们的 React 代码中充斥着 if 语句,这些语句检查一个人的角色以及其他条件来决定“ui 组件的类型”来呈现它们。这是一个非常简单的例子:

// Container.js
<div>
   <h1>Something</h1>
   {['SuperAdmin', 'Admin'].includes(user.role) && (<input type="text" value="{record.first_name}" />)}
   {['Reseller', 'Dealer'].includes(user.role) && (<span className="border-2">{record.first_name}</span>)}
   {['Customer', 'Volunteer'].includes(user.role) && process.env.NETWORK_MODE === 'intranet' && (
    <div className="bg-grey">
        <span>Created at {record.create_at}</span>
        <span>This customer wants to be anonymous</span>
    </div>
   )}
   {['Reseller'].includes(user.role) && user.accountBalance > 0 && user.statementDate > (new Date()) && (
     <div>Please pay your bills</div>
   )}
</div>

上面的例子我们很难管理,因为有100多个组件。当我们需要更改角色类型的访问权限或添加其他条件时,我们必须遍历每个组件并查找任何偏离的 if 语句。我们正在考虑编写一些自动化测试,但感觉我们需要在实用之前重构 UI 代码。

是否有一些软件工程设计模式与我的情况相关?如果是这样,请说出名字,我会仔细阅读。


现在,我正在考虑这样的方法:

// Container.js
<div>
   <h1>Something</h1>
   <Customer user={user} />
   <BillNotice user={user} />
</div>

// Customer.js
const Customer = ({user}) => {
   if(['Admin', 'SuperAdmin'].includes(user.role)) return <CustomerVersion1 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <CustomerVersion2 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <CustomerVersion3 />;
   ...etc...
}

const CustomerVersion1 = () => <Component />;
const CustomerVersion2 = () => <Component />;
const CustomerVersion3 = () => <Component />;

...etc...

// BillNotice.js
const BillNotice = ({user}) => {
   if(['Admin', 'SuperAdmin'].includes(user.role)) return <BillNotice1 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <BillNotice2 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <BillNotice3 />;
   ...etc...
}

const BillNotice1 = () => <Component />;
const BillNotice2 = () => <Component />;
const BillNotice3 = () => <Component />;

基本上我让每个组件负责决定它应该如何呈现自己。然后我可以通过模拟用户参数为每个组件编写单元测试并查看是否返回了适当的组件。如果这种方法真的有一个名字,那就太好了,我可以阅读更多关于它的内容!

根据我的经验,对于如此多的条件,没有灵丹妙药,毕竟它存在,我们需要在某个地方实施它们, 但是,如果 IMO 设计得好,我们可以只写一次(在一个地方管理)。

也许使用 hook 是这种情况下的一个好习惯。

  • ./挂钩
// control the logic in one place
const useAuthControl = (userRole, conditions) => {
  // return as demand, maybe a more nested structure in the real world case
  return {
    isAdmin: ['Admin', 'SuperAdmin'].includes(userRole),
    isRole: ['Role', 'Role'].includes(userRole) && condition1 && condition2 ...etc...,
    isAnotherRole: ['Role', 'Role'].includes(userRole) && condition1 && condition2 ...etc...,
  }
}
  • ./组件
// pass the condition dependencies if necessary
const { isAdmin, isRole, isAnotherRole, ... } = useAuthControl(userRole, ...condition);

// use for different components with a simpler method to write everywhere
return (
  <>
    {isAdmin && ...}
    {isRole && ...}
    {isAnotehrRole && ...}
  </>
)

// other components as well
return (
  <>
    {isAdmin && ...}
    {isRole && ...}
    {isAnotehrRole && ...}
  </>
)