improve decoration with persistent state with react-proxy

This commit is contained in:
Guillermo Rauch 2016-07-08 14:26:23 -07:00
parent d74f8ac1dc
commit 4d99089afb
4 changed files with 96 additions and 22 deletions

View file

@ -1,10 +1,15 @@
import Tabs from './tabs';
import Term from './term';
import Tabs_ from './tabs';
import Term_ from './term';
import RPC from './rpc';
import Mousetrap from 'mousetrap';
import classes from 'classnames';
import shallowCompare from 'react-addons-shallow-compare';
import React, { Component } from 'react';
import decorate from './plugins';
// make subcomponents reload siwth plugin changes
const Tabs = decorate(Tabs_);
const Term = decorate(Term_);
export default class HyperTerm extends Component {
constructor (props) {

View file

@ -1,16 +1,14 @@
import { render } from 'react-dom';
import HyperTerm from './hyperterm';
import HyperTerm_ from './hyperterm';
import React from 'react';
import Config from './config';
import Plugins from './plugins';
import decorate from './plugins';
// make the component reload with plugin changes
const HyperTerm = decorate(HyperTerm_);
require('./css/hyperterm.css');
require('./css/tabs.css');
const app = <Config>
<Plugins>
<HyperTerm />
</Plugins>
</Config>;
const app = <Config><HyperTerm /></Config>;
render(app, document.getElementById('mount'));

View file

@ -11,6 +11,7 @@
"react": "15.1.0",
"react-addons-shallow-compare": "15.1.0",
"react-dom": "15.1.0",
"react-proxy": "1.1.8",
"semver-compare": "1.0.0"
},
"devDependencies": {

View file

@ -1,18 +1,88 @@
import React from 'react';
import { ipcRenderer, remote } from 'electron';
import notify from './notify';
import { createProxy } from 'react-proxy';
export default class Plugins extends React.Component {
// remote interface to `../plugins`
let plugins = remote.require('./plugins');
componentDidMount () {
}
render () {
const child = React.Children.only(this.props.children);
return React.cloneElement(child, this.props);
}
componentWillUnmount () {
// `require`d modules
let modules;
// the fs locations where usr plugins are stored
const { path, localPath } = plugins.getBasePaths();
// where we store the decorated components
let proxies = {};
const clearCache = () => {
// clear require cache
for (const entry in window.require.cache) {
if (entry.indexOf(path) === 0 || entry.indexOf(localPath) === 0) {
// `require` is webpacks', `window.require`, electron's
delete window.require.cache[entry];
}
}
};
const loadModules = () => {
console.log('(re)loading renderer plugins');
const paths = plugins.getPaths();
modules = paths.plugins.concat(paths.localPlugins).map((path) => {
// window.require allows us to ensure this doens't get
// in the way of our build
try {
return window.require(path);
} catch (err) {
const name = remote.require('path').basename(path);
console.error(err.stack);
notify('Plugin load error', `"${name}" failed to load in the renderer process. Check Developer Tools for details.`);
}
});
};
const updateProxy = (name) => {
const [Component, proxy] = proxies[name];
let decorated = Component;
modules.forEach((mod) => {
const decorator = mod[`decorate${name}`];
if (decorator) {
console.log('decorating', name);
decorated = decorator(Component, __webpack_require__);
}
});
if (decorated !== Component) {
proxy.update(decorated);
}
};
const updateProxies = () => {
for (const name in proxies) {
updateProxy(name);
}
};
// load modules for initial decoration
loadModules();
// we want to refresh our modules cache every time
// plugins reload.
// the re-painting happens by the top-level `Config` component
// that reacts to configuration changes and plugin changes
ipcRenderer.on('plugins change', () => {
clearCache();
loadModules();
updateProxies();
});
export default function decorate (Component, props = null) {
const name = Component.name;
if (!proxies[name]) {
const proxy = createProxy(Component);
proxies[name] = [Component, proxy];
updateProxy(name);
}
const [, proxy] = proxies[name];
return proxy.get();
}