美文网首页
observable-hooks

observable-hooks

作者: 前端小白的摸爬滚打 | 来源:发表于2021-12-01 22:56 被阅读0次

用来连接普通值和 Observable
提供 hook 来连接 observable 和 React 状态

为什么需要在 hooks 中使用 Rxjs

因为 hooks 仅仅是为函数式组件提供了状态,但是对于一些异步操作,我们仍然需要一些异步的工具来减少工作

写在前面

  • observable-hooks 的 hooks 如果就接收一个函数作为参数,那么这个函数只会在组件初始化的时候执行一次

  • observable-hooks 返回的值在组件更新期间都不会发生改变

常用的 hooks

!useObservable

useObservable<TOutput>(init: function): Observable<TOutput>
useObservable<TOutput, TInputs>(
  init: function,
  inputs: TInputs
): Observable<TOutput>

在函数组件中创建一个流,每次组件更新返回的流的引用是不变的,也就是随着组件更新流不会每次都重新创建

  • 接收一个初始化函数,函数只会被执行一次,函数返回的是一个 observable。该函数接收一个参数是由依赖数组构成的 observable

  • 接收一个依赖数组,数组中任意一个元素的值发生变化流都会重新执行(当初始化函数中的流依赖了数组中的值时)

没有传递依赖数组的时候这个流只会在组件初始化的时候定义一次。

useLayoutObservable

与 useObservable 基本一样,不同的是底层使用 useLayoutEffect 监听改变。

useObservableCallback

useObservableCallback<TOutput, TInput, TParams>(
  init: function,
  selector?: undefined | function
): [function, Observable<TOutput>]
  • init 函数接收的参数就是执行该 hooks 返回的 function 在调用时传入的参数构建的流,init 函数的返回值就是执行该 hooks 返回的元组的第二个值

useObservableCallback 函数返回的值在组件更新的时候保持不变

init 函数只会在组件创建的时候执行一次

!useSubscription

useSubscription<TInput>(
  input$: Observable<TInput>,
  observer?: PartialObserver<TInput>
): React.MutableRefObject<Subscription | undefined>
useSubscription<TInput>(
  input$: Observable<TInput>,
  next?: function | null | undefined,
  error?: function | null | undefined,
  complete?: function | null | undefined
): React.MutableRefObject<Subscription | undefined>

订阅一个流,这个流改变/重新发出值的时候会被重新订阅,所以传入的流需要在组件的生命周期中引用不会发生改变,推荐是使用 useObservable/useRef 来创建这个流

使用 useSubscription 订阅和在 useEffect 中订阅的区别

  • useSubscription 的回调中我们可以保证始终可以拿到最新的 state/props
  • 当 Observable 改变时 useSubscription 会自动取消旧的并订阅新的。
  • 并发模式安全,会避免旧的 observable 触发回调

useLayoutSubscription

和 useSubscription 使用方式相同,只不过底层变化监听是在 useLayoutEffect 中实现的

!useObservableState

自动订阅 observable 得到 state,state 是订阅 observable 是发出的值,当订阅的流发出新的值或者改变的时候会重新订阅,因为是 state,所以值变化可以引起页面的刷新

传入的 observable 需要在组件的生命周期期间相同,否则会导致每次组件更新都会重新订阅,造成死循环

传入一个 observable 时,这个 observable 需要使用 useRef/useObservable;传入的是一个函数时,函数返回的 observable 则可以是一个普通的 observable,因为函数只会执行一次

useObservableState<TState>(
  input$: Observable<TState>
): TState | undefined
useObservableState<TState>(
  input$: Observable<TState>,
  initialState: TState | (() => TState)
): TState
useObservableState<TState, TInput = TState>(
  init: (input$: Observable<TInput>) => Observable<TState>
): [TState | undefined, (input: TInput) => void]
useObservableState<TState, TInput = TState>(
  init: (
    input$: Observable<TInput>,
    initialState: TState
  ) => Observable<TState>,
  initialState: TState | (() => TState)
): [TState, (input: TInput) => void]

当传入一个 init 函数时的源码

const init = state$OrInit;
const [state, setState] = (useState < TState) | (undefined > initialState);

const input$Ref = useRefFn < Subject < TInput >> getEmptySubject;

const state$ = useRefFn(() => init(input$Ref.current, state)).current;
const callback = useRef((state: TInput) =>
  input$Ref.current.next(state)
).current;

useSubscription(state$, setState);
useDebugValue(state);
return [(state, callback)];
  • 第二个参数是初始值,也就是 hook 的第一个参数中的流没有发出值的时候,state 的默认值,不传则是 undefined

  • 当直接传入一个 observable 是,返回值是一个 state

  • 当传入一个 init 函数时,返回值是一个元组;元组的第一个参数是 state,第二个参数则是一个更新函数,调用该函数可以重新触发流执行。

  • init 函数接收一个 observable,这个 observable 是我们调用更新函数是传入的参数包装而来的,init 函数内部的流需要使用到这个 observable 才可以有调用更新函数,流重新执行,状态更新的效果

需要注意一下初始化的时候,如果没有调用回调,可能就没有 state 值,此时我们可以给 input$.pipe(startWith(value))来帮助我们在组件初始化的时候执行一次流

useObservableEagerState

useLayoutObservableState

与 useObservableState 基本一样,不同的是底下使用 useLayoutEffect 监听改变。

!useObservableGetState

类似于 lodash 的 get
通过属性路径从 Observable 状态对象中获得值。

useObservableGetState<TState>(
  state$: Observable<TState>,
  initialState: TState | (() => TState)
): TState
useObservableGetState<TState, A extends keyof TState>(
  state$: Observable<TState>,
  initialState: TState[A] | (() => TState[A]),
  pA: A
): TState[A]
useObservableGetState<
  TState,
  A extends keyof TState,
  B extends keyof TState[A]
>(
  state$: Observable<TState>,
  initialState: TState[A][B] | (() => TState[A][B]),
  pA: A,
  pB: B
): TState[A][B]
...
  • 初始状态必须提供。
  • 只有最终得出的状态发生改变才会触发重渲染。
  • 不可访问的路径会触发异常。
const state$ = of({ a: { b: { c: "value" } } });

// 第一次渲染:'default'
// 第二次渲染:'value'
const text = useObservableGetState(state$, "default", "a", "b", "c");

!useObservablePickState

类似于 lodash 的 pick

useObservablePickState<
  TState,
  TKeys extends keyof TState,
  TInitial extends null | undefined | void
>(
  state$: Observable<TState>,
  initialState: TInitial,
  ...keys: TKeys[]
): { [K in TKeys]: TState[K] } | TInitial
useObservablePickState<TState, TKeys extends keyof TState>(
  state$: Observable<TState>,
  initialState:
    | { [K in TKeys]: TState[K] }
    | (() => { [K in TKeys]: TState[K] }),
  ...keys: TKeys[]
): { [K in TKeys]: TState[K] }
  • 从一个状态对象中挑选属性组合成新的对象。
  • 初始状态必须提供。
  • 挑选出的属性任一发生变化都会触发重渲染。
const state$ = of({ a: "a", b: "b", c: "c", d: "d" });

// 第一次渲染:{ a: '', b: '', c: '' }
// 第二次渲染:{ a: 'a', b: 'b', c: 'c' }
const picked = useObservablePickState(
  state$,
  () => ({ a: "", b: "", c: "" }),
  "a",
  "b",
  "c"
);

辅助方法

identity

identity<T>(value: T): T

返回第一个参数

function identity(value) {
  return value;
}

pluckFirst

pluckFirst<TArr>(inputs$: Observable<TArr>): Observable<TArr[0]>

接收一个数组流,返回数组第一个元素构成的流

useRefFn

useRefFn<T>(init: function): MutableRefObject<T>

接收一个函数,这个函数只会被调用一次,返回值就是使用这个函数的返回值作为 current 属性的 ref 对象

接收一个函数,将函数的返回值作为 ref 的 current 的值,返回这个 ref

默认 useRef 会在每次组件更新的时候都会重新执行,只不过不会更新引用

所以 useRef 如果传递一个函数,则每次更新传入的函数都是一个新的函数,但是 useRef 并不会用,同样的,传入一个 new 创建的实例时,每次组件更新也都是一个新的实例,会导致重复创建了很多对象。但是这些都没有关系,虽然每次都是新函数/新对象,但是我们的 current 并不会因为 useRef 的重新执行就更新上面的值。current 上的值在没有更新的情况下,始终保存的是组件创建时的 useRef 传入的值

useForceUpdate

调用这个 hook 可以使组件重新渲染,内部其实是定义了一个 state,每次调用这个 hook 都会改变这个 state 然后导致组件重新渲染

pluckCurrentTargetValue

pluckCurrentTargetValue<TEvent>(
  event$: Observable<TEvent>
): Observable<TEvent["currentTarget"]["value"]>

pluckCurrentTargetChecked

pluckCurrentTargetChecked<TEvent>(
  event$: Observable<TEvent>
): Observable<TEvent["currentTarget"]["checked"]>

相关文章

  • observable-hooks

    用来连接普通值和 Observable提供 hook 来连接 observable 和 React 状态 为什么需...

  • [FE] Hello "Observable Hooks"

    最近项目中用到了 react + rxjs + observable-hooks,下文总结一下 observabl...

网友评论

      本文标题:observable-hooks

      本文链接:https://www.haomeiwen.com/subject/pvbexrtx.html