React权限控制(特殊组件&按钮)

react项目中需要实现一个按钮级权限,一开始想的是写一个 AuthButton 组件,然后直接判断权限就好了。 但是,没有这么容易,需求有些不一样...

  • 有一些 TextArea 也需要实现(根据用户权限判断)可查看但不可编辑;
  • Dropdown Menu 有些需要实现(根据用户权限判断)可查看但某些 Menu Item 不可编辑;
  • 等等一些其他的特殊组件

思路1 ------ 每种组件写一个 AuthComp 【太不好维护,每增加一种组件权限需要再写新的组件,组件多维护成本高】

思路2------写一个 AuthWrapper, 将 disabled 和 visible 参数透传进最后的 button 里【代码耦合度太高了,props 传递了太多层,需要修改多个组件,容易产生bug】

思路3 ------ 写一个 AuthWrapper,将每个需要权限控制的组件 / 按钮的外层套一个AuthWrapper的壳。 【优点】:AuthWrapper 专注做用户权限相关交互,内部的 children 就只放 UI 组件相关,交互行为与UI组件相独立,耦合度低。

最后,我决定用 方案3,但是有一个新的问题

js 复制代码
const MyWrapper = ({ children }) => {
  return visible ? children(disabled) : null
}

// 使用
<MyWrapper auth="home.detail.edit">
  {({ disabled }) => ( <button disabled={disabled}>按钮</button> )}
</MyWrapper>

要用 propsRender 的方式穿参,如果每个地方都这样写,那也太奇怪了吧!!

经过一番查询,找到了方法

js 复制代码
const MyWrapper = ({ children, auth }) => {
  return React.Children.map(children, child => { 
    // 将disabled属性传递给子组件 
    return React.cloneElement(child, { disabled }); 
});
}

但是 MenuItem 的 disbled 属性怎么办呢?继续递归处理。 完整的测试代码如下:(代码使用"码上掘金"编辑器编写,可以直接复制进编辑器运行)

js 复制代码
import React, { useState, useCallback } from 'react';
import ReactDom from 'react-dom';

const MyWrapper = ({ children, auth }) => {
  // 模拟权限列表,根据实际情况修改
  const permissionList = ['home.detail.edit', 'some.other.permission']; 
  
  // 检查是否有权限
  const hasPermission = (permission) => {
    return permissionList.includes(permission);
  };

  const isVisible = permissionList.includes(auth);
  const isEditable = permissionList.includes(auth);

  // 根据权限信息决定是否禁用按钮
  const disabled = !isEditable;

  // 处理子组件
  const handleChild = (child) => {
    const disabled = !hasPermission(auth);
    // 如果子组件是 <select> 元素
    if (child.type === 'select') {
      // 提取出 <select> 元素
      // const selectElement = React.Children.only(child);
      // 这一步是为了限制 select 整个组件的 disabled
      const selectElement = React.cloneElement(child, { disabled }) 
      // 处理 <option> 元素
      const options = React.Children.map(selectElement.props.children, option => {
        if (option.type === 'option' && option.props.auth) {
          // 根据权限信息设置是否禁用,hidden处理子元素的隐藏
          const optionDisabled = !hasPermission(option.props.auth);
          return React.cloneElement(option, { disabled: optionDisabled , hidden: option.props.value === 'opel'});
        }
        return option;
      });
      // 返回处理后的 <select> 元素
      return React.cloneElement(selectElement, null, options);
    } else {
      return React.cloneElement(child, { disabled });
    }
  };

  // return isVisible ? React.Children.map(children, handleChild) : null
  return React.Children.map(children, handleChild)
}

const Test = function () {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div onClick={handleClick}>
      test
      <MyWrapper auth="home.detail.edit">
        <button>测试权限</button>
      </MyWrapper>
      <MyWrapper auth="home.detail.add">
        <button>测试权限不可</button>
      </MyWrapper>
      <MyWrapper auth="home.detail.edit">
        <input placeholder="测试可输入" />
      </MyWrapper>
      <MyWrapper auth="home.detail.add">
        <input placeholder="不可输入" />
      </MyWrapper>
      <MyWrapper auth="home.detail.edit">
        <select>
          <option value="volvo" auth="home.detail.edit">Volvo</option>
          <option value="saab" auth="home.detail.edit">Saab</option>
          <option value="opel" auth="home.detail.add">Opel</option>
          <option value="audi" auth="home.detail.add">Audi</option>
        </select>
      </MyWrapper>
      <MyWrapper auth="home.detail.add">
        <select>
          <option value="volvo" auth="home.detail.edit">Volvo</option>
          <option value="saab" auth="home.detail.edit">Saab</option>
          <option value="opel" auth="home.detail.add">Opel</option>
          <option value="audi" auth="home.detail.add">Audi</option>
        </select>
      </MyWrapper>
    </div>
  );
};

ReactDom.render(<Test />, document.getElementById('app'));
      
      
相关推荐
万少7 小时前
HarmonyOS 开发必会 5 种 Builder 详解
前端·harmonyos
橙序员小站9 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
炫饭第一名11 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
王晓枫12 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
符方昊12 小时前
React 19 对比 React 16 新特性解析
前端·react.js
ssshooter12 小时前
又被 Safari 差异坑了:textContent 拿到的值居然没换行?
前端
曲折12 小时前
Cesium-气象要素PNG色斑图叠加
前端·cesium
Forever7_12 小时前
Electron 淘汰!新的桌面端框架 更强大、更轻量化
前端·vue.js
不会敲代码112 小时前
前端组件化样式隔离实战:React CSS Modules、styled-components 与 Vue scoped 对比
css·vue.js·react.js
Angelial12 小时前
Vue3 嵌套路由 KeepAlive:动态缓存与反向配置方案
前端·vue.js