The Observer Pattern In Javascript as Implemented By Redux

0
1338

Whats is a Design Pattern?
In software development, a design pattern is a reusable solution to common problems. Think of them as commonly accepted patterns to solve similar problems. There are four main types of design patterns. These include Creational PatternsStructural PatternsBehavioral Patterns, and Concurrency Patterns.

As a developer, day to day, you will probably not be faced with implementing most of these (at least not knowingly). However, there are times where you may find use for them. Also, there are times where you may be working on a codebase that has a common design pattern implemented.

Beyond implementing these patterns yourself, a good reason to learn them is the fact that a lot of tools you may be using are probably using at least one (many times more) of these patterns. One of these patterns that many React developers use is the Observer Pattern.

What is the Observer Pattern?
The observer pattern, also referred to as the publish/subscribe pattern, is a design pattern where an object (called the subject or observable), will maintain a list of “dependents” called observers. Upon a state change, this subject will notify any of the observers automatically. An Observer (subject) is a one-to-many type of dependency between objects. That means, there is one observer that could have many subscribers.

This subject usually has three methods. One method to register an observer. One to remove an observer. And finally, one that will notify the list of subscribers of the change.

This really simplifies situations where a function or class needs to keep track of something, or better yet, know when something has happened (changed). You have two choices in this situation.

Choice 1
Polling: Polling is simple “pinging” the subject over and over asking for a change. The issue with this is you would have to constantly keep polling to keep your app up to date. What is the frequency in which you would do this? It can become very messy, very fast.

Choice 2
Push: This is the main function of the observer. Push “changes” to all the dependents observing

To explain this in greater detail, I recommend this video. Its almost 50 minutes long but if you speed it up to 1.5x or 2x you can finish it in just about half that time. It is worth it if you are interested in going deeper into the explanation. This video is a good summary of the Observer pattern chapter in the book Head First Design Patterns book.

Redux: A use case for the observer pattern
If you work in the world of React, you have probably dealt with Redux to manage the state of your application.

If you haven’t worked with Redux, or do not know what it is, you can start with the Official docs. If you are like me and don’t always feel like reading, there are plenty of youtube videos explaining what it is.

Redux is an implementation of the observer pattern. The redux store itself is more specific as to how this is done. A store holds the whole state tree of your application. If you want to change the state within the store, you need to dispatch an action on it.

The redux store is actually a very small library (as far as code size) for what it gives you. The store itself is simply an object that has some methods on it. These methods include getState(), dispatch(action), and subscribe(listener). replaceReducer(nextReducer) is another method but not important for this discussion. The following code is a “replica” of creating a redux store. (source fullstackreact)

function createStore(reducer, initialState) {
let state = initialState;
// Setup listners to keep track of when the state is changed
// to triger rerenders (observer pattern)
const listeners = [];
const subscribe = (listener) => (
listeners.push(listener)
);
const getState = () => (state);
const dispatch = (action) => {
state = reducer(state, action);
// call each listener function when the state is changed
// its just a notification that state is changed
listeners.forEach(l => l());
};
return {
subscribe,
getState,
dispatch,
};
}
function reducer(state, action) {
if(action.type === ADD_MESSAGE) {
return {
messages: state.messages.concat(action.message),
}
};
if(action.type === DELETE_MESSAGE) {
return {
messages: [
state.messages.slice(0, action.index),
state.messages.slice(
action.index + 1, state.messages.length
),
],
};
};
return state;
}
// set initial state to pass into to store
const initialState = { messages: [] };
// initialize the store
const store = createStore(reducer, initialState);
class Messages extends React.Component {
componentDidMount() {
store.subscribe(() => this.forceUpdate());
}
render() {
const messages = store.getState().messages;
return (
<div className=ui segment>
<MessageView messages={messages} />
<MessageInput />
</div>
)
}
}

This is a basic react setup where there is a class called Messages that renders a simple chat/messaging app. Within that component, there is a lifecycle method componentDidMount(). This will get called right after the component renders. What it is doing is subscribing to the store. It calls store.subscribe() in order to do this.

What is store.subscribe()? It is where we initialize the store by calling the createStore() function. createStore() is the “subject”. When we call this function you will notice that it takes two arguments. The first argument is reducer, the second is initialState.

reducer is actually the reducer function declared above the Messages component and it contains all of the action types.

Within createStore we are doing a few things related to the observer pattern. The first thing is creating an empty array of listeners.
const listeners = [];
This is what we will use to store listeners/subscribers.

Next there is a subscribe method. This method “pushes” new subscribers to the array
const subscribe = (listener) => (
listeners.push(listener)
); 

Remember that subscribe is called in the componentDidMount method which fires immediately after the component mounts.

The next piece is the dispatch() method. Take a look at the following bits of code here

class MessageInput extends React.Component {
state = {
value: ,
}
onChange = (e) => {
this.setState({
value: e.target.value,
})
};
handleSubmit = () => {
store.dispatch({
type: ADD_MESSAGE,
message: this.state.value,
});
this.ssetState({
value: ,
});
};
render() {
return (
<div className=ui input>
<input
onChange={this.onChange}
value={this.state.value}
type=text
/>
<button
onClick={this.handleSubmit}
className=ui primary button
type=submit
>
Submit
</button>
</div>
);
}
}

This is another component called “MessageInput’ that is called from within the main Messages component. Its function would be to simply enter a new message to send.

The key piece of this component for this subject is the store.dispatch call. dispatch() takes an action. We then set the state equal to the reducer() function that will update all of the reducers and finally call each listener to notify them of the state change.

If that is too much to handle, hopefully this diagram provides a simple summary.

Suggest

ES6 Javascript: The Complete Developer’s Guide

Vue JS 2 – The Complete Guide (incl. Vue Router & Vuex)

The Full JavaScript & ES6 Tutorial – (including ES7 & React)

JavaScript: Understanding the Weird Parts

The Complete JavaScript Course For Web Development Beginners

Source viva: http://www.thedevnotebook.com/2017/08/the-observer-pattern-in-javascript.html

LEAVE A REPLY

Please enter your comment!
Please enter your name here