Skip to content

Instantly share code, notes, and snippets.

@MHerszak
Last active November 24, 2017 03:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MHerszak/6dbeebf0fba14bc22dabbfefff533575 to your computer and use it in GitHub Desktop.
Save MHerszak/6dbeebf0fba14bc22dabbfefff533575 to your computer and use it in GitHub Desktop.
import React, { Component } from 'react';
import errors from 'feathers-errors';
import { APP_REST } from './../../components/constants';
import { setDisplayName } from './utils';
import { forEach, omit } from './../../utils';
function withStrategyCallback(callback) {
return function stateFunctionHOC(result) {
return function stateFunction(state, props) {
// callback will be false or a callback
return {
data: callback.call(this, result, state, props),
};
};
};
}
function withService(callback) {
const handler = withStrategyCallback(callback);
return function getResult(result) {
return handler(result);
};
}
const defaultEventTypes = ['created', 'updated', 'removed', 'patched'];
/**
* This function is supposed to do ui updates based on a specific data state.
* @param serviceName
* @param options
* @returns {rc}
*/
export default function withServiceEvents(
serviceName,
options = {
// Name for debugging
name: 'default',
// The key for props which will be processed
key: 'data',
// SelectData should load data on componentWillMount
selectData() { console.log('no selectData function'); },
}) {
/**
* The service will be plugged into app context.
*/
return function rc(WrappedComponent) {
// Set context types
const contextTypes = {
...APP_REST
};
/**
* Context of event hoc
*/
class WithServiceEvents extends Component {
static contextTypes = contextTypes;
constructor(props, context) {
super(props, context);
//
this.options = options;
// get Events
const filteredEvents = Object.keys(options).filter((key) => defaultEventTypes.includes(key));
//
this.events = filteredEvents.map(key => key);
// make sure context exists
const { app } = context;
// Is app actually available?
if (!app) {
throw new errors.NotFound(options.noErrMsg ? null : 'No app attached to WithSubscription HOC!');
}
// ... that takes care of the subscription...
this.service = app.service(serviceName);
// set State
this.state = {
data: props[options.key],
};
}
async componentWillMount() {
const { app } = this.context;
const data = await options.selectData(app.service(serviceName), this.props);
// console.log(data);
function updateDataArray(state, props) {
return { data };
}
this.setState(updateDataArray);
}
componentDidMount() {
// register all events and handlers
forEach(this.events, (eventType) => {
// created, updated, patched and removed
this.service.on(eventType, this.handleChange(eventType));
});
}
// componentWillReceiveProps(nextProps) {
// This would be a source to pull in new data and setState
// but what happens is
// #1: You do an optimistic UI update
// #2: ComponentWillReceiveProps will be triggered and sets another state based
// on old data (not a desirable result)
// #3: Component is updated with old data and not the mutated change
//
// What should happen is that all events trigger a redux action call to pull in new data.
// }
componentWillUnmount() {
// remove all events
forEach(this.events, (eventType) => {
// created, updated, patched and removed
this.service.removeListener(eventType, this.handleChange(eventType));
});
}
data = () => ({
data: this.state.data,
});
handleChange = (eventType = '') => (result) => {
const handler = withService(options[eventType]);
//
if (this.component) {
return this.setState(handler(result));
}
};
render() {
const { data } = this.data();
const rest = omit(this.props, 'data');
return (
<WrappedComponent
ref={(ref) => { this.component = ref; }}
data={data}
{...rest}
/>
);
}
}
WithServiceEvents.displayName = `WithServiceEvents(${setDisplayName(WrappedComponent)})`;
return WithServiceEvents;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment