Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Make your own shared state (store) in your React application with a little bit of reactive programming.

License

NotificationsYou must be signed in to change notification settings

hypercube-software/flux-pattern-with-rxjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

1.1 What Flux is all about?

Here is the Flux pattern:

https://www.opencodez.com/wp-content/uploads/2018/11/React-Flux-Action.png

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 official Facebook Flux. Official site ishere.
  • Redux, official site ishere.

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.

1.2 RxJS to the rescue

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.

1.2.1 The main idea

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.

1.2.2 How to subscribe to a subset of events

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 node
  • MyAppStore.subscribe("a.*",callback): react to any change on node 'a' and below

1.2.3 Don't forget to unsubscribe

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;}}

1.2.4 Protect the shared state

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.

1.3 A little bit of OOP for better encapsulation

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 inA.B.C.D
  • Widget W2 stores values inA.B.Z
  • Widget W3 stores values inA.U.V

Your shared state (GlobalStore) looks like this:

image-20200911223336153

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:

image-20200911223041604

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 withGlobalStore.send(key) andGlobalStore.get(key)

2 How to test this project

First install everything:

npm install

Then start the application

npm start

Then go tohttp://localhost:9090

image-20200911232645445

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 build

About

Make your own shared state (store) in your React application with a little bit of reactive programming.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp