rewrite a rxjs class (BehaviorSubject)
Bài đăng này đã không được cập nhật trong 4 năm
A. Make a simple counter with rxjs
- go to
http://codesandbox.io
then create a react one (mine is: https://codesandbox.io/s/rxjs-rewrite-behaviorsubject-wfh0n) - Create an observable object
let valueObservable = new BehaviorSubject(0);
- Create 2 separated components
Result
for showing the result andPanel
for inc/dec value. They will communicate thruvalueObservable
function Result() {
const [value, setValue] = useState(0);
useEffect(() => {
valueObservable.subscribe((it) => setValue(it));
}, []);
return <div> Result: {value} </div>;
}
function Panel() {
return (
<div>
<button
onClick={() => valueObservable.next(valueObservable.getValue() + 1)}
> + </button>
<button
onClick={() => valueObservable.next(valueObservable.getValue() - 1)}
> - </button>
</div>
);
}
- Then add them to the root App component and see the result
export default function App() {
return (
<div className="App">
<Result />
<Panel />
</div>
);
}
B. Rewrite it without using rxjs
- Create a class
MyBehaviorSubject
having same methods as rxjsBehaviorSubject
export default class MyBehaviorSubject {
constructor(defaultValue) {
}
subscribe(fn) {
}
getValue() {
}
next(nextValue) {
}
}
Now go implementing each methods:
constructor(defaultValue) {
this.value = defaultValue;
this.subscribers = [];
}
The subscribe method will take a function as argument, then it should be saved to a list, then they can be trigged later when value changed. So in the constructor, a default value and an empty subscribers list should be created
subscribe(fn) {
this.subscribers.push(fn);
return () => {
this.subscribers.pop();
};
}
Now the subscribe method will add each component update state function
to that subscribers list.
And you may wonder what is the purpose of that return function? It's for unmounted components. When a component unmounted, we should remove the callback out from the list, otherwise, it will be triggered every time value changed, even when it no longer appears on UI
getValue() {
return this.value;
}
This function just returns current value
next(nextValue) {
this.value = nextValue;
this.subscribers.forEach((fn) => fn(nextValue));
}
This function update new value, and then loop thru subscribers and excecute them (note that every element of subscribers list is a component update state function
. So the result of these executions is: all component will be synced with that new value
)
Then now, go to codesandbox and replace BehaviorSubject
by MyBehaviorSubject
. You should see the same result.
C. Make sure we handle well unmounted component case.
Now, implement something more to make sure that unmounted component callback will not be trigged anymore Add one more callback for sync number of subscribers that are active
constructor(defaultValue) {
......
this.fn = () => {};
}
subscribe2(fn) {
this.fn = fn;
}
Trigger it every when a subscribe created or removed
subscribe(fn) {
this.subscribers.push(fn);
this.fn(this.subscribers.length);
return () => {
this.subscribers.pop();
this.fn(this.subscribers.length);
};
}
And that's it. Result at https://codesandbox.io/s/rxjs-rewrite-behaviorsubject-wfh0n.
Every time you show the Result Component
, number of subscribers is 1. End when it hided, number of subscribers will be 0, means there is no callback execution when inc/dec button clicked.
All rights reserved