美文网首页
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

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