LeetCode-OpenSource / rxjs-hooks

React hooks for RxJS
2.19k stars 83 forks source link

Respond to prop changes with side effect #98

Open OliverJAsh opened 4 years ago

OliverJAsh commented 4 years ago

Sometimes we want to use an observable not to create state but rather just to perform side effects.

This is possible using useObservable:

import React from 'react';
import { EMPTY } from 'rxjs';
import { useObservable } from 'rxjs-hooks';
import { map, mergeMapTo, tap } from 'rxjs/operators';

declare const sideEffect: (number: number) => void;

const MyComponent: React.FC<{ foo: number }> = props => {
  useObservable(
    input$ =>
      input$.pipe(
        map(([foo]) => foo),
        tap(sideEffect),
        mergeMapTo(EMPTY),
      ),
    props.foo,
    [props.foo],
  );

  return <div>Hello, World!</div>;
};

… but it's a bit awkward for a few reasons:

What do you think about another hook which is specifically designed for this use case, to avoid the problems above?

useObservableSideEffect(
  input$ =>
    input$.pipe(
      map(([foo]) => foo),
      tap(sideEffect),
    ),
  [props.foo],
);

I'm sure we can find a better name… 😄

OliverJAsh commented 4 years ago

Something like this?

import { pipeWith } from 'pipe-ts';
import { useObservable } from 'rxjs-hooks';
import { RestrictArray } from 'rxjs-hooks/dist/cjs/type';

import * as Rx from 'shared/facades/rx';

/** `useObservable` is designed for creating state and using that in the component, but most of the
 * time we just want to run side effects. This is a specialised version of `useObservable` that
 * handles those use cases. https://github.com/LeetCode-OpenSource/rxjs-hooks/issues/98 */
export const useObservableSideEffect = <Inputs>(
  runSideEffects: (inputs$: Rx.Observable<RestrictArray<Inputs>>) => Rx.Observable<unknown>,
  inputs: RestrictArray<Inputs>,
): void => {
  useObservable(
    (_state$, inputs$) => pipeWith(inputs$, runSideEffects, Rx.ignoreElements()),
    undefined,
    inputs,
  );
};
Sawtaytoes commented 4 years ago

In Redux-Observable, for side-effects, I always use ignoreElements.

This is a matter of auto-returning the value that goes through the stream. I wrote an article about a possible alternative, but I dunno if that's the intention of the library. It's add more complexity. https://itnext.io/the-best-practice-anti-pattern-5e8bd873aadf?source=rss----5b301f10ddcd---4

Having a hook for side-effects makes more sense because that's the same as useEffect today.