参赛作品
亲爱的读者您好!
前一段时间(大约一年),我面临着需要根据当前用户权限有条件地渲染ReactJS中的组件的需求。 我首先开始寻找现成的解决方案和“最佳实践”。 文章“
React中基于角色的授权 ”对
高阶组件 (HOC)的使用印象最深。 但是,不幸的是,我没有找到令我满意的解决方案。
显然,他毕竟错过了一些东西……...或者不知道上下文的存在。 在撰写本文时,我
在stackoverflow中遇到了一个很好的
答案 。 我最终得到了一个非常相似的解决方案。
那时,我对react-redux-connect(npm模块)有点熟悉,并且对连接功能中使用的修饰方法非常着迷。 可以在
此处找到对连接设备的详细分析。
解决方案说明
首先,您需要确定决策呈现组件所需的最少信息。 显然,要进行渲染,必须满足一些条件(作为一种选择,有某种权利-例如,添加新用户的权利)。 我们将此条件称为
要求 (或英文要求)。 要了解是否满足要求,我们可以基于一
组当前的用户
权限 -
凭据 。 也就是说,定义一个函数就足够了:
function isSatisfied(requirement, credentials) { if (...) { return false; } return true; }
现在,我们或多或少地决定了渲染条件。 如何使用?
1.我们可以使用额头方法:
const requirement = {...}; class App extends Component { render() { const {credentials} = this.props; return isSatisfied(requirement, credentials) && <TargetComponent>; } }
2.我们可以走得更远,然后将目标组件包装在另一个组件中,这将验证需求:
const requirement = {...}; class ProtectedTargetComponent extends Component { render() { const {credentials} = this.props; return ( isSatisfied(requirement, credentials) ? <TargetComponent {...this.props}> {this.props.children} </TargetComponent> : null ); } } class App extends Component { render() { const {credentials} = this.props; return <ProtectedTargetComponent/>; } }
手动为每个目标组件编写包装器是很乏味的。 我们如何简化呢?
3.我们可以诉诸HOC机制(类似于“ react-redux-connect”中的connect):
function protect(requirement, WrappedComponent) { return class extends Component { render() { const { credentials } = this.props; return ( isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const {credentials} = this.props; return ( ... <AdminButton credentials={credentials}> Add user </AdminButton> ... ); } }
已经更好了,但仍然很麻烦-您需要在整个组件树中手动添加凭据。 该怎么办? 逻辑上假设当前用户的凭据是整个应用程序的全局对象。 然后,react-redux-connect再次起作用。 阅读有关该模块设备的文章后,我们发现它使用了一些ReactJS
上下文 。
4.使用上下文机制,我们得到最终的方法:
const { Provider, Consumer } = React.createContext(); function protect(requirement, WrappedComponent) { return class extends Component { render() { return ( <Consumer> { credentials => isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null } </Consumer> ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const { credentials } = this.props; return ( <Provider value={credentials}> ... <AdminButton> Add user </AdminButton> ... </Provider> ); } }
后记
这是对想法本身的简短论述。 基于此想法,实现了一个模块(
github ,
npm ),该模块提供了更多有趣的功能并且更易于嵌入(请参阅github中的README.md并使用该模块进行
演示 )。
出于某种原因,我无法将创建的npm软件包放入演示中,因此我不得不在其中插入模块代码。 但是通过
npm install react-rbac-guard安装的模块可以在本地运行(Chrome 69.0.3497.100)。 我怀疑问题出在构建方法中-我只是从具有类似用途的
模块中复制了package.json和webpack.config.prod.js文件。
由于我不是前端开发人员,所以仍有许多未完成的工作(缺少测试,
https: //codesandbox.io中的不可操作性,还有可能遗漏了其他要点)。 因此,如果有任何意见,建议或要求,欢迎您!
PPS:有关拼写的所有注释(包括README.md),请发送个人消息或以请求请求的形式发送。