UNPKG

rax

Version:

A universal React-compatible render engine.

86 lines (76 loc) 2.37 kB
import invokeFunctionsWithContext from './invokeFunctionsWithContext'; import { useState, useLayoutEffect } from './hooks'; import { isFunction } from './types'; import toArray from './toArray'; import getNearestParent from './vdom/getNearestParent'; let id = 0; export default function createContext(defaultValue) { const contextID = '_c' + id++; // Provider Component class Provider { constructor() { this.__contextID = contextID; this.__handlers = []; } __on(handler) { this.__handlers.push(handler); } __off(handler) { this.__handlers = this.__handlers.filter(h => h !== handler); } // Like getChildContext but called in SSR _getChildContext() { return { [contextID]: this }; } // `getValue()` called in rax-server-renderer getValue() { return this.props.value !== undefined ? this.props.value : defaultValue; } componentDidUpdate(prevProps) { if (this.props.value !== prevProps.value) { invokeFunctionsWithContext(this.__handlers, null, this.getValue()); } } render() { return this.props.children; } } function getNearestParentProvider(instance) { return getNearestParent(instance, parent => parent.__contextID === contextID); } // Consumer Component function Consumer(props, context) { // Current `context[contextID]` only works in SSR const [provider] = useState(() => context[contextID] || getNearestParentProvider(this)); let value = provider ? provider.getValue() : defaultValue; const [prevValue, setValue] = useState(value); if (value !== prevValue) { setValue(value); return; // Interrupt execution of consumer. } useLayoutEffect(() => { if (provider) { provider.__on(setValue); return () => { provider.__off(setValue); }; } }, []); // Consumer requires a function as a child. // The function receives the current context value. const consumer = toArray(props.children)[0]; if (isFunction(consumer)) { return consumer(value); } } return { Provider, Consumer, // `_contextID` and `_defaultValue` accessed in rax-server-renderer _contextID: contextID, _defaultValue: defaultValue, __getNearestParentProvider: getNearestParentProvider, }; }