admin管理员组

文章数量:1323735

Code es from the reduce-reducer repo:

   export function reduceReducers(...reducers) {
        return (previous, current) =>
            reducers.reduce(
                (p, r) => r(p, current),
                previous
            );
    }

I understand that the purpose of this function is to flatten the different slices of state in Redux, see here, but I don't understand how this function works. I have looked up MDN, but still don't understand.

What is calling previous, current and what do p and r represent. I can't identify the variables being called.

Edit

Mark Erikson defines this function in his Practical Redux Series:

reduceReducers is a nifty little utility. It lets us supply multiple reducer functions as arguments and effectively forms a pipeline out of those functions, then returns a new reducer function. If we call that new reducer with the top-level state, it will call the first input reducer with the state, pass the output of that to the second input reducer, and so on.

Edit 2

I wrote a post to explain reduceReducers().

Code es from the reduce-reducer repo:

   export function reduceReducers(...reducers) {
        return (previous, current) =>
            reducers.reduce(
                (p, r) => r(p, current),
                previous
            );
    }

I understand that the purpose of this function is to flatten the different slices of state in Redux, see here, but I don't understand how this function works. I have looked up MDN, but still don't understand.

What is calling previous, current and what do p and r represent. I can't identify the variables being called.

Edit

Mark Erikson defines this function in his Practical Redux Series:

reduceReducers is a nifty little utility. It lets us supply multiple reducer functions as arguments and effectively forms a pipeline out of those functions, then returns a new reducer function. If we call that new reducer with the top-level state, it will call the first input reducer with the state, pass the output of that to the second input reducer, and so on.

Edit 2

I wrote a post to explain reduceReducers().

Share Improve this question edited Jan 5, 2018 at 17:55 Avi Kaminetzky asked Jan 4, 2018 at 22:56 Avi KaminetzkyAvi Kaminetzky 1,5382 gold badges21 silver badges45 bronze badges 2
  • Can you be more specific about what you don't understand about it? – Sterling Archer Commented Jan 4, 2018 at 23:06
  • good point, I have updated – Avi Kaminetzky Commented Jan 4, 2018 at 23:13
Add a ment  | 

3 Answers 3

Reset to default 6

Perhaps it's a bit clearer if written this way:

export function reduceReducers(...reducers) {
    return function (state, action) {
        const reducerFunction = function (accumulator, currentValue) {
            return currentValue(accumulator, action);
        }

        return reducers.reduce(reducerFunction, state);
    }
}

Parameters & variables:

  • reducers: the array of reducers you want to reduce. In the question you linked this would be [reducerAdd, reducerMult]
  • state: (formerly previous) the redux state object
  • action: (formerly current) the action object
  • accumulator: (formerly p) the object used by the reduce() function. Initial value is the previous redux state
  • currentValue: (formerly r) the current reducer function to be run by the reduce() function

reduceReducers() returns a function (in your linked answer it's the addAndMult() function) that gets called by redux when it's processing dispatched actions. redux passes in it's previous state with the current action.

reduce() is run on the array of reducers ([reducerAdd, reducerMult]) which uses the reducerFunction (previously an anonymous arrow function, but I've named it for clarity) to call each reducer in turn on the redux state object that is passed in.

Hopefully that makes sense.

reducers is just a JavaScript array, and reducers.reduce is therefore good ol' Array.reduce. This takes a reducing function and an initial value to reduce. The reducing function can take four parameters but we only care about the first two here:

  • accumulator: the object gradually being built, returned at the end of the reduce
  • currentValue: the current array element

p and r are the accumulator and currentValue, above. p starts out with the value of previous. This is an array of functions (each reducer in turn is represented by r) so calling:

r(p, current)

is the same as calling:

r(previous, current)

for the first element in the array. After that, p bees gradually different to previous as the reducers do their work.

Array.reduce can be a little hard to wrap your head around at first. In this particular case, it might be summarised in plain language as follows:

Start with a state object (previous), and sequentially call functions on it, finally returning a new state object.

Likewise, the entire function could be summarised as:

Given some reducers as arguments, return a reducer function which will invoke each specified reducer in sequence, returning a new state object.

Personally, I think the variable current is probably better named action for clarity, but that's just what I'm used to I suppose.

let's have a look how reduce work and what it does. I haven't had a look the native reduce function but it would be similar to the following.

Array.prototype.reduce = function(fn,initial = 0){
   let i = 0;
   let accumulator = initial;
   while(i<this.length){
     accumulator = fn(accumulator,this[i++]);
   }
   return accumulator;
}

Let's use this reduce function with the example from the MDN site you brought.

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

console.log(array1.reduce(reducer, 5));
// 5 + 1 + 2 + 3 + 4
//expected output: 15

If you look at the reduce function body, this does the job

while(i<this.length){
    accumulator = fn(accumulator,this[i++]);
}

it passes the accumulator value which returns from the callback function. so the accumulator value would be changed something like the followings

  1. accumulator = 5(initial value) + 1(arr[0]) = 6
  2. accumulator = 6(accumulator from above) + 2(arr[1]) = 8
  3. accumulator = 8(accumulator from above) + 3(arr[2]) = 11
  4. accumulator = 11(accumulator from above) + 4(arr[3]) = 15

now the accumulator value is 15.

Let's move to the question you posted.

export function reduceReducers(...reducers) {
    return (previous, current) =>
        reducers.reduce(
            (p, r) => r(p, current),
            previous
        );
}

So this is so called partial application or currying in functional programming.
As the name says you partially invoke the function.
This is a small example.

const myfamily  = (lastName) => (firstName) => `${firstName} ${lastName}`;
const printName = myfamily("Kim");
const me        = printName("Jack"); // console Jack Kim
const parnter   = printName("Sarah");// console Sarah Kim

In this example, I want to print out our family members' full name.
so instead of writing repetitive surname, I invoke the myFamily function first passing surname and it returns another function which our surname is referenced inside the function body ( javascript closure ).
Using this technique, we can create a lot of interesting functions like once,debounce etc,
but this is out of our scope

Now, if we look at the reduceReducers function, the function signature looks identical to myFamily function.

It first accept reducers array and returns another function which expecting previous and current objects.

let's say we have the following reducers array

let reducers = [
function a(pre,cur){ return Object.assign(pre,{a:"This is a"},cur); },
function b(pre,cur){ return Object.assign(pre,{a:"This is new a",b:"This is 
b"},cur); },
function c(pre,cur){ return Object.assign(pre,{c:"This is c",final:"This is 
the last"},cur); }
];

we can call the reduceReducers function like this

const willbeReduced = reduceReducers.apply(null,reducers);

now we have willbeReduced function,
we can call it like this.

willbeReduced({pre:"This is previous"},{cur:"This is current"})
our state would be
     {pre:'This is previous',
        a:'This is new a',
      cur:'This is current',
        b:'This is b',
        c:'This is c',
    final:'This is the last'}

So previous and current are just plain objects. previous is used for the initial object for reduce function

p and r

p is the accumulator value
r is each function from the reducers array

r expects two objects first accumulator and current object and returns new object ( new accumulator value )

Have a look at the reduce function signature again and reducers array

本文标签: javascriptHow Does reduceReducers() WorkStack Overflow