Update plugins dev documentation
Description
We should update the plugins dev documentation including information of latest improvements:
- [ ] review code style (eg, use of function instead of classes, import/export instead of require ...)
- [ ] how to use of createPlugin function
- [ ] how to create of a plugin
- [ ] how to create of a map support plugin
- [ ] how to access context properties available in a plugin (eg, messages, ...)
- [ ] how to access to items and containers concept and properties
- [ ] use of react lazy and suspense to optimize the loading of component dependencies
- [ ] use of new store api
Documentation section involved
- [x] Developer Guide
Other useful information
Technically with the new store api we could move the reducers/epics registration in a component lifecycle. This could be a good option in case we add heavy dependencies in reducers or epics and in this way we could dynamcally load everything when the plugin component is actually mounted in the UI.
In this example below show a possible implementation where the registration of reducers and plugin is done via an useStoreManager hook. The Example plugin could be moved to another file and loaded with lazy and suspense with this kind of approach. The code below has not being tested yet and it will not work in an extension because StateUtils is only accessible to the main bundle.
import { useEffect } from 'react';
import { normalizeName } from '../utils/PluginsUtils';
import { getStore } from '../utils/StateUtils';
const useStoreManager = (name, {
reducers,
epics,
removeReducers,
muteEpics
}, dependencies = []) => {
useEffect(() => {
const key = normalizeName(name);
const store = getStore();
if (reducers) {
Object.keys(reducers).forEach((reducerKey) => store.storeManager.addReducer(reducerKey, reducers[reducerKey]));
}
if (epics) {
store.storeManager.addEpics(key, epics);
store.storeManager.unmuteEpics(key);
}
return () => {
if (reducers && removeReducers) {
Object.keys(reducers).forEach((reducerKey) => store.storeManager.removeReducer(reducerKey));
}
if (epics && muteEpics) {
store.storeManager.muteEpics(key);
}
};
}, dependencies);
};
export default useStoreManager;
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { Observable } from 'rxjs';
import { createPlugin, normalizeName } from '../utils/PluginsUtils';
import useStoreManager from '../hooks/useStoreManager';
const CLICK = 'CLICK:TEST_ACTION';
const click = () => ({ type: CLICK });
const reducers = {
example: function mapviews(state = {}, action) {
switch (action.type) {
case CLICK:
return { ...state, count: (state.count || 0) + 1 };
default:
return state;
}
}
};
const epics = {
exampleStream: (action$) => action$.ofType(CLICK)
.switchMap((action) => {
console.log(action);
return Observable.empty();
})
};
const PLUGIN_NAME = 'Example';
function Example({ onClick }) {
useStoreManager({
name: PLUGIN_NAME,
reducers,
epics
});
return (
<button id="example" onClick={onClick} style={{ position: 'absolute', zIndex: 1000 }}>
TEST
</button>);
}
const ConnectedExample = connect(() => ({}), { onClick: click })(Example);
export default createPlugin(PLUGIN_NAME, {
component: ConnectedExample
});