如何使用React钩子和可观察的设计模式来保持状态更新



这里是codesandbox的链接。

我正在学习可观察的设计模式。我正在尝试显示比特币价格更新,但我目前将price作为undefined。我已经调用了API,并且还为subscribenotify创建了可观察的方法。

我有一个观察者被创建为可观察者的构造函数,并试图从API更新价格状态。但价格显示尚未确定。我哪里错了?

export function Observable() {
this.observers = []; //array of observer functions
}
Observable.prototype = {
subscribe: function (fn) {
this.observers.push(fn);
},
fire: function () {
this.observers.forEach((fn) => {
fn.call();
});
}
};
import React from "react";
import { getBitcoinPrice } from "./api";
import { Observable } from "./observable";
const CryptoPrice = () => {
const [price, setPrice] = React.useState(0);
React.useEffect(() => {
const bitcoinObservable = new Observable();
console.log({ bitcoinObservable });
let livePrice = bitcoinObservable.subscribe(getBitcoinPrice);
setPrice(livePrice);
console.log({ livePrice });
let newPrice = bitcoinObservable.fire();
console.log({ newPrice });
}, [price]);
return (
<>
<h3>{price ? price : "undefined"}</h3>
</>
);
};
export default CryptoPrice;

代码中存在一些问题。让我从observable的实现开始:

  1. Fire不会将值传递给观察者!他们只被告知";发生了一些事情";,但不是确切的——所以观察者永远听不到实际的CCD_ 5
  2. 你确实需要一个unsubscribe方法

所以:

export function Observable() {
this.observers = []; //array of observer functions
}
Observable.prototype = {
subscribe: function (fn) {
this.observers.push(fn);
},
unsubscribe: function (fn) {
const index = this.observers.findIndex(fn);
if (index >= 0) {
this.observers.splice(index, 1);
}
},
fire: function (x) {
this.observers.forEach((fn) => {
fn(x);
});
}
};

getBitcoinPrice:的一些功能

  1. const data = await response.data;是多余的。你在承诺上await,只有axios.get(url)是承诺。之后,response.data是一个真实的对象,只要使用它就可以了。不过,如果你等待一个不承诺,它会立即运行
  2. 我看到data.ticker.price实际上是一个字符串,所以把它改成+data.ticker.price变成了一个数字。同样,这个应用程序没有造成任何伤害,但我认为在更大的应用程序中这样做更好

所以:

export const getBitcoinPrice = async () => {
const url = `https://api.cryptonator.com/api/ticker/btc-usd`;
try {
const response = await axios.get(url);
console.log('[getBitcoinPrice]', response.data.ticker.price);
return +response.data.ticker.price;
} catch (error) {
console.log("[getBitcoinPrice] error: ", error);
}
};

现在转到核心,CryptoPrice组件。以下是一种方法,我相信还有更好的方法。无论如何,我已经将处理分为两个阶段:一个阶段是周期性地获取价格并激发可观测值。另一种是实际观察变化并产生效果(此处更新显示(。注意React的useEffect的清理功能的使用——永远不要忘记释放资源!

const CryptoPrice = () => {
const [price, setPrice] = React.useState(0);
const [bitcoinObservable] = React.useState(() => new Observable());
// this effect is responsible for triggering the price load periodically
React.useEffect(() => {
const interval = setInterval(async () => {
const x = await getBitcoinPrice();
bitcoinObservable.fire(x);
}, 5000);
// the cleanup function of the effect, never forget!!!
return () => {
clearInterval(interval);
};
}, [bitcoinObservable]);
// this effect is responsible for the subscription
React.useEffect(() => {
const handler = function(price) {
console.log('[CryptoPrice]', price);
setPrice(price);
};
bitcoinObservable.subscribe(handler);
return () => {
bitcoinObservable.unsubscribe(handler);
};
}, [bitcoinObservable]);
return (
<>
<h3>{typeof price === 'number' ? price : "undefined"}</h3>
</>
);
};

最后,零在Javascript中是假的,所以price ? price : 'undefined'将产生未定义的0的初始价格。因此进行了类型检查CCD_ 18。

还有一些东西/指针:

  • 我更喜欢";第一阶段";,即创建";比特币价格可观测";在组件之外,可能在服务中。主要关注点分离
  • 如果您使用状态管理基础设施,例如Redux;服务";以上将是某种中间件
  • 承诺和可观察模式的一个非常有趣的概括是反应流。一开始可能很难把握,但从长远来看,它们非常强大
  • 如果您将Redux+RxJS组合在一起,则可以查看Redux

首先,我猜您是有意构建自己的可观察对象来学习睡眠,而不是使用像rxjs这样的库,所以我将重点讨论为什么您当前的实现不起作用。对于生产使用,我强烈建议使用经过战斗测试的可观察性库,而不是构建自己的库。

现在转到您的代码。

1.您混淆了订阅和排放(在您的情况下为fire(

现在,如果您调用fire,您的Observable将执行传递给subscribe的每个函数。但是没有一个观察者会得到API结果,因为您不会将其传递给fire。你应该这样修改你的观察:

export function Observable() {
this.observers = []; //array of observer functions
}
Observable.prototype = {
subscribe: function (fn) {
this.observers.push(fn);
},
// add parameter value
fire: function (value) {
this.observers.forEach((fn) => {
// call the observer callbacks directly with value
fn(value);
});
}
};

2.你从来没有;火;您对API结果的可观察性

相反,您已经通过了Promise,它将比特币价格返回给subscribe函数。如前所述,通过这种方式,没有其他观察者能够接收到API结果。

你应该这样修改你的useEffect

React.useEffect(() => {
const bitcoinObservable = new Observable();
bitcoinObservable.subscribe((livePrice) => {
console.log("sub!", { livePrice });
setPrice(livePrice);
});
getBitcoinPrice().then((livePrice) => {
console.log('fire!', { livePrice });
bitcoinObservable.fire(livePrice);
});
}, [])

现在,您的代码沙箱示例已经运行良好。当然,在同一个useEffect调用中创建可观察对象并订阅它没有多大意义。但是,如果你想在多个地方使用你的可观察到的东西,还有其他事情你应该做:

3.添加取消订阅逻辑以避免内存泄漏

React组件总是被挂载和卸载,并使用它们的useEffect逻辑。如果你不取消订阅observable,你可能会导致内存泄漏。所以你应该在你的Observable:中添加这样的取消订阅功能

Observable.prototype = {
subscribe: function (fn) {
this.observers.push(fn);
// return an object with the unsubscribe function
return {
unsubscribe: () => {
this.observers.splice(this.observers.indexOf(fn));
}
};
},
// ...
};

然后在您的useEffect中,您可以在效果清理回调中取消订阅:

React.useEffect(() => {
// ...
// save the return object of the subscribe call
const subscription = bitcoinObservable.subscribe((livePrice) => {
console.log("sub!", { livePrice });
setPrice(livePrice);
});
// ...
// add cleanup logic
return () => {
subscription.unsubscribe();
};
}, []);

如果组件被破坏,这将清除您的订阅回调。一旦你在多个地方使用你的可观察物,这将是必要的。

如果你想了解更多关于可观察性的信息,我推荐这样的文章:https://indepth.dev/posts/1155/build-your-own-observable-part-1-arrays

最新更新