Intégrez facilement rxjs dans les crochets de réaction ! analyse du code source observable-hooks !

avant-propos

Un de mes anciens collègues vient de rejoindre une entreprise publique locale à Chengdu (il est trop enviable de quitter le travail à 5 ​​heures). Ils ont utilisé la bibliothèque observable-hooks, que je n'ai jamais utilisée auparavant. Je ne connais qu'un bibliothèque appelée rxjs-hooks de l'équipe frontale leetcode. , j'ai essayé d'aller sur le site officiel pour le voir, et j'ai trouvé que les crochets observables sont en effet meilleurs en capacité

Mais pour être honnête, les documents écrits sur le site officiel sont vraiment difficiles à comprendre pour les personnes ayant une maîtrise moyenne de rxjs. Alors cet article est fait pour apprendre avec tout le monde. Chaque API a un boîtier en ligne, ce qui est pratique pour que chacun puisse jouer seul.

Explication du code source de l'API principale

useObservable

Cette API est généralement utilisée pour surveiller les changements de valeur, puis renvoyer un Observable, qui a l'avantage d'être réactif comme vue et mobx.

En un mot : écoutez les changements de valeur puis générez un nouveau flux, utilisez cette méthode, le cas est le suivant :

Cliquez sur le bouton, adresse en ligne :

codesandbox.io/s/sharp-haw…

import "./styles.css";
import { useObservable } from "observable-hooks";
import { map } from "rxjs";
import { useState } from "react";

const App = (props) => {
  const [showPanel] = useState("hello");

  // 监听 props 或 state 变化
  const enhanced$ = useObservable(
    (inputs$) => inputs$.pipe(map(([showPanel]) => showPanel + "world")),
    [showPanel]
  );
  return (
    <div className="App">
      <h1>{showPanel}</h1>
      <button
        onClick={() => {
          // 这个方法
          enhanced$.subscribe((value) => alert(value));
        }}
      >
        click
      </button>
    </div>
  );
};

export default App;
复制代码

Analyse du code source, code clé :

export function useObservable(
  init,
  inputs?: [...TInputs]
): Observable<TOutput> {

  const inputs$Ref = useRefFn(() => new BehaviorSubject(inputs))
  const source$Ref = useRefFn(() => init(inputs$Ref.current))

  const firstEffectRef = useRef(true)
  useEffect(() => {
    if (firstEffectRef.current) {
      firstEffectRef.current = false
      return
    }
    inputs$Ref.current.next(inputs)
  }, inputs)

  return source$Ref.current
}
复制代码

useRefFn utilise useRef pour créer de nouveaux BehaviorSubject(inputs), au lieu d'instancier new à chaque fois, nous pouvons réutiliser le nouveau BehaviorSubject généré pour la première fois.Le code source est très simple, veuillez noter que init() ne sera appelé qu'une seule fois, car suit :

/**
 * 一个返回值的函数。 只会被调用一次
 */
 export function useRefFn<T>(init: () => T) {
  const firstRef = useRef(true)
  // 请注意init() 只会调用一次
  const ref = useRef<T | null>(null)
  if (firstRef.current) {
    firstRef.current = false
    ref.current = init()
  }
  return ref;
}
复制代码

Eh bien, regardons le code source useObservable, créez d'abord un nouveau BehaviorSubject (inputs), inputs est le deuxième paramètre de useObservable, en fonction des données, useObservable repoussera le flux lorsque ces données changeront.

Le code à re-push est :

  useEffect(() => {
    if (firstEffectRef.current) {
      firstEffectRef.current = false
      return
    }
    inputs$Ref.current.next(inputs)
  }, inputs)
复制代码

On peut voir que useEffect est utilisé pour surveiller les changements d'entrées, puis inputs$Ref.current.next(inputs) pour repousser le flux.

Enfin, regardons cette phrase

  const source$Ref = useRefFn(() => init(inputs$Ref.current))
复制代码

Il s'agit de transmettre le nouveau flux BehaviorSubject(inputs) à la fonction init.La fonction init est le premier paramètre de useObservable, que nous avons personnalisé.

useLayoutObservable

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

如果需要在下次浏览器绘制前拿到值可以用它, 所以源码跟我们之前是一样的,就是把useEffect改成了useLayoutEffect而已。

useObservableCallback

一言以蔽之,这个useObservableCallback一般用来给事件监听的,事件一变化就产生新的流。需要注意的是,需要自己手动去订阅。

案例如下(当input值变化时,注意看控制台信息变化): codesandbox.io/s/affection…

import "./styles.css";
import { pluck, map } from "rxjs";
import { useObservableCallback } from "observable-hooks";
import { useEffect } from "react";

const App = (props) => {
  const [onChange, outputs$] = useObservableCallback((event$) =>
    event$.pipe(pluck("currentTarget", "value"))
  );
  useEffect(() => outputs$.subscribe((v) => console.log(v)));
  return <input type="text" onChange={onChange} />;
};

export default App;
复制代码

源码如下:

export function useObservableCallback(
  init,
  selector
) {
  const events$Ref = useRefFn(new Subject())
  const outputs$Ref = useRefFn(() => init(events$Ref.current))
  const callbackRef = useRef((...args) => {
    events$Ref.current.next(selector ? selector(args) : args[0])
  })
  return [callbackRef.current, outputs$Ref.current]
}
复制代码
  • 首先events$Ref就是一个new Subject
  • 然后定义一个消费流outputs$Ref,我们传入的自定义init函数第一个参数就是上一步的new Subject
  • callbackRef是一个注册函数,常用于给事件,也就是事件触发,就给outputs$Ref推送数据

当然需要回调函数传入多个参数才需要selector,大家现在只是入门,等用到的时候再了解不迟。

useSubscription

useSubscription说白了,就是subscribe的hooks而已。

源码如下:

源码只做了一个特殊处理需要注意,其他的不用看,就是subscrible

if (input$ !== argsRef.current[0]) {
     // stale observable
     return
}
复制代码
export function useSubscriptionInternal(
  args
) {
  const argsRef = useRef(args)
  const subscriptionRef = useRef()

  useEffect(() => {
    argsRef.current = args
  })

  useEffect(() => {
    const input$ = argsRef.current[0]

    const subscription = input$.subscribe({
      next: value => {
        if (input$ !== argsRef.current[0]) {
          // stale observable
          return
        }
        const nextObserver =
          argsRef.current[1].next ||
          argsRef.current[1]
        if (nextObserver) {
          return nextObserver(value)
        }
      },
      error: error => {
        if (input$ !== argsRef.current[0]) {
          // stale observable
          return
        }
        const errorObserver =
          argsRef.current[1].error ||
          argsRef.current[2]
        if (errorObserver) {
          return errorObserver(error)
        }
        console.error(error)
      },
      complete: () => {
        if (input$ !== argsRef.current[0]) {
          // stale observable
          return
        }
        const completeObserver =
          argsRef.current[1].complete ||
          argsRef.current[3]
        if (completeObserver) {
          return completeObserver()
        }
      }
    })

    subscriptionRef.current = subscription

    return () => {
      subscription.unsubscribe()
    }
  }, [args[0]])

  return subscriptionRef
}
复制代码

useLayoutSubscription

与 useSubscription 一样,除了 subscription 是通过 useLayoutEffect 触发。

当需要在 DOM 绘制前拿到值时会有用。

尽量少用,因为其是在浏览器绘制前同步调用。过多的同步值产生会延长组件的 commit 周期。

useObservableState

Cette fonction peut être utilisée comme useState ou useReducer. Je vous suggère d'utiliser useReducer au lieu de state pour vos propres projets, car généralement votre état est classé. Par exemple, lorsque vous demandez une donnée, cette donnée est une variable, mais elle s'accompagne également du fait que la requête L'état de chargement des données, le chargement et les données demandées sont intégrés.Pourquoi utiliser deux useStates ?Cela semble vraiment gênant.

Le cas plus un moins un est le suivant :

Code en ligne : codesandbox.io/s/kind-jasp…

import "./styles.css";
import { scan } from "rxjs";
import { useObservableState } from "observable-hooks";

const App = (props) => {
  const [state, dispatch] = useObservableState(
    (action$, initialState) =>
      action$.pipe(
        scan((state, action) => {
          switch (action.type) {
            case "INCREMENT":
              return {
                ...state,
                count:
                  state.count + (isNaN(action.payload) ? 1 : action.payload)
              };
            case "DECREMENT":
              return {
                ...state,
                count:
                  state.count - (isNaN(action.payload) ? 1 : action.payload)
              };
            default:
              return state;
          }
        }, initialState)
      ),
    () => ({ count: 0 })
  );

  return (
    <div className="App">
      <h1>{state.count}</h1>
      <button
        onClick={() => {
          dispatch({ type: "INCREMENT" });
        }}
      >
        加一
      </button>
      <button
        onClick={() => {
          dispatch({ type: "DECREMENT" });
        }}
      >
        减一
      </button>
    </div>
  );
};

export default App;
复制代码

Nous présentons donc uniquement comment utiliser useObservableState pour implémenter useReducer ici, voici le code clé

  • l'état est les données que nous voulons
  • Le rappel transmettra la valeur que vous souhaitez transmettre au premier paramètre state$OrInit, qui est notre flux personnalisé
  • useSubscription renverra éventuellement le dernier état des données traitées setState dans le flux
useObservableStateInternal(
  state$OrInit,
  initialState){
    const init = state$OrInit
    const [state, setState] = useState(initialState)

    const input$Ref = useRefFn(new Subject())

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

    useSubscription(state$, setState)

    return [state, callback]

}
复制代码

finir

Je suppose que tu aimes

Origine juejin.im/post/7079704126548312100
conseillé
Classement