在我们的应用中,每个按钮都有个权限点,前端通过接口的方式取得此用户的权限点列表,再来决定按钮是否显示。假設我们权限点列表取得后存在window.globals.permissions
。
传统的方式可以在redner的时候,判断用户是否有此权限,在来决定按钮是否显示。例如:
class Page1 extend React.Component {
render() {
const {create, update} = window.globals.permissions;
return (
{create && <Button>Create Project</Button>}
{update && <Button>Update Project</Button>}
);
}
}
如果现在又有个页面叫做Page2,又要再写类似的判断在Page2里面。再者如果需求改变成:"当用户没有权限的时候,按钮设定为disabled"。这样又要把分散再各页面的权限管理逻辑,通通修改一变,如:
class Page1 extend React.Component {
render() {
const {create, update} = window.globals.permissions;
return (
{<Button disabled={!create}>Create Project</Button>}
{<Button disabled={!update}>Update Project</Button>}
);
}
}
这显然不是一个优雅的解法。其实用户是否有某按钮的权限,只需要关心權限點的名稱,能不能这样写?
class Page1 extend React.Component {
render() {
return (
{<Button auth="create">Create Project</Button>}
{<Button auth="update">Update Project</Button>}
);
}
}
在按钮的地方,只要指定权限点名称就好了。要判断window.globals.permissions和是否隐藏/disabled就交由包装器统一处理即可。接下来我们看一下包装器的部份:
export default function injectAuth(component) {
const checkAuth = (props) => {
let {auth} = props;
// No Auth required
if(auth == null) {
return true;
}
return windows.globals.permissions[auth];
}
let WrappedClass = class extends component {
render() {
if(checkAuth(this.props)) {
return super.render();
}
else {
const noAuthType = this.props.noAuthType || 'hidden';
if(noAuthType === 'hidden') {
return null;
} else if(noAuthType === 'disabled') {
// React doesn't allow to modify props like this.prpos.disabled=ture
// Should clone the element to with inital props
// See: http://stackoverflow.com/questions/32370994/how-to-pass-props-to-this-props-children
return React.cloneElement(super.render(), {disabled:true});
} else {
throw new Error('noAuthType必须是hidden或disabled');
}
}
}
}
return WrappedClass;
}
injectAuth(component)
方法的入参是组件的Class,这里的例子是Button。injectAuth将产生了一个新的Class,这个Class继承了Button Class,所以原本Button组件的行为都会被保留下来。
内部再调用checkAuth方法,检查此外部传进来的auth property是否在windows.globals.permissions是存在的。如果权限检查通过,就直接返回原本Button的render结果:
return super.render();
如果权限检查不通过,則根据noAuthType来决定是要隐藏或者disabled组件。
OK, 有了这个injector后,在我们可以在组件本身都统一置入这样的功能:
class Button {
...
}
export default injectAuth(Button);
这样在个页面使用组件的时候,就不用一一的去写injectAuth的带代码了。