DEV Community

Cover image for About redux
quoc187
quoc187

Posted on

About redux

I've never used redux before. Today i tried redux-core with React. It did not work, why?

First, why do we use redux?

Managing state, it means redux must have such functionalities like subscribes to changes, makes the components update when state changed, right?
Does redux-core have such things? the answer is No, it doesnt. That's why they created react-redux.

The reducer function.

Simply take the previous state and the action, which is a normal object, but we usually structure this as a object with 2 fields: type(string) and payload(any), of course we can use any type but that is not recommended.

How redux initiated the state?

There's 2 way to initial Redux state, arcorrding to the docs.
The first way is passing the preloadedState to createStore function. Nothing to say about this way.
The second way is by passing the reducer's default argument for state, something like const reducer = (state = defaultState, action) => ....
Arcorrding to the docs, redux will dispatch abitrary action to get the default-state, that action will fall in to the default case we provided(if we use switch to determine the type).

createStore(combineReducers({
    test1: (state = 'YESY', action: any) => { console.log('test1', state, action); return state; },
    test2: (state = 'YESY2', action: any) => { console.log('test2' ,state, action); return state; },
  })
Enter fullscreen mode Exit fullscreen mode

You will get the store of { test1: 'YESY', test2: 'YESY2' }
The initial state is always the default value of state? No, it is not, since redux dispatches some dummy actions to get the default state, it runs our reducer and treat the return-value as a default state.
Try this

createStore(combineReducers({
    test1: (state = 'YESY', action: any) => { console.log('test1', state, action); return 'NOT_A_DEFAULT_VAL'; },
    test2: (state = 'YESY2', action: any) => { console.log('test2' ,state, action); return state; },
  })
Enter fullscreen mode Exit fullscreen mode

You will get the store of { test1: 'NOT_A_DEFAULT_VAL', test2: 'YESY2' }

We dispatch a action, which reducers will be run?

Redux passes the action to all the reducer, if the reducer expects action, it will return new state, otherwise returns the previous state(maintained the referrence)(this depends on your implementation, it might return previous state or something else). So i guess if we have about 10000 reducers :D, our app will be so freaking slow.

What applyMiddleware does?

Ive felt very confuse the first time read the docs, it said take the dispatch, return new dispatch, i cant figure out how it works fromt the docs, so i choose to read the source code :D
applyMiddleware
applyMiddleware creates new dispatch function, redux assign this dispatcher to store. The dispatch is created by calling compose(...chain)(store.dispatch)
chain is created by mapping all the middleware to the result returned by calling itself with the context, context contains the getState, and the default dispatcher, note that it map, not reduce, so every context passed to the middleware are the same and middlewares passed to applyMiddleware will be called from left to right
Continue on, the chain will be composed together, but what compose does? compose takes a list of function, then returns a functiont that take only 1 parameter, when we call the function, first the last function in compose'parameter will be called, get the value returned, call the last-1 function with that value, and so on to the first parameter. Confuse right, here is a example: compose(fn1, fn2, fn3)(input) => fn1(fn2(fn3(input)), dispatcher returned by applyMiddleware will be called from right to left. It means whatever the chain is, the first middleware will take the original-action, the final-action will depends on what the first middleware's dispatcher returns, usually we will call the next dispatcher and get back the (modified) action, and do things with the action returned.
Back to the applyMiddleware, new dispatcher is created by calling compose with chain, and pass in a default dispatcher, note that default dispacher and new dispacher is the same type, so chain must be a list of function that take a dispatcher and return a dispatcher, arcorrding to how compose works.
Finnally we can simplify the parameter of applyMiddleware like so:

({ dispatch: defaultDispatch, getState }) => lastDispatch => newDispatch
Enter fullscreen mode Exit fullscreen mode

What is the lastDispatch of the last middleware we passed? it simply the function that returns whatever i takes. lastDispatch = (action) => action.
What if one middleware didnt call the lastMiddleware? The chain will stop right at that middleware, any middleware after that will not be called, and makes no effect.
The only thing a dispatcher can do is modify the action passed to it, but how we archieve the modified action? That's the question i asked myself, the answer is returning the modified version, so the dispacher that called it can get back the modified action. Whatever we wanna do in our middleware's dispacher, better return an action

How it works

Top comments (0)