Build Your Own Flux

Architecture in the Client

What’s the idea behind our own Flux?

{
type: ‘ADD_ITEM_TO_TOP’,
item: {
name: 'John Doe',
id: 1234
}
}
  • Other than the proposal in Flux, we would allow only one big store containing all the application state
  • The dispatcher itself is silly, but can be enriched by handler functions, that register for action ids
  • We divide these functions into two types:
  • reducers (borrowed from Redux) work in the context of the store and define the next state of it. These reducers are pure functions as defined in functional programming. They are processed without any side effects to the rest of the application, just transforming an input into an output - in that case a completely new object holding the next state.
  • services define the transition to all backing systems like workers, access to local storage and of course the ajax interface of the backend.
  • The store itself is modularized into one core part and one section for each registered module.
  • We subclass React.Component to connect React to this system. Once connected the Component gets its state exclusively from the store

Enter with a Service Handler

{
type: 'STARTPAGE:ENTER',
user: 12345
}
dispatcher.registerService(
'STARTPAGE:ENTER',
function(action, dispatch) {
http.get('/user/' + action.user + '/stories').then(stories => {
dispatch({
type: 'STARTPAGE:INCOMING',
stories: stories
});
}).catch(err => {
dispatch({
type: 'STARTPAGE:SERVER_ERROR',
error: err
});
});
)}
);

Modify the State

dispatcher.registerReducer(
'STARTPAGE:ENTER',
function(stateTools, action) {
return stateTools.transform('startpage', {
message: 'Your latest stories are being fetched'
});
)}
);
dispatcher.registerReducer(
'STARTPAGE:INCOMING',
function(previousState, action, transform) {
return transform(previousState, {
startpageMessage: '',
startpageStories: action.stories
});
}
)

Create a State Aware Component

class StoryList extends React.Component { …}
class StoryList extends StoreComponent { … }
constructor() {
super();
registerFor({
startpageMessage: 'No information available',
startpageStories: []
};
)}
render() {
return(
<main>
<div class="message">
{this.state.startpageMessage}
</div>
<ul class="stories">
{this.state.startpageStories.map(story => (
<StartpageStory story={story}/> // #A
))}
<ul>
</main>
)
}

Implementing the StoreComponent

class StoreComponent extends React.Component { // #A
constructor() {
super();
this.store = dispatcher.getStore(); // #B
}

componentDidMount() {
this.listenerId =
this.store.onChange(
this._handleChange.bind(this)
); // #C
}

componentWillUnmount() {
this.store.removeListener(this.listenerId); // #D
}

registerFor(items) {
this.registeredKeys = items;
this._setStateFromStore(); // #E
}

_setStateFromStore() {
const newState = {};
for(let key of Object.keys(this.registeredKeys)) {
newState[key] = this.store.get(key) !== undefined ?
this.store.get(key) :
this.registeredKeys[key];
}
}
this.setState(newState); // #F
}

_handleChange(state, keys) {
if(this._intersection(keys, this.keys).length > 0) {
this._setStateFromStore(); // #C
}
}

_intersection(keys1, keys2) {
return keys1.filter(key => keys2.indexOf(key) > -1)
}
}

Summary

  • the Store
  • the Dispatcher
  • the StoreComponent
  • having module based partitions in the store
  • creating a garbage collector, that automatically deletes module data from the store if the user has left a module
  • connecting everything to the browser’s local storage to preserve settings for the user
  • creating a rewind/undo function, which should be easy, because our reducers are fully compatible with Javascript’s Array.prototype.reduce()

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matthias Reis

Matthias Reis

More from Medium

🗿📃✂️🖖🦎”Rock Paper Scissors Spock Lizard” RPSSL Game 🕹️

Configure vite and storybook to respect alias of tsconfig.json

SvelteKit doesn’t like symlinks: compare absolute paths instead of relative paths

Leave tracker for a website: how to catch the moment your customer is leaving