likes
comments
collection
share

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

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

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

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

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

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

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

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

const MyWrapper = ({ children }) => {
  return visible ? children(disabled) : null
}

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

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

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

const MyWrapper = ({ children, auth }) => {
  return React.Children.map(children, child => { 
    // 将disabled属性传递给子组件 
    return React.cloneElement(child, { disabled }); 
});
}
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'));