- Notifications
You must be signed in to change notification settings - Fork0
Make your own shared state (store) in your React application with a little bit of reactive programming.
License
hypercube-software/flux-pattern-with-rxjs
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Table of content
1 React Flux pattern with RxJS 6
1.1 What Flux is all about?
1.2 RxJS to the rescue
1.2.1 The main idea
1.2.2 How to subscribe to a subset of events
1.2.3 Don't forget to unsubscribe
1.2.4 Protect the shared state
1.3 A little bit of OOP for better encapsulation
2 How to test this project
Here is the Flux pattern:
The goal is:
- To have ashared state between your widgets. Note that props are not designed for this.
- Toavoid coupling between the one that change the shared state and those who consume the shared state
- To respectunidirectional data flow
There are actually two implementations of this architectural pattern
The issue is the usual one with frameworks and API:
- You have to learn them to understand your application code. What is an action? what is a dispatcher?...
- You will heavily depend on them at the point your project will be recognized as a "Flux project" or a "Redux project".
- Their objects will spread everywhere in your code. Actions everywhere. Dispatchers everywhere.
This project demonstratehow reactive programming is way more simple to implement a shared state with loose coupling. I argue that learning RxJS is more valuable than learning Flux or Redux because it is a programming paradigm that you can leverage in other languages like Java. One single JavaScript file is enough to build you own event bus.
The shared state is just a tree of objects hosted in a singleton called astore. Any modification on this tree will fire an event inside an event bus. We are going to build this event bus using an RxObservable. You can subscribe to it and unsubscribe from it at any point in time.
The observable is like a stream of events. Each event is just a string saying "the property A.B.C.D in the shared state have changed".
The important point here is that we usehot observables instead ofcold observables. In RxJS 6, They are calledConnectableObservable.
- Cold observables are not designed to build an event bus, because you will read all events from the start.
- Hot observables is the perfect choice to build an event bus, because when you connect to it, you get the stream of events as it is now. You don't receive past events.
Sending an event into the bus is done through anObserver with the methodnext:
this.outputObserver.next("a.b.c.d");
As you can see, our event is just a simple string.
The beauty of Rx is that you can filter the stream of events before subscribing to it. This means you can decide to subscribe to a subset of events instead of all of them. This is done using the operatorfilter:
observable.pipe(filter(event=>...selectwhatyouwant...)).subscribe(subscriberCallBack)
In the ES6 classGlobalStore we use this technique in the methodsubscribe.
MyAppStore.subscribe("a.b.c",callback): react only on this specific nodeMyAppStore.subscribe("a.*",callback): react to any change on node 'a' and below
In RxJS when you subscribe, you receive an object used to unsubscribe. So it is important to use this "handle" to avoid any memory leak:
- Subscribe when your component did mount
- Unsubscribe when your component will unmount
Example:
componentDidMount(){console.log("ItemsViewer::componentDidMount subscribe to store...");this.storeHandle=MyAppStore.subscribe("model",this.onModelUpdated);}componentWillUnmount(){console.log("ItemsViewer::componentWillUnmount unsubscribe from store...");if(this.storeHandle){this.storeHandle.unsubscribe();this.storeHandle=null;}}
It would be very dangerous to give a direct reference to the shared state to the consumer. Remember we want to keep unidirectional data flow. Here some solutions to this problem:
- We can clone before delivering the value
- We can use a library that promote immutable data structures likeimmutable.js
To keep it simple, we opted to clone the value, even it is slow.
The ES6 classGlobalStore provides the basics of a shared state with an event bus powered by RxJS.
If you let anybody store something in a shared state, you will have trouble to keep track of what can be found in the state.
For instance:
- Widget W1 stores values in
A.B.C.D - Widget W2 stores values in
A.B.Z - Widget W3 stores values in
A.U.V
Your shared state (GlobalStore) looks like this:
What you don't want is to read all your code base to have the list of all these locations. What we can do, is to inherit fromGlobalStore and provides various getters to hide the keys used to store the values.
Here is a typical example: we hide the key"model" to the caller. The caller see onlyupdateModel and the gettermodel. That's all.
importGlobalStorefrom'./common/GlobalStore';classMyAppStoreextendsGlobalStore{constructor(){super();}updateModel(data){this.send("model",data);}getmodel(){returnthis.get("model");}}/** * Singleton pattern */exportdefaultnewMyAppStore();
Using this store is very simple through the getter (seeItemsViewer.jsx):
MyAppStore.model.listOfItems.map(item=><div>{item}</div>)
Let say you introduce two new keysusers.profiles andusers.credentials:
classMyAppStoreextendsGlobalStore{constructor(){super();}updateModel(data){this.send("model",data);}getmodel(){returnthis.get("model");}updateProfiles(data){this.send("users.profiles")}getprofiles(){returnthis.get("users.profiles");}updateCredentials(data){this.send("users.credentials")}getcredentials(){returnthis.get("users.credentials");}}
At some point in time, the shared state will contain this tree:
So here the idea is to create as many stores you want to encapsulate the keys you are using. All of these stores inherit fromGlobalStore class. In this project, we definedMyAppStore as an example.
What you want is
- To avoid the use of a single giant store for absolutely everything.
- To avoid the use of hard coded keys everywhere in the code with
GlobalStore.send(key)andGlobalStore.get(key)
First install everything:
npm installThen start the application
npm startThen go tohttp://localhost:9090
Notes:
- Each time you click on the button "Update Store", 3 items will be stored with a time stamp. It fires an event on the Observable.
- The button "Add/Remove" demonstrate the case where you need to unsubscribe from the Observable.
You can build without running the server with:
npm run buildAbout
Make your own shared state (store) in your React application with a little bit of reactive programming.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.



