Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Avoid effects in useBehaviourSubject in suspense-mode (#521)

authored by

Phil Plückthun and committed by
GitHub
4abf9a6e 3b459f14

+24 -2
+24 -2
src/hooks/useSource.ts
··· 16 16 subscribe, 17 17 } from 'wonka'; 18 18 19 + import { useClient } from '../context'; 20 + 19 21 export const useSource = <T>(source: Source<T>, init: T): T => 20 22 useSubscription( 21 23 useMemo((): Subscription<T> => { 22 24 let hasUpdate = false; 23 25 let currentValue: T = init; 24 26 27 + // Wrap the source and track its `currentValue` 25 28 const updateValue = pipe( 26 29 source, 27 30 onPush(value => { ··· 30 33 ); 31 34 32 35 return { 36 + // getCurrentValue may be implemented by subscribing to the 37 + // given source and immediately unsubscribing. Only synchronous 38 + // values will therefore reach our `onPush` callback. 33 39 getCurrentValue(): T { 34 40 if (!hasUpdate) publish(updateValue).unsubscribe(); 35 41 return currentValue; 36 42 }, 43 + // subscribe is just a regular subscription, but it also tracks 44 + // `hasUpdate`. If we're subscribed and receive a new value we 45 + // set `hasUpdate` to avoid `getCurrentValue` trying to subscribe 46 + // again. 37 47 subscribe(onValue: () => void): Unsubscribe { 38 48 hasUpdate = true; 39 49 const { unsubscribe } = pipe(updateValue, subscribe(onValue)); ··· 47 57 ); 48 58 49 59 export const useBehaviourSubject = <T>(value: T) => { 60 + const client = useClient(); 61 + 50 62 const state = useMemo((): [Source<T>, (value: T) => void] => { 51 63 let prevValue = value; 52 64 ··· 56 68 map(() => prevValue) 57 69 ); 58 70 71 + // This turns the subject into a behaviour subject that returns 72 + // the last known value (or the initial value) synchronously 59 73 const source = concat([prevValue$, subject.source]); 60 74 61 75 const next = (value: T) => { 62 - subject.next((prevValue = value)); 76 + // We can use the latest known value to also deduplicate next calls. 77 + if (value !== prevValue) { 78 + subject.next((prevValue = value)); 79 + } 63 80 }; 64 81 65 82 return [source, next]; 66 83 }, []); 67 84 85 + // NOTE: This is a special case for client-side suspense. 86 + // We can't trigger suspense inside an effect but only in the render function. 87 + // So we "deopt" to not using an effect if the client is in suspense-mode. 68 88 useEffect(() => { 69 - state[1](value); 89 + if (!client.suspense) state[1](value); 70 90 }, [state, value]); 91 + 92 + if (client.suspense) state[1](value); 71 93 72 94 return state; 73 95 };