persisted plugins state
Summary: Two pros are passed into every plugin to persist state: - `this.props.persistedState` which is the object of the persisted state - `this.props.setPersistedState` which can be used to modify the persisted state The state itself is stored in redux and therefore persisted when switching plugins. The lifecycle hooks used a HOC are now implemented by the `ref`-function, which makes the code a little cleaner. Reviewed By: jknoxville Differential Revision: D8752097 fbshipit-source-id: d4f081f149cd840a29f1132bde91d72d3fba67ed
This commit is contained in:
committed by
Facebook Github Bot
parent
d0ecb46d64
commit
f5dcaf02a4
@@ -8,7 +8,6 @@ import type {SonarPlugin, SonarBasePlugin} from './plugin.js';
|
|||||||
import type LogManager from './fb-stubs/Logger';
|
import type LogManager from './fb-stubs/Logger';
|
||||||
import type Client from './Client.js';
|
import type Client from './Client.js';
|
||||||
import type BaseDevice from './devices/BaseDevice.js';
|
import type BaseDevice from './devices/BaseDevice.js';
|
||||||
import type {Props as PluginProps} from './plugin.js';
|
|
||||||
|
|
||||||
import {SonarDevicePlugin} from './plugin.js';
|
import {SonarDevicePlugin} from './plugin.js';
|
||||||
import {ErrorBoundary, Component, FlexColumn, FlexRow, colors} from 'sonar';
|
import {ErrorBoundary, Component, FlexColumn, FlexRow, colors} from 'sonar';
|
||||||
@@ -51,78 +50,67 @@ type State = {
|
|||||||
pluginKey: string,
|
pluginKey: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
function withPluginLifecycleHooks(
|
function computeState(props: Props): State {
|
||||||
PluginComponent: Class<SonarBasePlugin<>>,
|
// plugin changed
|
||||||
target: Client | BaseDevice,
|
let activePlugin = devicePlugins.find(
|
||||||
) {
|
(p: Class<SonarDevicePlugin<>>) => p.id === props.selectedPlugin,
|
||||||
return class extends React.Component<PluginProps<any>> {
|
);
|
||||||
plugin: ?SonarBasePlugin<>;
|
let target = props.selectedDevice;
|
||||||
|
let pluginKey = 'unknown';
|
||||||
static displayName = `${PluginComponent.title}Plugin`;
|
if (activePlugin) {
|
||||||
|
pluginKey = `${props.selectedDevice.serial}#${activePlugin.id}`;
|
||||||
componentDidMount() {
|
} else {
|
||||||
const {plugin} = this;
|
target = props.clients.find(
|
||||||
if (plugin) {
|
(client: Client) => client.id === props.selectedApp,
|
||||||
activateMenuItems(plugin);
|
);
|
||||||
plugin._setup(target);
|
activePlugin = plugins.find(
|
||||||
plugin._init();
|
(p: Class<SonarPlugin<>>) => p.id === props.selectedPlugin,
|
||||||
}
|
);
|
||||||
}
|
if (!activePlugin || !target) {
|
||||||
|
throw new Error(
|
||||||
componentWillUnmount() {
|
`Plugin "${props.selectedPlugin || ''}" could not be found.`,
|
||||||
if (this.plugin) {
|
|
||||||
this.plugin._teardown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<PluginComponent
|
|
||||||
ref={(ref: ?SonarBasePlugin<>) => {
|
|
||||||
if (ref) {
|
|
||||||
this.plugin = ref;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{...this.props}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
pluginKey = `${target.id}#${activePlugin.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
activePlugin,
|
||||||
|
target,
|
||||||
|
pluginKey,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class PluginContainer extends Component<Props, State> {
|
class PluginContainer extends Component<Props, State> {
|
||||||
static getDerivedStateFromProps(props: Props) {
|
plugin: ?SonarBasePlugin<>;
|
||||||
let activePlugin = devicePlugins.find(
|
|
||||||
(p: Class<SonarDevicePlugin<>>) => p.id === props.selectedPlugin,
|
|
||||||
);
|
|
||||||
let target = props.selectedDevice;
|
|
||||||
let pluginKey = 'unknown';
|
|
||||||
if (activePlugin) {
|
|
||||||
pluginKey = `${props.selectedDevice.serial}#${activePlugin.id}`;
|
|
||||||
} else {
|
|
||||||
target = props.clients.find(
|
|
||||||
(client: Client) => client.id === props.selectedApp,
|
|
||||||
);
|
|
||||||
activePlugin = plugins.find(
|
|
||||||
(p: Class<SonarPlugin<>>) => p.id === props.selectedPlugin,
|
|
||||||
);
|
|
||||||
if (!activePlugin || !target) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
pluginKey = `${target.id}#${activePlugin.id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
constructor(props: Props) {
|
||||||
pluginKey,
|
super();
|
||||||
activePlugin,
|
this.state = computeState(props);
|
||||||
target,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
pluginKey: 'unknown',
|
if (
|
||||||
activePlugin: null,
|
nextProps.selectedDevice !== this.props.selectedDevice ||
|
||||||
target: null,
|
nextProps.selectedApp !== this.props.selectedApp ||
|
||||||
|
nextProps.selectedPlugin !== this.props.selectedPlugin
|
||||||
|
) {
|
||||||
|
this.setState(computeState(nextProps));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refChanged = (ref: ?SonarBasePlugin<>) => {
|
||||||
|
if (this.plugin) {
|
||||||
|
this.plugin._teardown();
|
||||||
|
this.plugin = null;
|
||||||
|
}
|
||||||
|
const {target} = this.state;
|
||||||
|
if (ref && target) {
|
||||||
|
activateMenuItems(ref);
|
||||||
|
ref._setup(target);
|
||||||
|
ref._init();
|
||||||
|
this.plugin = ref;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -142,15 +130,13 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
activePlugin.title
|
activePlugin.title
|
||||||
}" encountered an error during render`}
|
}" encountered an error during render`}
|
||||||
logger={this.props.logger}>
|
logger={this.props.logger}>
|
||||||
{React.createElement(
|
{React.createElement(activePlugin, {
|
||||||
withPluginLifecycleHooks(activePlugin, target),
|
key: pluginKey,
|
||||||
{
|
logger: this.props.logger,
|
||||||
key: pluginKey,
|
persistedState: pluginStates[pluginKey] || {},
|
||||||
logger: this.props.logger,
|
setPersistedState: state => setPluginState({pluginKey, state}),
|
||||||
persistedState: pluginStates[pluginKey],
|
ref: this.refChanged,
|
||||||
setPersistedState: state => setPluginState({pluginKey, state}),
|
})}
|
||||||
},
|
|
||||||
)}
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Container>
|
</Container>
|
||||||
<SidebarContainer id="sonarSidebar" />
|
<SidebarContainer id="sonarSidebar" />
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ export default function reducer(
|
|||||||
if (action.type === 'SET_PLUGIN_STATE') {
|
if (action.type === 'SET_PLUGIN_STATE') {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[action.payload.pluginKey]: action.payload.state,
|
[action.payload.pluginKey]: {
|
||||||
|
...state[action.payload.pluginKey],
|
||||||
|
...action.payload.state,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return state;
|
return state;
|
||||||
|
|||||||
Reference in New Issue
Block a user