diff --git a/CHANGELOG.md b/CHANGELOG.md index 53137bf..7e0e428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log ## [Unreleased] +### Changed +- No useLayoutEffect for invoking listeners (which leads de-opt sync mode) ## [0.6.0] - 2019-07-15 ### Changed diff --git a/src/Provider.js b/src/Provider.js index 64b2133..c10c288 100644 --- a/src/Provider.js +++ b/src/Provider.js @@ -5,8 +5,6 @@ import { useRef, } from 'react'; -import { useIsomorphicLayoutEffect } from './utils'; - // ------------------------------------------------------- // context // ------------------------------------------------------- @@ -54,9 +52,11 @@ export const createProvider = (customContext, customUseValue) => ({ } const [state, dispatch] = useValue(); const listeners = useRef([]); - useIsomorphicLayoutEffect(() => { - listeners.current.forEach(listener => listener(state)); - }, [state]); + // we call listeners in render intentionally. + // listeners are not technically pure, but + // otherwise we can't get benefits from concurrent mode. + // we make sure to work with double or more invocation of listeners. + listeners.current.forEach(listener => listener(state)); const subscribe = useCallback((listener) => { listeners.current.push(listener); const unsubscribe = () => { diff --git a/src/useSelector.js b/src/useSelector.js index bb7064f..c7089e9 100644 --- a/src/useSelector.js +++ b/src/useSelector.js @@ -1,11 +1,9 @@ import { useContext, - useEffect, useRef, } from 'react'; import { defaultContext } from './Provider'; - import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; const defaultEqualityFn = (a, b) => a === b; @@ -26,23 +24,20 @@ export const createUseSelector = customContext => ( selected, }; }); - useEffect(() => { + useIsomorphicLayoutEffect(() => { const callback = (nextState) => { - if (ref.current.state === nextState) return; - let changed; try { - changed = !ref.current.equalityFn(ref.current.selected, ref.current.selector(nextState)); + if (ref.current.state === nextState + || ref.current.equalityFn(ref.current.selected, ref.current.selector(nextState))) { + // not changed + return; + } } catch (e) { - changed = true; // stale props or some other reason - } - if (changed) { - ref.current.state = nextState; - forceUpdate(); + // ignored (stale props or some other reason) } + forceUpdate(); }; const unsubscribe = subscribe(callback); - // force update in case the state is already changed - forceUpdate(); return unsubscribe; }, [subscribe, forceUpdate]); return selected; diff --git a/src/useTrackedState.js b/src/useTrackedState.js index 1b5d218..bd922ef 100644 --- a/src/useTrackedState.js +++ b/src/useTrackedState.js @@ -1,16 +1,12 @@ import { useContext, - useEffect, useMemo, useRef, } from 'react'; import { defaultContext } from './Provider'; - import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; - import { createDeepProxy, isDeepChanged } from './deepProxy'; - import { createUseDispatch } from './useDispatch'; export const createUseTrackedState = customContext => (opts = {}) => { @@ -31,23 +27,22 @@ export const createUseTrackedState = customContext => (opts = {}) => { /* eslint-enable no-nested-ternary, indent, @typescript-eslint/indent */ }; }); - useEffect(() => { + useIsomorphicLayoutEffect(() => { const callback = (nextState) => { - const changed = isDeepChanged( - lastTracked.current.state, - nextState, - lastTracked.current.affected, - lastTracked.current.cache, - lastTracked.current.assumeChangedIfNotAffected, - ); - if (changed) { - lastTracked.current.state = nextState; - forceUpdate(); + if (lastTracked.current.state === nextState + || !isDeepChanged( + lastTracked.current.state, + nextState, + lastTracked.current.affected, + lastTracked.current.cache, + lastTracked.current.assumeChangedIfNotAffected, + )) { + // not changed + return; } + forceUpdate(); }; const unsubscribe = subscribe(callback); - // force update in case the state is already changed - forceUpdate(); return unsubscribe; }, [subscribe, forceUpdate]); const proxyCache = useRef(new WeakMap()); // per-hook proxyCache