The ability to reply to discussions is limited to PRO members. Want to join in the discussion? Click here to subscribe now.

Redux: Implementing Store from Scratch

Redux: Implementing Store from Scratch

2:28
Learn how to build a reasonable approximation of the Redux Store in 20 lines. No magic!
Watch this lesson now
Avatar
egghead.io

Learn how to build a reasonable approximation of the Redux Store in 20 lines. No magic!

Avatar
Steve

I'm curious why listeners aren't called with the current state? What is the advantage of the listeners calling getState() themselves?

In reply to egghead.io
Avatar
Dan Abramov

Thanks, it's a great question. The main reason is to keep the API orthogonal. Basically, same reason why React components don't receive props as argument to render(). There should be just one obvious way to do something.

For example, in Rx, subscribers receive new value like you described. But in Rx there is no notion of "current" state. So argument is the only way to receive current value. In our case, however, getState() exists, and this is why we don't pass it as argument.

If this sounds unconvincing consider that subscribe is a low level API. Usually you'd use connect from React Redux or a custom helper. Why? It's usually not enough to just receive the current state. You also want to have the previous state so you can compare whether the part you're interested in has changed. In fact you might want to write a function like subscribeTo(store, selectState, onChange) that encapsulates it. At this point it doesn't matter that you don't receive an argument in the low level subscribe API because you created a custom function and you can do it in your API just fine.

More practically we do this because there are wrappers around Redux Store that may change its behavior. For example, Redux DevTools wraps both reducer and getState. If the subscriber received the current state, such "store enhancers" would also have to override subscribe which is fragile. Orthogonal APIs are much simpler to extend!

In reply to Steve
Avatar
Steve

Dan,
Thanks for the detailed reply. I guess I still don't see how the signature of the subscribe function affects the signature of the subscriber/callback/listener function? If, for example, the convention for calling all callback/listener functions was with the action, previous state and new state the subscribe function would stay the same:

const render = (state, action, previous_state) => {
document.body.innerText = state;
};
store.subscribe(render);

I see how I could write my own higher level subscription function like subscribeTo() to filter based on content of the new state by supplying a filtering callback function via the subscribe primitive (although I don't yet see how I would get access to prior state since the dispatch() function appears to call the listener/callback after the state change; but maybe you'll cover that in later videos!).

I'm looking forward to the future videos; so I can see how I can use the Redux API as you intended.

In reply to Dan Abramov
Avatar
Dan Abramov

If you’re interested in why it’s best to keep subscriber signature argumentless, please check out Redux DevTools implementation, and how it overrides getState() method on the store. It’s hard to continue the discussion without that. ;-)

In reply to Steve
Avatar
Steve

At first, looking at that code I couldn't follow the lifting/unlifting concepts and the multiple levels of functions being returned. But I came across the middleware docs: http://rackt.org/redux/docs/api/applyMiddleware.html which helped explain the design. I've read that doc multiple times and I'm starting to follow the implementation.

I'm used to middleware implementations where the action and state are passed along (or not) and/or extended/modified on the way through the "chain" of middleware. For example this is the Django middleware model where middleware can access/modify the request(action) and response (state): https://docs.djangoproject.com/en/1.9/topics/http/middleware/#hooks-and-application-order.

To my eye it appears to be functionally equivalent to the redux middleware mechanism(?). In the Django model, it is easy to write middleware by implementing a function accepting a request or response and reacting to/modifying/stopping/delaying it. So Django uses a convention of supplying the (possibly modified) action/state to each middleware in the chain w/o an equivalent to redux's getState().

As I mentioned previously, it isn't my intent to argue about the model you've chosen for redux. But I hope you can see from my previous experience why I asked my question. Now that I've found the middleware docs I see how you thought about the problem.

In reply to Dan Abramov
Avatar
Lucas

Hello, i didn't understand why needed to return the function that remove de subscriber listener of listeners array.

Avatar
Negin

Hi,

I have a very basic and fundamental question.

Why do we need to implement createStore while Redux provide it to us?

In which circumstances we need to create our own store and in which situation we can use Redux store?

Avatar
Sammy Gonzales-Luna

I too was confused by this aspect of the createStore implementation. If you listen close, Dan mentions that the purpose of the return statement is to avoid having a separately defined method to UN-subscribe a listener. Therefore, the return statement returns an anonymous function that takes a listener and removes that listener from the listeners array.

In reply to Lucas
Avatar
Justin

He is just showing how it works by providing a simplified example of what goes on "behind the scenes."

In reply to Negin
Avatar
Vadim

And I want to extend your great answer with one thing, because it may be not obvious for everyone. We need to save store.subscribe(render) to a variable, which we will call with parenthesis for unsubscribe. It may seems like:

//For subscribe
let renderSubscription = store.subscribe(render);
//For unsubscribe
renderSubscription();
In reply to Sammy Gonzales-Luna
Avatar
aaronisme

I got a question about how to implement the unsubscribe here, why we are not providing an dedicated method to do it?

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?