[8:19 PM] Rtransat: What is the best way to set data in reducer after we put other data in other reducer?
For example I have a list of items in a reducer:
[{
amount: 10
}, {amount: 20}]
And another reducer with the value null by default and after fetching the first data in the store I want to calculate the sum of the total amount
[8:19 PM] Rtransat: and put it in a store too
[8:19 PM] acemarke:
function* someNumbers() {
yield 1;
yield 2;
yield 3;
}
const iter = someNumbers();
console.log(iter.next());
// {value : 1, more : true} , or something like that
[8:19 PM] acemarke: that's basic generator syntax
[8:19 PM] ridencode: i'd call .next() ?
[8:19 PM] ridencode: or saga/redux calls it for me
[8:19 PM] acemarke: the saga middleware calls that for you
[8:19 PM] ridencode: gotit
[8:22 PM] Rtransat: I need to dispatch an action after the end of my fetching data?
[8:23 PM] ridencode: i think so..
[8:24 PM] Rtransat: So I'm fetching my data, put it in state, I use a selector to have the sum of my previous data, and I dispatch an action to put the result in my other state reducer?
[8:25 PM] acemarke: that's one way. You can also restructure your reducer logic so that the "sum things up" reducer runs after the "put everything in place" reducer
[8:26 PM] acemarke: I've got some examples of doing that here: http://blog.isquaredsoftware.com/2017/01/practical-redux-part-7-forms-editing-reducers/#structuring-reducer-logic-for-features and http://redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html
Mark's Dev Blog
Practical Redux, Part 7: Form Change Handling, Data Editing, and...
[8:26 PM] acemarke: roughly:
[8:27 PM] Rtransat: it seems "complicated" :/
[8:28 PM] acemarke:
// before
const rootReducer = combineReducers({a, b, c, d});
const store = createStore(rootReducer);
// after
const mainReducer = combineReducers({a, b, c, d});
const doMoreThingsReducer = (state, action) => {
// do more work here:
return newState
}
const rootReducer = reduceReducers(mainReducer, doMoreThingsReducer);
const store = createStore(rootReducer);
[8:28 PM] ridencode: what is reduce reducers
[8:29 PM] acemarke: https://github.com/acdlite/reduce-reducers
GitHub
acdlite/reduce-reducers
reduce-reducers - Reduce multiple reducers into a single reducer.
[8:29 PM] ridencode: sure'd be nice if we knew all tehse functions you know of haha
[8:29 PM] Rtransat: I did not understand your example x)
[8:29 PM] acemarke: which is, in its entirety:
export default function reduceReducers(...reducers) {
return (previous, current) =>
reducers.reduce(
(p, r) => r(p, current),
previous
);
}
[8:29 PM] acemarke: basically, call each reducer in turn, pass the result to the next one
[8:30 PM] Rtransat: @acemarke so I have 2 reducer, I do my logic sum and after I merge the reducer into one?
[8:30 PM] ridencode: ohh
[8:30 PM] ridencode: makes perfect sense now
[8:30 PM] acemarke:
const timesTwo = (state, action) => state * 2;
const addFour = (state, action) => state + 4;
const rootReducer = reduceReducers(timesTwo, addFour);
rootReducer(5, dontCare); // 14
[8:30 PM] acemarke: or at least, it should be that
[8:30 PM] ridencode: your before example just didn't show enough code so it was confusing
[8:31 PM] acemarke: again, making things up as I go :)
[8:31 PM] acemarke: so, rootReducer is one big function
[8:31 PM] Rtransat: but if I need to recalculate thing when the amount change?
[8:32 PM] Rtransat: I have DepartmentList with amount and other one called with total amount from previous department. When previous department change the total need to be recalculate everytime
[8:33 PM] acemarke: sure. Your "second reducer" could do that
[8:33 PM] Rtransat: Why I need to have the total in the same reducer?
[8:33 PM] Rtransat: Or, why I need to merge the total in the same reducer
[8:33 PM] acemarke: Redux only has one reducer function in the store
[8:34 PM] acemarke: but that one "root reducer" can be made up of many functions inside
[8:34 PM] ridencode: lol
[8:34 PM] Rtransat: @acemarke Yes, I wanted to say functions from reducer not reducer itself
[8:35 PM] acemarke: hmm. I'm not quite sure I understand the question
[8:35 PM] acemarke: lemme see if I can explain it another way
[8:36 PM] acemarke: the typical approach is to just use combineReducers to produce the root reducer
[8:36 PM] acemarke: which splits up the work based on the keys in your state
[8:36 PM] acemarke:
const rootReducer = combineReducers({
users : userReducer,
items : itemsReducer
});
[8:36 PM] acemarke: neither of those slice reducers cares about the "timing" at all
[8:37 PM] acemarke: but now, you're wanting to introduce some ordering into the work
[8:37 PM] acemarke: you need Part A to be done first, so that another chunk of logic can read the values
[8:37 PM] Rtransat: @acemarke this state if fine?
{
departments: [
{name: 'foo', amount: 100},
{name: 'bar', amount: 200},
],
totalAmount: 300
}
[8:37 PM] acemarke: sure
[8:37 PM] ridencode: lol
[8:38 PM] acemarke: it's just a question of how you want to organize the logic to update that state
[8:38 PM] acemarke: here's one other thought
[8:38 PM] ridencode: just child nest all the actions?
[8:38 PM] acemarke: you don't have to keep totalAmount in state
[8:38 PM] Rtransat: I fetch departments, I calculate total and I dispatch an action to calculate the sum? Nothing wrong about that?
[8:38 PM] acemarke: you can derive it
[8:38 PM] ridencode: for order of reducers
[8:38 PM] acemarke: yeah, you can also dispatch multiple actions if you want to
[8:38 PM] acemarke: so for the "deriving" aspect
[8:38 PM] acemarke: if I have [100, 200] in state already
[8:38 PM] acemarke: I don't have to keep the calculated total in state too
[8:39 PM] acemarke: I can just read those values and add them up when I need to use the result
[8:39 PM] Rtransat: So the total will be in a local state component?(edited)
[8:41 PM] Rtransat: @acemarke so you know my problem better than before π I don't need reduce reducer thing?
[8:41 PM] sheldonj: Don't put computed values in your reducers. Use memoized selectors.
[8:42 PM] acemarke: it's basically the same answer as this FAQ question: http://redux.js.org/docs/faq/Reducers.html#reducers-share-state
[8:42 PM] acemarke: you can add more reducer logic in sequence; you can dispatch multiple actions; you can derive the results as needed
[8:42 PM] Rtransat: I never memoized thing yet ^^. My app is not huge π
[8:42 PM] acemarke: all valid approaches
[8:42 PM] sheldonj: https://github.com/reactjs/reselect
GitHub
reactjs/reselect
reselect - Selector library for Redux
[8:43 PM] Rtransat: Ok, I need to choose one of them then
[8:43 PM] sheldonj: If you need a simple solution
[8:43 PM] sheldonj: Then just create a simple function in your mapStateToProps
[8:43 PM] Rtransat: @sheldonj memoize is good for perf? I don't need perf yet π I'm still practicing with little project
[8:44 PM] sheldonj: You don't need to memoize to leverage selectors
[8:44 PM] Rtransat: I use selector (but without memoization ;))
[8:45 PM] Rtransat: @acemarke I think I will use "derivate" approach π
[8:45 PM] Rtransat: Thx guys π
[8:45 PM] acemarke: sure
[8:48 PM] Rtransat: @acemarke If you have some time left for me π https://www.webpackbin.com/bins/-KfYlOkauSTjrAWCDUnz
The comment in my component seems fair to you?
[8:49 PM] acemarke: sure. I'd suggest adding curly braces and doing the calculation before you return the JSX, but should be fine
[8:50 PM] acemarke:
const DepartmentList = ({
departments
}) => {
const total = departments.reduce( (sum, dept) => {
return sum + dept.amount;
}, 0);
return (
<div>
<h1>Liste des dΓ©partements</h1>
{departments.map((department) => <Department {...department}/>)}
<DeptTotal total={total} />
</div>
)
}
[8:50 PM] Rtransat: I need the same structure as my department because when the total change I will need to display the sum of everything x)
[8:50 PM] everdimension: @acemarke this reduceReducers stuff is very interesting, thanks for bringing it up
I saw it some time ago and could not get why it would be used but now I see it much more clearly.
[8:50 PM] acemarke: sure
[8:51 PM] acemarke: one way to think of it
[8:51 PM] acemarke: the original "Flux architecture" included this store.waitFor() idea
[8:51 PM] acemarke: ie, you need some ordering when stores update
[8:51 PM] acemarke: with Redux, there's only one store
[8:51 PM] acemarke: now the "stores" are functions
[8:51 PM] acemarke: so, if you want to have ordering between functions.... write logic that calls functionA first, and functionB second :)
[8:52 PM] Rtransat: @acemarke Thx dude ^^
[8:53 PM] Rtransat: I'm still trying to understand the reduce reducer thing but even when your explaination, don't get it at all :/
[8:54 PM] acemarke: go back to this example:
const timesTwo = (state, action) => state * 2;
const addFour = (state, action) => state + 4;
const rootReducer = reduceReducers(timesTwo, addFour);
rootReducer(5, dontCare); // 14
[8:54 PM] acemarke: when we call rootReducer()
, it first calls timesTwo(5, undefined)
[8:54 PM] acemarke: that returns 10
[8:54 PM] acemarke: then it takes that result, and calls addFour(10, undefined)
[8:54 PM] acemarke: and it takes the result of addFour
, and that's the final result
[8:55 PM] acemarke: it's a pipeline
[8:56 PM] acemarke: so, if we have:
const mainReducer = combineReducers({a, b, c, d});
const doMoreThingsReducer = (state, action) => {
// do more work here:
return newState
}
const rootReducer = reduceReducers(mainReducer, doMoreThingsReducer);
const store = createStore(rootReducer);
[8:56 PM] acemarke: when the action is dispatched, it first goes to mainReducer(state, action)
[8:56 PM] Rtransat: I understand until the dontCare variable
[8:56 PM] Rtransat: if dontCare is 10 or 1000 or 5000 the result will be the same?
[8:56 PM] acemarke: that's just me saying we're calling rootReducer
ourselves, and our example functions don't care about actions
[8:56 PM] acemarke: replace that with rootReducer(5, undefined)
[8:57 PM] acemarke: you can see that timesTwo
and addFour
don't use the action parameter
[8:57 PM] acemarke: here, lemme try another variation
[8:58 PM] acemarke:
const times= (state, action) => state * action.times;
const add= (state, action) => state + action.add;
const rootReducer = reduceReducers(times, add);
rootReducer(5, {times : 2, add : 4}); // 14
[8:59 PM] Rtransat: Ok I get it (finally x))
[8:59 PM] acemarke: yay!
[8:59 PM] acemarke: so back to the last code sample I pasted before this
[8:59 PM] acemarke: mainReducer
will run first
[8:59 PM] acemarke: and do everything like normal
[8:59 PM] Rtransat: It was your value example, I was lost with : rootReducer(5, dontCare); // 14
[9:00 PM] Rtransat: the result 14
[9:00 PM] acemarke: but instead of that being the only work done, now we pass the mainReducer
result to doMoreThingsReducer
[9:00 PM] Rtransat:
const timesTwo = (state, action) => state * 2;
const addFive = (state, action) => state + 5;
const rootReducer = reduceReducers(timesTwo, addFive);
rootReducer(5, dontCare); // 15
15 is the result?
[9:01 PM] acemarke: yes
[9:02 PM] Rtransat: so the addFive take the value from the previous reducer function, right? π
[9:02 PM] Rtransat: Please tell me I understood x)
[9:02 PM] acemarke: yep
[9:02 PM] Rtransat: But in which case we need something like that?
[9:02 PM] acemarke: like I said earlier: when you have some logic that needs part of the work done first
[9:03 PM] acemarke: instead of the usual "I don't care about timing, just (state, action) => newState "
[9:03 PM] acemarke: if I have const mainReducer = combineReducers({a, b, c, d})
[9:03 PM] acemarke: the bReducer
doesn't care whether it's called first, last, or in the middle
[9:04 PM] acemarke: but in your example, you needed the list of amounts to be updated first, then read the amounts to calculate the total
[9:05 PM] Rtransat: @acemarke yes π
[9:06 PM] acemarke: here's one other possible way to write this
[9:07 PM] Rtransat: So with reduce reducer I will have my amounts value from the reducerA, now I know I have my value for calculate total, then I calculate total from previous reducer result(edited)
[9:07 PM] Rtransat: ?
[9:08 PM] acemarke:
function rootReducer(state, action) {
let newState = mainReducer(state, action);
if(action.type === "UPDATE_DEPARTMENTS") {
newState.departments.total = calculateTotal(newState.departments.departments);
}
return newState;
}
[9:08 PM] acemarke: and yes
[9:09 PM] Rtransat: I get it. Thank you @acemarke π
[9:09 PM] acemarke: sure
[9:10 PM] Rtransat: You're so much help π
[9:10 PM] acemarke: :)
[9:10 PM] Rtransat: I understood but at the end I will use the "derivate" thing x)
[9:11 PM] Rtransat: more simple for my case (I think)
[9:11 PM] acemarke: sure. and much of the time, that's the best approach
[9:11 PM] Rtransat: Yes π
[9:12 PM] Rtransat: reduce reducer is rather unusual in general?
[9:14 PM] acemarke: it's definitely not as well known as combineReducers, because combineReducers comes with Redux, and that's enough for most people
[9:15 PM] Rtransat: And like you told me, you can use the "derivate" approach rather than put the total value in the state