UNPKG

14.5 kBSource Map (JSON)View Raw
1{"version":3,"file":"hooks.module.js","sources":["../src/index.js"],"sourcesContent":["import { options } from 'preact';\n\n/** @type {number} */\nlet currentIndex;\n\n/** @type {import('./internal').Component} */\nlet currentComponent;\n\n/** @type {number} */\nlet currentHook = 0;\n\n/** @type {Array<import('./internal').Component>} */\nlet afterPaintEffects = [];\n\nlet oldBeforeRender = options._render;\nlet oldAfterDiff = options.diffed;\nlet oldCommit = options._commit;\nlet oldBeforeUnmount = options.unmount;\n\nconst RAF_TIMEOUT = 100;\nlet prevRaf;\n\noptions._render = vnode => {\n\tif (oldBeforeRender) oldBeforeRender(vnode);\n\n\tcurrentComponent = vnode._component;\n\tcurrentIndex = 0;\n\n\tconst hooks = currentComponent.__hooks;\n\tif (hooks) {\n\t\thooks._pendingEffects.forEach(invokeCleanup);\n\t\thooks._pendingEffects.forEach(invokeEffect);\n\t\thooks._pendingEffects = [];\n\t}\n};\n\noptions.diffed = vnode => {\n\tif (oldAfterDiff) oldAfterDiff(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks && c.__hooks._pendingEffects.length) {\n\t\tafterPaint(afterPaintEffects.push(c));\n\t}\n};\n\noptions._commit = (vnode, commitQueue) => {\n\tcommitQueue.some(component => {\n\t\ttry {\n\t\t\tcomponent._renderCallbacks.forEach(invokeCleanup);\n\t\t\tcomponent._renderCallbacks = component._renderCallbacks.filter(cb =>\n\t\t\t\tcb._value ? invokeEffect(cb) : true\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tcommitQueue.some(c => {\n\t\t\t\tif (c._renderCallbacks) c._renderCallbacks = [];\n\t\t\t});\n\t\t\tcommitQueue = [];\n\t\t\toptions._catchError(e, component._vnode);\n\t\t}\n\t});\n\n\tif (oldCommit) oldCommit(vnode, commitQueue);\n};\n\noptions.unmount = vnode => {\n\tif (oldBeforeUnmount) oldBeforeUnmount(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks) {\n\t\ttry {\n\t\t\tc.__hooks._list.forEach(invokeCleanup);\n\t\t} catch (e) {\n\t\t\toptions._catchError(e, c._vnode);\n\t\t}\n\t}\n};\n\n/**\n * Get a hook's state from the currentComponent\n * @param {number} index The index of the hook to get\n * @param {number} type The index of the hook to get\n * @returns {import('./internal').HookState}\n */\nfunction getHookState(index, type) {\n\tif (options._hook) {\n\t\toptions._hook(currentComponent, index, currentHook || type);\n\t}\n\tcurrentHook = 0;\n\n\t// Largely inspired by:\n\t// * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs\n\t// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs\n\t// Other implementations to look at:\n\t// * https://codesandbox.io/s/mnox05qp8\n\tconst hooks =\n\t\tcurrentComponent.__hooks ||\n\t\t(currentComponent.__hooks = {\n\t\t\t_list: [],\n\t\t\t_pendingEffects: []\n\t\t});\n\n\tif (index >= hooks._list.length) {\n\t\thooks._list.push({});\n\t}\n\treturn hooks._list[index];\n}\n\n/**\n * @param {import('./index').StateUpdater<any>} initialState\n */\nexport function useState(initialState) {\n\tcurrentHook = 1;\n\treturn useReducer(invokeOrReturn, initialState);\n}\n\n/**\n * @param {import('./index').Reducer<any, any>} reducer\n * @param {import('./index').StateUpdater<any>} initialState\n * @param {(initialState: any) => void} [init]\n * @returns {[ any, (state: any) => void ]}\n */\nexport function useReducer(reducer, initialState, init) {\n\t/** @type {import('./internal').ReducerHookState} */\n\tconst hookState = getHookState(currentIndex++, 2);\n\thookState._reducer = reducer;\n\tif (!hookState._component) {\n\t\thookState._component = currentComponent;\n\n\t\thookState._value = [\n\t\t\t!init ? invokeOrReturn(undefined, initialState) : init(initialState),\n\n\t\t\taction => {\n\t\t\t\tconst nextValue = hookState._reducer(hookState._value[0], action);\n\t\t\t\tif (hookState._value[0] !== nextValue) {\n\t\t\t\t\thookState._value[0] = nextValue;\n\t\t\t\t\thookState._component.setState({});\n\t\t\t\t}\n\t\t\t}\n\t\t];\n\t}\n\n\treturn hookState._value;\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {any[]} args\n */\nexport function useEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 3);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._args = args;\n\n\t\tcurrentComponent.__hooks._pendingEffects.push(state);\n\t}\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {any[]} args\n */\nexport function useLayoutEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 4);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._args = args;\n\n\t\tcurrentComponent._renderCallbacks.push(state);\n\t}\n}\n\nexport function useRef(initialValue) {\n\tcurrentHook = 5;\n\treturn useMemo(() => ({ current: initialValue }), []);\n}\n\n/**\n * @param {object} ref\n * @param {() => object} createHandle\n * @param {any[]} args\n */\nexport function useImperativeHandle(ref, createHandle, args) {\n\tcurrentHook = 6;\n\tuseLayoutEffect(\n\t\t() => {\n\t\t\tif (typeof ref == 'function') ref(createHandle());\n\t\t\telse if (ref) ref.current = createHandle();\n\t\t},\n\t\targs == null ? args : args.concat(ref)\n\t);\n}\n\n/**\n * @param {() => any} factory\n * @param {any[]} args\n */\nexport function useMemo(factory, args) {\n\t/** @type {import('./internal').MemoHookState} */\n\tconst state = getHookState(currentIndex++, 7);\n\tif (argsChanged(state._args, args)) {\n\t\tstate._args = args;\n\t\tstate._factory = factory;\n\t\treturn (state._value = factory());\n\t}\n\n\treturn state._value;\n}\n\n/**\n * @param {() => void} callback\n * @param {any[]} args\n */\nexport function useCallback(callback, args) {\n\tcurrentHook = 8;\n\treturn useMemo(() => callback, args);\n}\n\n/**\n * @param {import('./internal').PreactContext} context\n */\nexport function useContext(context) {\n\tconst provider = currentComponent.context[context._id];\n\t// We could skip this call here, but than we'd not call\n\t// `options._hook`. We need to do that in order to make\n\t// the devtools aware of this hook.\n\tconst state = getHookState(currentIndex++, 9);\n\t// The devtools needs access to the context object to\n\t// be able to pull of the default value when no provider\n\t// is present in the tree.\n\tstate._context = context;\n\tif (!provider) return context._defaultValue;\n\t// This is probably not safe to convert to \"!\"\n\tif (state._value == null) {\n\t\tstate._value = true;\n\t\tprovider.sub(currentComponent);\n\t}\n\treturn provider.props.value;\n}\n\n/**\n * Display a custom label for a custom hook for the devtools panel\n * @type {<T>(value: T, cb?: (value: T) => string | number) => void}\n */\nexport function useDebugValue(value, formatter) {\n\tif (options.useDebugValue) {\n\t\toptions.useDebugValue(formatter ? formatter(value) : value);\n\t}\n}\n\nexport function useErrorBoundary(cb) {\n\tconst state = getHookState(currentIndex++, 10);\n\tconst errState = useState();\n\tstate._value = cb;\n\tif (!currentComponent.componentDidCatch) {\n\t\tcurrentComponent.componentDidCatch = err => {\n\t\t\tif (state._value) state._value(err);\n\t\t\terrState[1](err);\n\t\t};\n\t}\n\treturn [\n\t\terrState[0],\n\t\t() => {\n\t\t\terrState[1](undefined);\n\t\t}\n\t];\n}\n\n/**\n * After paint effects consumer.\n */\nfunction flushAfterPaintEffects() {\n\tafterPaintEffects.some(component => {\n\t\tif (component._parentDom) {\n\t\t\ttry {\n\t\t\t\tcomponent.__hooks._pendingEffects.forEach(invokeCleanup);\n\t\t\t\tcomponent.__hooks._pendingEffects.forEach(invokeEffect);\n\t\t\t\tcomponent.__hooks._pendingEffects = [];\n\t\t\t} catch (e) {\n\t\t\t\tcomponent.__hooks._pendingEffects = [];\n\t\t\t\toptions._catchError(e, component._vnode);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t});\n\tafterPaintEffects = [];\n}\n\n/**\n * Schedule a callback to be invoked after the browser has a chance to paint a new frame.\n * Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after\n * the next browser frame.\n *\n * Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked\n * even if RAF doesn't fire (for example if the browser tab is not visible)\n *\n * @param {() => void} callback\n */\nfunction afterNextFrame(callback) {\n\tconst done = () => {\n\t\tclearTimeout(timeout);\n\t\tcancelAnimationFrame(raf);\n\t\tsetTimeout(callback);\n\t};\n\tconst timeout = setTimeout(done, RAF_TIMEOUT);\n\n\tlet raf;\n\tif (typeof window != 'undefined') {\n\t\traf = requestAnimationFrame(done);\n\t}\n}\n\n// Note: if someone used options.debounceRendering = requestAnimationFrame,\n// then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay.\n// Perhaps this is not such a big deal.\n/**\n * Schedule afterPaintEffects flush after the browser paints\n * @param {number} newQueueLength\n */\nfunction afterPaint(newQueueLength) {\n\tif (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) {\n\t\tprevRaf = options.requestAnimationFrame;\n\t\t(prevRaf || afterNextFrame)(flushAfterPaintEffects);\n\t}\n}\n\n/**\n * @param {import('./internal').EffectHookState} hook\n */\nfunction invokeCleanup(hook) {\n\tif (typeof hook._cleanup == 'function') hook._cleanup();\n}\n\n/**\n * Invoke a Hook's effect\n * @param {import('./internal').EffectHookState} hook\n */\nfunction invokeEffect(hook) {\n\thook._cleanup = hook._value();\n}\n\n/**\n * @param {any[]} oldArgs\n * @param {any[]} newArgs\n */\nfunction argsChanged(oldArgs, newArgs) {\n\treturn !oldArgs || newArgs.some((arg, index) => arg !== oldArgs[index]);\n}\n\nfunction invokeOrReturn(arg, f) {\n\treturn typeof f == 'function' ? f(arg) : f;\n}\n"],"names":["currentIndex","currentComponent","prevRaf","currentHook","afterPaintEffects","oldBeforeRender","options","_render","oldAfterDiff","diffed","oldCommit","_commit","oldBeforeUnmount","unmount","getHookState","index","type","_hook","hooks","__hooks","_list","_pendingEffects","length","push","useState","initialState","useReducer","invokeOrReturn","reducer","init","hookState","_reducer","_component","_value","undefined","action","nextValue","setState","useEffect","callback","args","state","_skipEffects","argsChanged","_args","useLayoutEffect","_renderCallbacks","useRef","initialValue","useMemo","current","useImperativeHandle","ref","createHandle","concat","factory","_factory","useCallback","useContext","context","provider","_id","_context","sub","props","value","_defaultValue","useDebugValue","formatter","useErrorBoundary","cb","errState","componentDidCatch","err","flushAfterPaintEffects","some","component","_parentDom","forEach","invokeCleanup","invokeEffect","e","_catchError","_vnode","hook","_cleanup","oldArgs","newArgs","arg","f","vnode","c","requestAnimationFrame","raf","done","clearTimeout","timeout","cancelAnimationFrame","setTimeout","window","commitQueue","filter"],"mappings":"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"}
\No newline at end of file