UNPKG

cortina-react

Version:

Coroutine-based EDSL for building React components and applications

121 lines (98 loc) 2.7 kB
import React, { Component } from 'react'; import { Process } from 'cortina'; import { all, race } from 'cortina/src/combinators'; import { getIterator, isFunction } from 'cortina/src/types'; import { emit } from './event'; // export const co = coroutineElement; export function coroutineElement(iterator, children) { return React.createElement( Coroutine, { __iterator: getIterator(iterator) }, ...children ); } // export function coroutineComponent(generator) { return class extends Coroutine { static get displayName() { return generator.name || generator.displayName || 'Anonymous'; } static get coroutine() { return generator; } get [Symbol.iterator]() { return generator(this.props); } }; } class Coroutine extends Component { constructor(props) { super(props); this.state = { view: null }; this.target = this.iterator = this.forceUpdate(props); this.promise = null; this.mounted = false; } componentDidMount() { if (this.mounted) this.forceUpdate(this.props); this.mounted = true; } componentDidUpdate(prevProps) { if (this.props === prevProps) return; this.cancel(); if (this.mounted) this.forceUpdate(this.props, prevProps); } componentWillUnmount() { this.cancel(); this.mounted = false; } cancel() { this.promise && this.promise.cancel(); this.iterator && isFunction(this.iterator.return) && this.iterator.return(); } restart(props, prevProps) { this.iterator = getIterator( props.__iterator || this[Symbol.iterator], props, prevProps ); this.target = this.iterator; return this.iterator; } update = view => { if (this.mounted && this.iterator === this.target) { this.setState({ view }); } }; forceUpdate(props, prevProps) { this.restart(props, prevProps); this.promise && this.promise.cancel(); this.promise = new Process(this.iterator, this._handler).run(); const { onReturn } = props; this.promise.then(r => (isFunction(onReturn) ? onReturn(r) : null)); return this.iterator; } render() { return this.state.view || null; } _handler = query => { if (!query) return; const { onEmit, onYield } = this.props; if (isFunction(onYield)) { onYield(query.event); } if (React.isValidElement(query)) { this.update(query); return; } else if (query instanceof emit) { if (isFunction(onEmit)) onEmit(query.event); return query; } else if (query instanceof all || query instanceof race) { query.handler = this._handler; return query; } else { return query; } }; }