Front End

[FE] Hello "Observable Hooks"

2021-03-24  本文已影响0人  何幻

最近项目中用到了 react + rxjs + observable-hooks,下文总结一下 observable-hooks 的学习心得。

1. 搭建 React 环境

使用 create-react-app 创建 react 项目 test-hooks

$ npm i -g create-react-app
$ create-react-app test-hooks
$ cd test-hooks
$ npm i
$ npm start

我们可以在 src/App.js 中写一些实验代码,例如,

function App() {
  return (
    <>Hello World</>
  );
}
export default App;

2. Observable Hooks

Observable Hooks 是 React hooks for RxJS Observables
官方文档 Core Concepts 中介绍了 Two Worlds 概念模型,非常值得一读。

为了使用 Observable Hooks,需要在上文 test-hooks 项目中,
安装 rxjsobservable-hooks 依赖。

$ npm i -S rxjs observable-hooks

(1)异步取值

功能:页面初始化展示 0,500ms 后展示 1

import { from } from 'rxjs';
import { useObservableState } from 'observable-hooks';

function App() {
  const output$ = from(new Promise(res => setTimeout(() => res(1), 500)));
  const initialOutput = 0;
  const output = useObservableState(output$, initialOutput);

  return (
    <>{output}</>
  );
}
export default App;

这种写法非常适合处理异步加载页面的场景:

首屏加载后,前端通过 ajax 请求获取数据,更新页面。

(2)事件更新

功能:页面展示一个按钮和数字 0,每次点击按钮,后面的数字加一。

import { useObservableState } from 'observable-hooks';

function App() {
  const initialOutput = 0;
  const transform = output$ => output$.pipe(
    //
  );
  const [output, onInput] = useObservableState(transform, initialOutput);

  const onClick = () => {
    onInput(output + 1);
  };

  return (
    <>
      <input type="button" value="Add One" onClick={onClick} />
      {output}
    </>
  );
}
export default App;

每次 onInput 加入流中的值,
通过 transform,再经过 useObservableState,最后赋值给了 output

(3)异步回调

功能:页面加载 500ms 后,写 log

import { from } from 'rxjs';
import { useSubscription } from 'observable-hooks';

function App() {
  const output$ = from(new Promise(res => setTimeout(() => res(1), 500)));
  useSubscription(output$, v => {
    console.log(v);
  });

  return (
    <>Hello World</>
  );
}
export default App;
const output$ = from(new Promise(res => setTimeout(() => res(1), 500)));
useSubscription(output$, v => {
  console.log(v);  // 1
});
useSubscription(output$, v => {
  console.log(v);  // 1
});

实际上,上文介绍的 useObservableStateuseSubscription 对于同一个流也是可以多次使用的。

import { from } from 'rxjs';
import { useObservableState, useSubscription } from 'observable-hooks';

function App() {
  const output$ = from(new Promise(res => setTimeout(() => res(1), 500)));

  const initialOutput = 0;
  const output = useObservableState(output$, initialOutput);
  debugger;  // 1 [先触发,跟先后顺序有关]
  
  useSubscription(output$, v => {
    debugger;  // 1 [后触发,跟先后顺序有关,放在前面就会先触发]
  });

  return (
    <>Hello World</>
  );
}
export default App;

(4)变量写入流

功能:
页面展示一个按钮和数字 0,每次点击按钮,后面的数字加一。
按钮点击会改变组件状态,组件状态变更,会导致流中加入新值。

import { useState } from 'react';
import { useObservableState, useObservable } from 'observable-hooks';

function App() {
  const [state, setState] = useState(0);

  const transform = output$ => output$.pipe(
    //
  );
  const output$ = useObservable(  // 这里是关键
    transform,
    [state]
  );

  const initialOutput = state;
  const output = useObservableState(output$, initialOutput);

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <>
      <input type="button" value="Add One" onClick={onClick} />
      {output}
    </>
  );
}
export default App;

(5)事件写入流

功能:
页面展示一个按钮和数字 0,每次点击按钮,后面的数字加一。
按钮点击事件,会直接在流中加入新值。

import { useObservableState, useObservableCallback } from 'observable-hooks';

function App() {
  const transform = output$ => output$.pipe(
    //
  );
  const [onInput, output$] = useObservableCallback(  // 这里是关键
    transform
  )

  const initialOutput = 0;
  const output = useObservableState(output$, initialOutput);

  const onClick = () => {
    onInput(output + 1);
  };

  return (
    <>
      <input type="button" value="Add One" onClick={onClick} />
      {output}
    </>
  );
}
export default App;
(6)创建常量流

上文提到了 from 将 Promise 转换成流的做法,每次调用 from 都会产生新的流。
尤其是当组件通过 setState 状态更新的时候。

而使用下述写法,可以创建一个在组件更新时不变的流 output$
每次 useObservable 都返回相同的对象。

import { useState } from 'react';
import { from } from 'rxjs';
import { useObservable } from 'observable-hooks';

let a;
function App() {
  const [state, setState] = useState(0);

  const output$ = useObservable(
    () => from(new Promise(res => setTimeout(() => res(1), 500))),
  );

  const l = a === output$;
  debugger;     // 组件更新时 l === true
  a = output$;  // 保存上一次的值

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <input type="button" value="Add One" onClick={onClick} />
  );
}
export default App;

3. 小结

// 异步取值 [流 -> 值]
const output = useObservableState(output$, initialOutput);

// 事件更新 [流变换 -> 值, 写入流]
const [output, onInput] = useObservableState(transform, initialOutput);

// 异步回调 [流, 回调 -> void]
useSubscription(output$, v => {
  console.log(v);
});

// 变量写入流 [流变换, 变量依赖 -> 常量流]
const output$ = useObservable(
  transform,
  [state],
);

// 事件写入流 [流变换 -> 写入流, 常量流]
const [onInput, output$] = useObservableCallback(
  transform,
);

// 创建同一个流 [流变换 -> 常量流]
const output$ = useObservable(
  () => from(new Promise(res => setTimeout(() => res(1), 500))),
);
import { useState } from 'react';
import { from } from 'rxjs';
import { useObservableState } from 'observable-hooks';

function App() {
  const [state, setState] = useState(0);

  const output$ = from(new Promise(res => setTimeout(() => res(1), 500)));
  const output = useObservableState(output$, 0);  // output$ 在组件更新后重置,所以 output 总是为 0

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <>
      {output}
      <input type="button" value="Add One" onClick={onClick} />
    </>
  );
}
export default App;

参考

Observable Hooks
RxJS

上一篇下一篇

猜你喜欢

热点阅读