Open jameslaneconkling opened 5 years ago
the author has discontinued support for recompose and recommended react hook as a replacement.
Understood, and I think that's a fine choice. I'm just not sure how (or even if, given the current state of the hooks API) it would be possible to recreate the functionality of mapPropsStream using hooks.
At issue is delaying the component's render from when it receives props to when the observable emits (which may be immediate or not). The current recompose implementation, and the above simplified reimplementation, essentially achieve that by using shouldComponentUpdate
. Deferring rendering is not something that's supported by hooks atm, though I suspect the suspense API is intended for this type of use case.
@jameslaneconkling Did you manage to recreate the functionality of mapPropsStream using hooks? I am looking for this
@gemma-ferreras the solution I've ended up w/ looks like:
const useStream = <T, R>(
project: (stream$: Observable<T>) => Observable<R>,
data: T,
): R | undefined => {
const prev = useRef<T>()
const stream$ = useRef(new Subject<T>())
const emit = useRef<R>()
const synchronous = useRef(true)
const [_, rerender] = useState(false)
useLayoutEffect(() => {
const subscription = stream$.current.pipe(project).subscribe({
next: (next) => {
emit.current = next
if (!synchronous.current) {
rerender((prev) => !prev)
}
}
})
stream$.current.next(data)
return () => subscription.unsubscribe()
}, [])
synchronous.current = true
if (prev.current !== data) {
emit.current = undefined
stream$.current.next(data)
}
prev.current = data
synchronous.current = false
return emit.current
}
It's a little bit wordier than I'd hoped, but essentially subscribes to a stream for the lifecycle of the component, while ensuring that synchronous emits don't render twice. To use:
export const Widget: SFC<{}> = () => {
const [channel, setChannel] = useState('friend-list')
const selectChannel = useCallback(({ target: { value } }) => setChannel(value), [])
const next = useStream((stream$) => stream$.pipe(
switchMap(() => interval(500).pipe(
startWith(-1),
scan<number, number[]>((data) => [...data, Math.floor(Math.random() * 10)], []),
take(4),
)),
), channel)
return el('div', null,
el('div', null,
el('select', { value: channel, onChange: selectChannel },
el('option', { value: 'friend-list' }, 'Friends'),
el('option', { value: 'enemy-list' }, 'Enemies'),
el('option', { value: 'grocery-list' }, 'Groceries'))),
el('h1', null, channel),
el('ul', null, ...(next || []).map((item, idx) => (
el('li', { key: idx }, item))))
)
}
(accidentally fat finger closed this--just reopened)
As a result of React's deprecation of the
componentWillMount
andcomponentWillReceiveProps
lifecycle hooks, recompose'smapPropsStream
now warnsI've been looking for an equivalent implementation using either Hooks, the new Suspense API, or new lifecycle methods, without much luck. The following works using the unsafe lifecycle methods
However, I haven't been able to accomplish the same thing without
UNSAFE_componentWillReceiveProps
. Given that renders are triggered by changes to theprops$
props stream, and not the props themselves, I suspect that the Suspense API could be helpful. Curious if anyone else is tackling the same issue.