Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respond to prop changes with side effect #98

Open
OliverJAsh opened this issue Nov 27, 2019 · 2 comments
Open

Respond to prop changes with side effect #98

OliverJAsh opened this issue Nov 27, 2019 · 2 comments

Comments

@OliverJAsh
Copy link
Contributor

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:

  • We are forced to return an Observable of a certain type (State), when the type doesn't matter to us, since the result is not being used outside of the hook. We need to do mergeMapTo(EMPTY) at the end of the chain, to satisfy the return type.
  • We are forced to provide an initial state, but again we are not using State here.

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
Copy link
Contributor Author

OliverJAsh commented Feb 7, 2020

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
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants