diff --git a/package.json b/package.json index 9a87bf0..bef3b12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "suspense-service", - "version": "0.2.7", + "version": "0.2.8", "description": "Suspense integration library for React", "repository": "github:patrickroberts/suspense-service", "main": "dst/cjs/suspense-service.js", diff --git a/src/IdContext/Consumer/index.tsx b/src/IdContext/Consumer/index.tsx index 2812983..8b95edd 100644 --- a/src/IdContext/Consumer/index.tsx +++ b/src/IdContext/Consumer/index.tsx @@ -12,15 +12,15 @@ export function createIdContextConsumer( { Consumer }: Context>, ): IdContextConsumer { const EnvironmentConsumer: IdContextConsumer = ({ id, children }) => { - const render = useCallback((env: Environment) => { - const value = unwrap(env, id); + const render = useCallback( + (env: Environment) => children(unwrap(env, id)), + [id, children], + ); - return children(value); - }, [id, children]); - - return useMemo(() => ( - {render} - ), [render]); + return useMemo( + () => {render}, + [render], + ); }; EnvironmentConsumer.defaultProps = defaultProps; diff --git a/src/IdContext/Environment.ts b/src/IdContext/Environment.ts index b34d96a..3ef62a3 100644 --- a/src/IdContext/Environment.ts +++ b/src/IdContext/Environment.ts @@ -2,25 +2,18 @@ import Id from './Id'; /** @internal */ export default interface Environment { - has (key: Id): boolean; - get (key: Id): T | undefined; - [Symbol.iterator] (): IterableIterator<[Id, T]>; + has(key: Id): boolean; + get(key: Id): T | undefined; + [Symbol.iterator](): IterableIterator<[Id, T]>; } /** @ignore */ -export function wrap( - env: Environment, - value: T, - id: Id = null, -): Environment { +export function wrap(env: Environment, value: T, id: Id = null): Environment { return new Map(env).set(id, value).set(null, value); } /** @ignore */ -export function unwrap( - env: Environment, - id: Id = null, -): T { +export function unwrap(env: Environment, id: Id = null): T { if (!env.has(id)) { throw new Error(`Provider with id ${String(id)} is not in scope`); } diff --git a/src/IdContext/Provider/index.tsx b/src/IdContext/Provider/index.tsx index 9b4d225..c5df877 100644 --- a/src/IdContext/Provider/index.tsx +++ b/src/IdContext/Provider/index.tsx @@ -14,13 +14,15 @@ export function createIdContextProvider( const { Provider } = EnvironmentContext; const EnvironmentProvider: IdContextProvider = ({ value, id, children }) => { const prev = useContext(EnvironmentContext); - const next = useMemo(() => ( - wrap(prev, value, id) - ), [value, id, prev]); + const next = useMemo( + () => wrap(prev, value, id), + [value, id, prev], + ); - return useMemo(() => ( - {children} - ), [children, next]); + return useMemo( + () => {children}, + [children, next], + ); }; EnvironmentProvider.defaultProps = defaultProps; diff --git a/src/Service/Consumer/index.tsx b/src/Service/Consumer/index.tsx index 9516b53..c8ebc2b 100644 --- a/src/Service/Consumer/index.tsx +++ b/src/Service/Consumer/index.tsx @@ -16,17 +16,13 @@ export function createServiceConsumer( const ResourceConsumer: ServiceConsumer = ({ id, children }) => { const render = useCallback(( resourceAndSetState: [Resource, Dispatch>], - ) => { - const resource = resourceAndSetState[0]; - const setState = resourceAndSetState[1]; - const response = resource(); + ) => children(resourceAndSetState[0](), resourceAndSetState[1]), + [children]); - return children(response, setState); - }, [children]); - - return useMemo(() => ( - {render} - ), [id, render]); + return useMemo( + () => {render}, + [id, render], + ); }; ResourceConsumer.defaultProps = defaultProps; diff --git a/src/Service/Handler.ts b/src/Service/Handler.ts index 28d4eff..583b8a4 100644 --- a/src/Service/Handler.ts +++ b/src/Service/Handler.ts @@ -31,13 +31,10 @@ export function createUseHandler( state = { promise, status: Status.Pending }; return () => { - const { status } = state; - switch (state.status) { case Status.Pending: throw state.promise; case Status.Fulfilled: return state.value; - case Status.Rejected: throw state.reason; - default: throw new Error(`Unexpected status ${status}`); + default: throw state.reason; } }; }, [request, id]); diff --git a/src/Service/Provider/index.tsx b/src/Service/Provider/index.tsx index 13e7914..60d07db 100644 --- a/src/Service/Provider/index.tsx +++ b/src/Service/Provider/index.tsx @@ -19,18 +19,17 @@ export function createServiceProvider( request, id, children, fallback, reset, }) => { const stateAndSetState = useResetState(request, reset); - const state = stateAndSetState[0]; + const resource = useHandler(stateAndSetState[0], id); const setState = stateAndSetState[1]; - const resource = useHandler(state, id); - const element = useMemo(() => ( - fallback != null - ? {children} - : children - ), [children, fallback]); + const element = useMemo( + () => (fallback == null ? children : {children}), + [children, fallback], + ); - return useMemo(() => ( - {element} - ), [resource, setState, id, element]); + return useMemo( + () => {element}, + [resource, setState, id, element], + ); }; ResourceProvider.defaultProps = defaultProps; diff --git a/src/Service/Status.ts b/src/Service/Status.ts index ed24316..9c2160f 100644 --- a/src/Service/Status.ts +++ b/src/Service/Status.ts @@ -2,7 +2,7 @@ const enum Status { Pending, Fulfilled, - Rejected + Rejected, } export default Status; diff --git a/src/Service/index.ts b/src/Service/index.ts index 45ddaaf..e2e7361 100644 --- a/src/Service/index.ts +++ b/src/Service/index.ts @@ -35,7 +35,7 @@ export function createService( handler: Handler, ): Service { const ResourceContext = createIdContext<[ - Resource, Dispatch> + Resource, Dispatch>, ]>(defaultValue); return { @@ -54,11 +54,8 @@ export function useServiceState( service: Service, id: Id = null, ): [TResponse, Dispatch>] { const resourceAndSetState = useIdContext(service[kResource], id); - const resource = resourceAndSetState[0]; - const setState = resourceAndSetState[1]; - const response = resource(); - return [response, setState]; + return [resourceAndSetState[0](), resourceAndSetState[1]]; } /** diff --git a/src/State/useResetReducer.ts b/src/State/useResetReducer.ts index 0599e6f..52f615f 100644 --- a/src/State/useResetReducer.ts +++ b/src/State/useResetReducer.ts @@ -20,8 +20,12 @@ export default function useResetReducer>( initialState, state: initialState, dispatch: (action: ReducerAction) => { - current.state = current.reducer(current.state, action); - forceUpdate(); + const nextState = current.reducer(current.state, action); + + if (!Object.is(current.state, nextState)) { + current.state = nextState; + forceUpdate(); + } }, }); diff --git a/src/StateContext/Consumer/index.tsx b/src/StateContext/Consumer/index.tsx index 66b65d7..a40165b 100644 --- a/src/StateContext/Consumer/index.tsx +++ b/src/StateContext/Consumer/index.tsx @@ -14,12 +14,7 @@ export function createStateContextConsumer( ): StateContextConsumer { const StateConsumer: StateContextConsumer = ({ id, children }) => { const render = useCallback( - (stateAndSetState: State) => { - const state = stateAndSetState[0]; - const setState = stateAndSetState[1]; - - return children(state, setState); - }, + (stateAndSetState: State) => children(stateAndSetState[0], stateAndSetState[1]), [children], ); diff --git a/src/StateContext/Provider/index.tsx b/src/StateContext/Provider/index.tsx index 06b0142..59ed8c6 100644 --- a/src/StateContext/Provider/index.tsx +++ b/src/StateContext/Provider/index.tsx @@ -17,9 +17,10 @@ export function createStateContextProvider( const StateProvider: StateContextProvider = ({ value, id, children, reset }) => { const state = useResetState(value, reset); - return useMemo(() => ( - {children} - ), [state, id, children]); + return useMemo( + () => {children}, + [state, id, children], + ); }; StateProvider.defaultProps = defaultProps; diff --git a/src/StateContext/index.ts b/src/StateContext/index.ts index bcafb7f..f64404e 100644 --- a/src/StateContext/index.ts +++ b/src/StateContext/index.ts @@ -20,6 +20,8 @@ export default interface StateContext { [kState]: IdContext>; } +const defaultDispatch = () => undefined; + /** * Creates a State Context for providing a stateful value and a function to update it. * @param defaultValue the value consumed if no {@link StateContextProvider} is in scope and the @@ -27,7 +29,7 @@ export default interface StateContext { */ export function createStateContext(defaultValue: T): StateContext { const StateContext = createIdContext>( - [defaultValue, () => undefined], + [defaultValue, defaultDispatch], ); return {