我有一个使用React / Redux的应用程序。我使用Electron将其打包为独立应用程序。我目前正在构建基础,以允许用户将自己的插件或小部件添加到应用程序。由于我使用Redux,我希望继续使用Redux来跟踪安装的内容以及它为应用程序带来的更改/新功能。
在深入探讨我的问题之前,让我们举个例子。我的核心应用程序有一个带有"控制"的侧边栏。纽扣。核心有两个按钮,但我想让插件在那里添加自己的按钮。当然,手动我可以继续,在按钮侧边栏组件的js文件中导入插件并手动添加按钮,但这会破坏可插拔性的目的。因此,我能想到的最好和最简单的解决方案是从插件代码中调度一个动作,例如ADD_SIDEBAR_BUTTON,它基本上将一个带有按钮元数据的对象添加到一个按钮数组(包括那些核心按钮)。 像这样:
store.dispatch({type: "ADD_SIDEBAR_BUTTON", payload: {type: "button", path: "goSomewhere", label: "A label"}});
虽然这可以工作,但它限制了按钮可以做的很多,因为按钮的实际逻辑和jsx将来自核心应用程序。 在实践中,我只是渴望让插件传递一个React组件,这将使插件在事件,生命周期的完全控制等方面具有很大的自由度......但是传递除了普通对象之外的任何东西都是一个Redux no-no。
另一个解决方案是让插件开发人员创建一个带有导出类的文件,并使用redux提供它的路径,如下所示:
class MyPluginButton extends Component {
handleClick() => evt => {
// do my special things
}
render() {
return <button onClick={this.handleClick}>My Special Button</button>
}
}
export default MyPluginButton;
然后插件的initialize()函数将调度操作并传递核心导入按钮所需的信息(如果没有正确完成,我也可以考虑安全问题):
store.dispatch({type: "ADD_SIDEBAR_BUTTON", payload: {path: "myplugin/src/components/MyPluginButton"}});
现在我的问题是,有没有人有好的想法如何实现/应该实施?我想确保我没有错过任何其他模式或实现。
答案 0 :(得分:0)
我最终做的是一个pluginRegistration模块,如下所示,在博客上找到的基本想法我再也找不到了:
const _registeredComponents = {};
export const registerComponent = (pluginName, component, injectAction) => {
_registeredComponents[`plugin_${pluginName}_${component.PLUGIN_COMPONENT_NAME}`] = {pluginName: pluginName, component: component, action: injectAction};
};
export const getRegisteredComponents = () => {
return {..._registeredComponents};
};
export const getRegisteredComponent = fullPluginComponentName => {
return _registeredComponents[fullPluginComponentName].component;
}
想要添加的组件使用上面的函数注册自己并传递一个动作。从我在ComponentDidMount中的主要App组件,然后循环遍历_registeredComponents中的所有条目,调度沿组件类ref传递的操作,将我的按钮(在商店中,键字符串)添加到现有的ControlButtons组件。之后,父控件按钮组件从状态获取fullPluginComponentName,并从引用中实例化组件。瞧,使用Redux进行完全动态的React组件实例化/注入,但只保留商店中的普通对象。
我的按钮组件非常普通,除了添加了静态getter(使用constructor.name很诱人,但是当缩小JS时,名称会丢失并且可能会导致问题。)
export class MyButtonComponent extends Component {
static get PLUGIN_COMPONENT_NAME() {
return "MyButtonComponent";
}
render() {
return (<button>My Useless Button</button>);
}
}
在我创建我的按钮以注入React类的同一个文件中,我只需注册该组件,如下所示:
registerComponent("MyPluginName", MyButtonComponent, actions.addButtonToControls);
这将插件名称,实际组件类引用和我想要触发的操作绑定在一起。
为了获取可供使用的组件条目的键,我的主App组件在安装后循环注册组件条目:
class App extends Component {
componentDidMount() {
let pluginComponents = getRegisteredComponents();
for (let fullPluginComponentName in pluginComponents) {
let entry = pluginComponents[fullPluginComponentName];
this.props.dispatch({type: entry.action, payload: {pluginName: entry.pluginName, fullPluginNameComponent: fullPluginComponentName}});
}
}
render() { ... }
}
这将有效的方法是将组件键添加到商店中,以便稍后在ControlButtons(一个动态加载按钮数组的容器)中获取类引用。 这是后者如何从商店加载它们的简要版本:
class ControlButtons extends Component {
getDynamicButtons() {
// this.props.dynamicButtons is an array stored in redux and populated from the looped dispatch-es calls for each registered component.
return this.props.dynamicButtons.map(componentEntry => {
let MyDynamicButton = getRegisteredComponent(componentEntry.fullPluginComponentName);
return <MyDynamicButton />;
});
}
render() {
<div>
{this.getDynamicButtons()}
</div>
}
}
当然,不知何故,必须导入包含按钮类定义的文件。这是我旅程的下一步,我可能还会拥有一系列模块/文件名,并使用require加载它们。但这是另一个故事。