Middleware with deep understanding of redux

Keywords: JSON github

See my github for the code in this article. https://github.com/Rynxiao/redux-middleware

For redux usage, see the previous article http://blog.csdn.net/yuzhongzi81/article/details/51880577

Understanding reduce function

The reduce() method receives a function as an accumulator, and each value in the array (left to right) begins to shrink and ends up with a value.

arr.reduce([callback, initialValue])

As for the use of reduce, I won't go into more detail here, so I can go. Here See

Look at the following examples:

let arr = [1, 2, 3, 4, 5];

// 10 represents the initial value, p represents the cumulative value for each time, and 10 for the first time.
// If there is no initial value, then the first value of p is 1
// At this point the cumulative result is 15.
let sum = arr.reduce((p, c) => p + c, 10);  // 25

// Converting to es5 means:
var sum = arr.reduce(function(p, c) {
    console.log(p);
    return p + c;
}, 10);

Next, let's look at an advanced extension of reduce. Now there is such a data structure, as follows:

let school = {
    name: 'Hope middle school',
    created: '2001',
    classes: [
        {
            name: 'Class two in three years',
            teachers: [
                { name: 'Zhang Er er', age: 26, sex: 'male', actor: 'Headmaster' },
                { name: 'Wang Xiao Xiao', age: 23, sex: 'female', actor: 'English teacher' }
            ]
        },
        {
            name: 'Star class',
            teachers: [
                { name: 'Ouyang Nana', age: 29, sex: 'female', actor: 'Headmaster' },
                { name: 'Li Yifeng', age: 28, sex: 'male', actor: 'physical education teacher' },
                { name: 'Yang Mi', age: 111, sex: 'female', actor: 'Art teacher' }
            ]
        }
    ]
};

For example, I want to get the name of the first teacher in the first class of this school. Maybe you will write as follows:

school.classes[0].teachers[0].name

Is that all right? so easy! Yeah, there's no problem with "no problem" if you already know that the value really exists, then what if you don't know? Maybe you want to write like this:

school.classes &&
school.classes[0] &&
school.classes[0].teachers &&
school.classes[0].teachers[0] &&
school.classes[0].teachers[0].name

I'm going to be a freshman, but is it true that the scenario of taking values in deep objects is real in work? What should I do? Wandering to know a great God Solution As follows:

const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);

// call
get('classes', 0, 'teachers', 0, 'name', school);   // Zhang Er er

Is it easy to solve this problem gracefully with reduce?

Understanding the Compoose Function of Reux

After talking about Redux for so long, isn't that redux? That's embarrassing. Now let's see why we need to talk about the reduce function. Go to github and find the Redux source code. You will see a compose.js file with 22 lines of annotations, in which reduce is used. So what is this function for? Let's have a look:

export default function compose(...funcs) {
    if (funcs.length === 0) {
      return arg => arg
    }

    if (funcs.length === 1) {
      return funcs[0]
    }

    return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

Initially, it looks like a nested call to a function. Let's go and find out where we'll use this function. Look in the source code and find this call in applyMiddleware.js:

export default function applyMiddleware(...middlewares) {
    return (createStore) => (reducer, preloadedState, enhancer) => {
        const store = createStore(reducer, preloadedState, enhancer)
        let dispatch = store.dispatch
        let chain = []

        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)

        return {
          ...store,
          dispatch
        }
    }
}

See something familiar? Apply Middleware, we're writing functions that middleware must use. Let's see how a simple middleware is written. For example, if I want to write a logger Middleware, it's like this:

const logger = store => next => action => {
    console.log('action', action);
    let result = next(action);
    console.log('logger after atate', store.getState());
    return result;
}

When we created a store, we called it this way:

let middlewares = [loggerMiddleware, thunkMiddleware, ...others];
let store = applyMiddleware(middlewares)(createStore)(reducer, initialState);

So the funcs passed to compose are actually an array of such functions:

function(next) {
    return function(action) {
        return next(action);
    }
}

What kind of chemical reactions will occur when such an array is passed to compose? Looking at it a little, it should be easy to see that eventually a function will be returned. This function is processed through layers of middleware, and the final shape is still the same as above. Note that the next action is not executed at this time, when it is executed

compose(...chain)(store.dispatch)

After that, it returned like this:

function(action) {
    return next(action);
}

Ladies and gentlemen, do you see anything? It's like dispatch in createStore. Yes, it's actually a dispatch, but it's just a dispatch that's on the go, waiting for another chance. We have a number of action s plus one, like this:

export function addCount() {
    return {
        type : ADD_COUNT
    }
}

// Now let's trigger it.
dispatch(addCount());

Yes, dispatch is executed at this time. What will happen when the outermost dispatch is executed? Look at the following:

return next(action);

// This next is the dispatch function, but the dispatch function is retained every time it is executed.
// The reference to the dispatch function passed by the last middleware, so it will be passed on all the time.
// Until the final store.dispatch execution

So let's look at the definition of dispatch function in createStore:

function dispatch(action) {
      // ...

      try {
            isDispatching = true
            currentState = currentReducer(currentState, action)
      } finally {
            isDispatching = false
      }

      // ...

      return action
  }

Find this sentence

currentState = currentReducer(currentState, action);

When this step is executed, at this moment, the original initial state value passed in has changed, and then the operation after middleware will be executed layer by layer. Remember what we wrote in middleware?

const logger = store => next => action => {
    console.log('action', action);
    let result = next(action);
    console.log('logger after atate', store.getState());
    return result;
}

That's why we retrieve the state in the store after next execution.

Asynchronous middlewares

Asynchronous action may be written differently from immediate action, for example:

// Non-pure functions defined to provide asynchronous request support
// Thunk middleware needs to be used in sotre
export function refresh() {
    return dispatch => {
        dispatch(refreshStart());
        return fetch(`src/mock/fetch-data-mock.json`)
            .then(response => response.json())
            .then(json => {
                setTimeout(() => {
                    dispatch(refreshSuccess(json && json.data.list));
                }, 3000);
            });
    }
}

Why use thunk Middleware? Let's find out what exactly is written in thunk Middleware.

function createThunkMiddleware(extraArgument) {
    return ({ dispatch, getState }) => next => action => {
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }

        return next(action);
    };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

Just 14 lines of code. Look at this sentence:

if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
}

If the type of action is function, then it will be executed directly. In fact, it is to convert an asynchronous operation into two immediate actions, which only need to send state before and after asynchronization. Why decompose? What would happen if it didn't decompose? Do you remember this line of code?

currentReducer(currentState, action);

Here reducer ** only accepts pure functions, only pure functions, only pure functions **, important things to say three times. So what the hell is it that you pass an impure function? Isn't that the default switch? So the state we get is still the state we used to be, without any change.

Posted by deejay1111 on Sat, 01 Jun 2019 15:38:08 -0700