Einfaches Integrieren von rxjs in Reaktionshaken! Observable-Hooks Quellcodeanalyse!

Vorwort

Ein ehemaliger Kollege von mir ist gerade in ein lokales staatliches Unternehmen in Chengdu eingetreten (es ist zu beneidenswert, um 5 Uhr von der Arbeit zu gehen). Sie haben die Observable-Hooks-Bibliothek benutzt, die ich vorher nicht benutzt habe Bibliothek namens rxjs-hooks vom Leetcode-Front-End-Team. Ich habe versucht, auf die offizielle Website zu gehen, um sie zu sehen, und festgestellt, dass Observable-Hooks tatsächlich besser in der Lage sind

Aber um ehrlich zu sein, die auf der offiziellen Website geschriebenen Dokumente sind für Leute mit durchschnittlichen rxjs-Kenntnissen wirklich schwer zu verstehen. Dieser Artikel ist also gemacht, um mit jedem zu lernen. Jede API hat einen Online-Fall, den jeder bequem selbst spielen kann.

Erläuterung des Kern-API-Quellcodes

beobachtbar verwenden

Diese API wird im Allgemeinen verwendet, um Wertänderungen zu überwachen und dann ein Observable zurückzugeben, was den Vorteil hat, dass es wie vue und mobx reaktionsschnell ist.

Kurz gesagt: Auf Wertänderungen warten und dann einen neuen Stream generieren, verwenden Sie diese Methode, der Fall ist wie folgt:

Klicken Sie auf die Schaltfläche, Online-Adresse:

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;
复制代码

Quellcodeanalyse, Schlüsselcode:

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 verwendet useRef, um neue BehaviorSubject(inputs) zu erstellen, anstatt jedes Mal neu zu instanziieren, können wir das neu generierte BehaviorSubject wiederverwenden.Der Quellcode ist sehr einfach, bitte beachten Sie, dass init() nur einmal aufgerufen wird, as folgt:

/**
 * 一个返回值的函数。 只会被调用一次
 */
 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;
}
复制代码

Schauen wir uns den Quellcode von useObservable an, erstellen Sie zuerst ein neues BehaviorSubject (inputs), input ist der zweite Parameter von useObservable, abhängig von den Daten wird useObservable den Stream erneut pushen, wenn sich diese Daten ändern.

Der Code zum erneuten Pushen lautet:

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

Es ist ersichtlich, dass useEffect verwendet wird, um die Änderungen von Eingaben zu überwachen, und dann input$Ref.current.next(inputs), um den Stream erneut zu pushen.

Schauen wir uns abschließend diesen Satz an

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

Es soll den neuen BehaviorSubject(inputs)-Stream an die Init-Funktion übergeben.Die Init-Funktion ist der erste Parameter von useObservable, der von uns angepasst wird.

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

Diese Funktion kann als useState oder useReducer verwendet werden. Ich schlage vor, dass Sie useReducer anstelle von state für Ihre eigenen Projekte verwenden, da Ihr Status im Allgemeinen klassifiziert wird. Wenn Sie beispielsweise ein Datenelement anfordern, sind diese Daten eine Variable, aber es geht auch damit einher, dass beim Request der Ladezustand der Daten, das Laden und die angeforderten Daten integriert werden.Warum zwei useStates verwenden?Das sieht echt umständlich aus.

Der Fall plus eins minus eins ist wie folgt:

Online-Code: 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;
复制代码

Daher stellen wir hier nur vor, wie useObservableState verwendet wird, um useReducer zu implementieren, das Folgende ist der Schlüsselcode

  • state sind die Daten, die wir wollen
  • Der Callback übergibt den Wert, den Sie übergeben möchten, an den ersten Parameter state$OrInit, der unser benutzerdefinierter Stream ist
  • useSubscription gibt schließlich den letzten Zustand des verarbeiteten Datensatzes setState im Stream zurück
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]

}
复制代码

Ende

Ich denke du magst

Origin juejin.im/post/7079704126548312100
Empfohlen
Rangfolge