UNPKG

ez-react

Version:

A small react-connector for ez-flux

72 lines (58 loc) 2.39 kB
/* @flow */ /* eslint-disable no-return-assign, react/sort-comp */ import * as React from 'react'; type Store = Object; type Options = { shouldListen: boolean }; type Stores = { [storeName: string]: Store }; type StoreHandler = (Store, props: Object) => Object | void; type StoreHandlers = { store: Store, handler: StoreHandler }[]; type Handler = StoreHandler | string[]; type Handlers = { [storeName: string]: Handler }; type Events = { store: Store, listener: () => void }[]; const handlerErr = '2nd arg must be an object mapping state keys to eventHandlers'; export function normaliseHandler(handler: Handler): StoreHandler { if (typeof handler === 'function') return handler; if (Array.isArray(handler)) { return store => handler.reduce((acc, k) => { acc[k] = store[k]; return acc; }, {}); } throw new Error(handlerErr); } export default function createConnector( stores: Stores, opts: Options = { shouldListen: true }, ): Function { return function connect(Component: Function, handlers: Handlers): Function { if (typeof Component !== 'function') throw new Error('1st arg must be a React Component'); if (!handlers || typeof handlers !== 'object') throw new Error(handlerErr); const storeHandlers: StoreHandlers = Object .keys(handlers) .filter((k) => { if (!stores[k]) throw new Error(`store "${k}" not found`); return true; }) .map(k => ({ handler: normaliseHandler(handlers[k]), store: stores[k] })); return class EZWrapper extends React.Component<Object, Object> { // Set a readable displayName static displayName = `EZWrapper(${Component.name})`; events: Events; constructor(props) { super(props); this.state = {}; } componentWillMount() { this.events = storeHandlers.map(({ store, handler }) => { const listener = () => { const newState: any = handler(store, { ...this.state, ...this.props }); if (newState) this.setState(newState); }; if (opts.shouldListen) store.$on('change', listener); listener(); return { store, listener }; }); } componentWillUnmount() { if (opts.shouldListen) this.events.forEach(e => e.store.$off('change', e.listener)); } render() { return <Component {...this.state} {...this.props} />; } }; }; }