Skip to content

Instantly share code, notes, and snippets.

@markerikson
Last active September 1, 2016 19:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markerikson/b0cb7aeef04e54466fb348f1f1d8e080 to your computer and use it in GitHub Desktop.
Save markerikson/b0cb7aeef04e54466fb348f1f1d8e080 to your computer and use it in GitHub Desktop.
Redux docs discussion

[04:52 PM] acemarke: Question for y'all. I'm trying to define some semi-official-ish terminology for common types of functions that show up when writing reducer logic
The original reason for the term "reducer" is that it takes (state, action) and returns the new state, same as a callback to reduce()
Does it make sense to still use the word "reducer" when describing functions that are used inside of reducer logic, but do not have the exact (state, action) signature?
[04:57 PM] acemarke: Some examples might be:

  • a function that handles a "cross-slice" action, like const newSliceA = handleSomeCase(state.a, state.b)
  • A function that handles a specific pattern that occurs inside of multiple cases, and each case calls this function to do part of the work

[05:22 PM] totaldis: how is that a reduce?
[05:22 PM] acemarke: that's kinda what I'm asking
people generally throw around the phrase "reducers" to mean "any function at all that gets called somewhere inside that logic"
or "sub-reducers"
and I'm trying to figure out some more specific terms that could be used
[05:23 PM] totaldis: the less ambiguity the better
[05:24 PM] acemarke: if you look at https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/reducers/02-SplittingReducerLogic.md , I'd proposed a few distinctions
[05:24 PM] totaldis: I don't deal in 'sub-reducers' so I'm probably not the one to ask since I don't entirely understand the use case
[05:25 PM] acemarke: and I'm now debating some of what I first thought up
[05:26 PM] totaldis: when I hear reducer wrt to redux I think of the piece of middleware that listens to my actions
[05:26 PM] acemarke: ???
[05:26 PM] totaldis: I'm not sure overloading the term (even though it's already been done) helps for docs
[05:26 PM] acemarke: the issue is that the term is already overloaded in discussions
so I'm hoping to help clarify future discussions
[05:27 PM] totaldis: but you're introducing case reducer and slice reducer
it all sounds a bit over engineered
as to why I'm not the person to be responding, most of my shaping of data happens ahead of reaching reducers typically within a saga
[05:29 PM] acemarke: so you're basically just blindly merging stuff in, then?
[05:29 PM] totaldis: blindly?
[05:29 PM] acemarke: return {...previousState, ...action.payload} type of stuff?
[05:29 PM] totaldis: essentially
all my "subreducers" can be found in model.js for each module
which would be the 'case reducer' and 'slice reducer'
they're aware of the required state tree shape
they're just functions to me
I sympathize with your problem of managing the confusion in the umpteen discussions both here, medium, and gh
but when I hear 'reducer' I think of what's explained to me in the redux docs
[05:33 PM] acemarke: I... think that was the point of my original question?
[05:34 PM] totaldis: so from my standpoint, to answer your question, no
it doesn't make sense
[05:34 PM] acemarke: "reducer" was originally used to mean "stateless function with the signature (state, action) -> newState"
and has sorta drifted to mean "any function called anywhere in that sequence"
[05:35 PM] totaldis: akin to [].reduce(fn)
so is your goal to row back to the original meaning shoreline or to set anchor and embrace the jelly fish?
I couldn't think of a proper metaphor for new meaning
[05:37 PM] acemarke: sorta what I'm trying to figure out
per that page, I'm seeing several specific common patterns/types of functions that show up when you start splitting reducer logic into smaller pieces
I'm trying to come up with names to distinguish them
[05:38 PM] totaldis: the question is, do they need names?
[05:38 PM] slightlytyler: idk proably not
"helper functions"
specifically data manipulating functions
[05:38 PM] acemarke: I think there's some value in distinguishing them, yeah
even if only inside this chunk of docs I'm writing
[05:41 PM] naw: I would call the top-level reduce a rootReducer. I would use subReducer for any reducer that manages a single slice and is primarily intended to be consumed by combineReducers
I'm not quite sold on the case reducer or utility reducer terms
[05:41 PM] totaldis: what is a single slice though?
[05:41 PM] naw: @totaldis: Are you familiar with combineReducers?
[05:41 PM] acemarke: a single key's worth of state
[05:42 PM] totaldis: but that's n-levels
[05:42 PM] acemarke: sure
[05:42 PM] naw: a subReducer can be at any level
[05:42 PM] slightlytyler: yeah not just a single key, a single branch?
[05:42 PM] naw: it's fractal
[05:43 PM] acemarke: the function handling that slice could still have several levels of nesting inside of it, but it's still handling that one "slice" in its entirety
[05:43 PM] totaldis: and the level above that? and above that? eventually you'll find the rootReducer
so naming a nested reducer something else? just seems confusing
[05:44 PM] acemarke: you just lost me there
[05:44 PM] totaldis: me too a bit
it's the founders scotch style ale
[05:45 PM] naw: So one of the main points of redux is that there is just one state object, and there is just one reducer to manage that state object. That one reducer is the only thing that has to conform to the reducer method signature. However, there are patterns you can use to "build" that one reducer. One of the most common patterns, especially for newbies is combineReducers, where you break the management of state into individual slices, each managed by their own reducer
The term "slice" is used pretty commonly
[05:46 PM] acemarke: ^^^
(and is in the Redux docs already)
[05:46 PM] naw: It seems like it would be nice to have a name for the reducers that manage those slices.
I do think there is value in dintinguishing them from the main reducer
[05:46 PM] totaldis: aren't those just.... reducers?
[05:46 PM] slightlytyler: high order reducer?
[05:46 PM] acemarke: sigh
that's the point of my question
[05:47 PM] naw: high order reducer could be confused with higher-order reducer, which would be completely different
[05:47 PM] slightlytyler: what would be that difference
[05:47 PM] acemarke: now I'm REALLY lost
[05:47 PM] naw: a higher-order reducer would be a function that builds a reducer. like, a factory
[05:47 PM] totaldis: dear lord
[05:47 PM] naw: in the same way that a higher-order component is a function that builds a component
[05:47 PM] totaldis: I hope you aren't assigning different meanings based on a -
[05:48 PM] slightlytyler: mmm high order component is a component that returns a component?
[05:48 PM] naw: No, higher order component is a function
[05:48 PM] slightlytyler: could be a class
I'd agree that what you're describing is a factory
[05:48 PM] totaldis: it's all just functor
[05:49 PM] naw: I'm using the terminology from https://github.com/acdlite/recompose/blob/master/docs/API.md of course that's not official, I suppose
[05:49 PM] slightlytyler: even by my definition it wouldn't be "high order" as it's not a reducer => reducer
[05:49 PM] naw: when I hear "high order reducer", I think factory. Do you?
[05:49 PM] totaldis: or higher order functions more precisely
[05:50 PM] slightlytyler: idk I think component factory and HOC component are different
[05:50 PM] totaldis: certainly
[05:50 PM] acemarke: yeah, I think the term "higher order $X" is "a function that takes one $X, and returns a new $X wrapped around the input $X"
[05:50 PM] totaldis: yup
fn(fn, *arguments)
[05:51 PM] slightlytyler: naming is a bitch
[05:51 PM] nopestack: ↑ this
[05:52 PM] naw: I suppose I wouldn't have thought that higher order $X has to take an $X as one of its arguments , although it certainly could
My understanding could be off
[05:53 PM] slightlytyler: I think that's the difference between a HOC and a factory
what you just described
a factory doesn't take a component as an argument
or at least as it's lead argument
[05:53 PM] naw: Well, either way, a higher-order reducer, would not be a reducer, but a function, right?
[05:53 PM] slightlytyler: yeah I was wrong
[05:54 PM] totaldis: @acemarke what are utility reducers vs case reducers
utility a superset of case and slice ?
[05:55 PM] acemarke: so if you look at the WIP example I have of progressively splitting up reducer logic at https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/reducers/03-RefactoringReducersExample.md
this is how I would classify things
this would be a "utility function":

function updateObject(oldObject, newValues) {  
    // Copy fields from oldObject to a new empty object.  
    // Then copy fields from newValues to the new object, overwriting any existing fields.  
    // This gives us a new updated copy, without modifying the original  
    return Object.assign({}, oldObject, newValues);  
}  

[05:56 PM] slightlytyler: so like an immutability helper
[05:56 PM] acemarke: this would be a "case reducer":
[05:57 PM] slightlytyler: or utility
[05:57 PM] acemarke: ``1js

function addTodo(todosState, action) {  
    const newTodos = todosState.concat({  
        id: action.id,  
        text: action.text,  
        completed: false  
    });  
  
    return newTodos;  
}  

this would be a "slice reducer":

function todosReducer(todosState = [], action) {  
    switch(action.type) {  
        case 'ADD_TODO' : return addTodo(todosState, action);  
        case 'TOGGLE_TODO' : return toggleTodo(todosState, action);  
        case 'EDIT_TODO' : return toggleTodo(todosState, action);  
        default : return todosState;  
    }  
}  

I don't have any good examples of what I was envisioning as a "utility reducer" in that page
[05:58 PM] naw: Well, even wikipedia can't agree on what higher-order function means, so perhaps our differing thoughts is warranted: https://en.wikipedia.org/wiki/Talk:Higher-order_function
[05:58 PM] slightlytyler: lol we just make shit up as we go, let's be real
@acemarke seems reasonable
curious what a utility reducer would be...
[05:59 PM] totaldis: I defer to mathematics definition of hof
[05:59 PM] acemarke: I'm picturing it as a shared bit of business-y logic that could get called in multiple different cases to do part of the work
for example
[06:00 PM] slightlytyler: but still with the signature (state, action) => newState?
[06:00 PM] acemarke: my own app is geospatial related
no, and that was the crux of my original question
so example
my app involves editing waypoint-based geo polylines
I can edit lat/lon coordinates for a waypoint, delete waypoints, insert new waypoints before or after a given waypoint, etc
as part of that process, I have to track the current ordering
oh, and I can reorder waypoints as well
so many of the editing actions involve updating the current "order/index" field on several of these waypoints and their parents
now, I'm using Redux-ORM to manage my relational entities, and it handles the process of applying updates immutably, etc
same as if I was using spread operators or a library like dot-prop-immutable or React-Addons-Update
so I have this function in with the rest of the "case handlers" for editing waypoints:

// Updates the sequence ID for each leg in a QuerySet  
// Only queues up immutable commands, but technically non-pure  
function updateLegOrdering(legQuerySet) {  
    legQuerySet.withModels.forEach( (leg, index) => {  
        const sequenceID = index + 1;  
        leg.legSequenceID = sequenceID;  
    });  
}  

[06:04 PM] slightlytyler: that's a utility reducer?
[06:04 PM] acemarke: yeah
[06:04 PM] slightlytyler: I'd say just utility
[06:04 PM] acemarke: so, reusable logic, business-related
[06:04 PM] slightlytyler: if there is one thing I can agree on it's that a reducer has that specific signature
[06:05 PM] totaldis: got my vote
[06:05 PM] acemarke: and that's why I was asking that question in the first place
"functions with the signature (state, action)" vs "any function that gets called anywhere in that hierarchy"
so kinda starting over with the distinctions here:

  • (state, action), top level / passed to createStore: "root reducer"
  • (state, action), used with combineReducers or similar to handle one "slice" of state: "slice reducer"
  • may or may not have (state, action), used to handle one specific action: ???
  • probably does not have (state, action), reusable business logic called by many other functions: ???
  • probably does not have (state, action), reusable logic that is not business-related: ???
    [06:11 PM] totaldis: I can get behind slice reducer
    the others...
    I don't know that they all need their own names suffixed with reducer
    [06:12 PM] slightlytyler: I don't mind case reducer so much either
    additional language could be confusing... on the other these terms help illustrate how reducer composition works
    [06:13 PM] acemarke: which is generally the end goal here
    [06:13 PM] totaldis: to be clear here, this is the case reducer?
function addTodo(todosState, action) {  
    const newTodos = todosState.concat({  
        id: action.id,  
        text: action.text,  
        completed: false  
    });  
  
    return newTodos;  
}  

[06:13 PM] acemarke: yes
[06:14 PM] naw: Do you think case reducer always only handles a single action?
If so, perhaps action reducer would be a better term
[06:14 PM] totaldis: and you name it as such because, it's beaneath a case BLAH:
[06:14 PM] naw: If not, ....
[06:14 PM] totaldis: that's confusing
[06:14 PM] slightlytyler: single action or single type of action
[06:14 PM] totaldis: I think naw is on to something
[06:14 PM] acemarke: and note that in this example, the state arg isn't technically needed:

function setVisibilityFilter(visibilityState, action) {  
    // Technically, we don't even care about the previous state  
    return action.filter;  
}  

[06:14 PM] totaldis: I had to scroll back up and look at your wording because I thought you had it backwards
[06:15 PM] slightlytyler: still good to keep the signature though
[06:15 PM] acemarke: right, and if I was going with the lookup map approach, that consistency would be more relevant
here's another related example
[06:16 PM] slightlytyler: case reducer implies use of switch statements... action impies coupling...
[06:16 PM] acemarke: in my own app, I've got a couple different "relational entities" sections
one for "actual data", the other for "WIP data being edited"
I can select one entity at a time and start editing it
but that usually involves several other entities as well
like, if I start editing one of my waypoint-based routes, I could actually make changes to any of its children in the process
so the "edit item" action involves copying a tree of entities from my "original entities" section to my "WIP entities" section
so I have my standard combineReducers-based function for generally handling stuff by slice
but I also have another function that specifically handles those cross-slice actions
and my root reducer is actually const rootReducer = reduceReducers([combinedReducer, globalSliceReducer])
and that "global slice" reducer looks like:

// These actions require access to multiple keys of our state tree, rather than just  
// a single key's slice of state.  We define a new top-level reducer function that  
// specifically deals with these actions, and passes the appropriate slices of state  
// to  
export default function globalEditingReducer(state, action) {  
    switch(action.type) {  
        case EDIT_ITEM_START: {  
            const selection = editItemStart(state.selection, state.entities);  
  
            return {  
                ...state,  
                selection,  
            };  
        }  
        case EDIT_ITEM_APPLY: {  
            const entities = editItemApply(state.selection, state.entities);  
  
            return {  
                ...state,  
                entities  
            };  
        }  
        default :  
            return state;  
    }  
};  

[06:20 PM] slightlytyler: couldn't you just have multiple reducers respond to the same action?
[06:20 PM] acemarke: if I stuck a whole bunch of extra data in the action, yeah
which is a viable approach
not the one I took here
basically, at this particular point all the data I need is already in state, I just have to update it cross-slice
and I generally prefer to keep my actions relatively minimal
content-wise
[06:23 PM] naw: So what would you call globalEditingReducer?
[06:23 PM] acemarke: hmm. well, it's aimed at the top-level, and has the signature (state, action)
[06:23 PM] naw: Note: I think an important distinction between rootReducer and anything else, is that rootReducer is expected (indirectly) to know how to handle Every action type
[06:23 PM] acemarke: but in practice, actually gets wrapped up into one more function around it
[06:24 PM] slightlytyler: I'm guessing you omitted the part where globalEditingReducer calls the origin root reducer?
[06:25 PM] acemarke: yeah, I would agree that there's only one "root" reducer, and that's the literal function you passed to createStore
no, it's not
const rootReducer = reduceReducers(combinedReducer, globalEditingReducer);
[06:25 PM] slightlytyler: ah I see
[06:26 PM] naw: So one way to think about it (just braininstorming here) is....

  1. something API-compatible with combineReducers is a slice-reducer
  2. something API-compatible with reduceReducers is a _______________
  3. something API-compatible with a type map is a _______
    [06:27 PM] slightlytyler: 2. auxillary reducer?
    is 3. what we were calling case / action reducers?
    or something different?
    [06:28 PM] naw: if an action reducer is only for a single action type, then it's probably API compatible with a type map, so it would be a (3)
    [06:28 PM] slightlytyler: what about a reducer functionally the same as the addTodo examples thats used on various reducers
    addRecord or something
    [06:30 PM] naw: Whichever reudcers use it would be delegating to it via a type map (or a case statement) (or an if statement, etc). but it's still fundamentally a (3), I think?
    [06:30 PM] slightlytyler: pretty much, just not 1:1 with an action type
    [06:31 PM] naw: I think what you're saying is an action reducer is a subset of (3)?
    [06:31 PM] acemarke: heh. I've created a monster :)
    so I suppose there's two different aspects in play here: function signature and use case
    [06:32 PM] naw: The point is there are a few common patterns for building reducers --- there is the combineReducers factory, the reduceReducers factory, and various implemtnations of type maps
    And the "things" you pass to those factories each bear a slightly different "flavor"
    [06:33 PM] acemarke: I'd say that anything with the (state, action) signature is a "reducer"
    any function that returns a function with the (state, action) signature is a "higher order reducer"
    [06:34 PM] naw: or a reducer factory, if you want to avoid the ambiguity of "higher-order"
    [06:34 PM] acemarke: so that would cover combineReducers, reduceReducers, stuff like the Undo wrapper, etc
    [06:34 PM] naw: right, so there a lot of factories to make reducers... things like CRUD stuff, list management, etc.
    But those are all a different animal than the reducers themselves.
    I think we're all on the same page on that
    [06:35 PM] acemarke: yeah, all "higher order reducers" in some way
    so then the questions are "different use cases for functions with (state, action)", and "functions that do not have (state, action)"
    "root reducer" is whatever you passed to createStore
    sliceReducer is a function whose primary responsibility is updating one slice of state, whether you directly used combineReducers or called it yourself
    maybe we just stop there?
    [06:37 PM] naw: Just to clarify, it's not just an argument that takes (state, action), but it has to return new state which is intended to replace the state it received, whether that be all of state, or some slice in the tree
    [06:37 PM] acemarke: right
    [06:37 PM] naw: so (state, action) => slice is not A reducer
    Although it could be used as a helper/utility function by a reducer
    [06:38 PM] acemarke: you lost me on that a bit
    so glancing at this: http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
    they have this definition:
    [06:38 PM] naw: So you could imagine a helper function that takes all state, but his job is only to return a particular slice
    [06:38 PM] acemarke: So we'll say simply that a reducing fn has the shape:

(f result input) -> new-result
[06:39 PM] naw: Right, so if something takes (state, action) and doesn't return new-state (but instead returns a slice of new-state), then it's not a reducer
But it could be used as a helper function by a reducer
[06:40 PM] acemarke: okay, I think I follow
[06:40 PM] naw: I can give a concrete example, if that would help
[06:40 PM] acemarke: sure
[06:41 PM] naw: Not sure how to do multi-line snippets in discord
[06:41 PM] acemarke: triple-backticks, plus a language abbreviation, like ```js CODE HERE ```
[06:42 PM] naw:

rootReducer = (state, action) => {  
    return { ...state, notifications: notificationUtilityFunction(state, action) }  
}  

so notificationUtilityFunction needs to see all of state, but it only returns the notifications slice
I would say it is not a reducer
[06:44 PM] acemarke: yeah, this is definitely where it gets down to nitpicky semantics
[06:44 PM] naw: So one mark of a reducer is that you could chain them together
[06:44 PM] acemarke: good point
[06:44 PM] naw: so you could have two different Todos reducers, and chain them like this: newtodos = todosReducer1(todosReducer2(state, action), action)
in essence, you can reduce over them
[06:45 PM] acemarke: yeah. gotcha, agreed
[06:45 PM] naw: But you can't do that with notificationUtilityFunction Either way, I suppose this isn't really a major point for your overall objective
sorry for the tangent
[06:46 PM] acemarke: no, no, I think that's a valuable point
do we just sorta lump everything else into "utility functions" and "business functions" for simplicity?
[06:47 PM] naw: I tend to get lost with the "Business" vs. "not-business" terminology
[06:47 PM] acemarke: "stuff related to the app itself" vs "stuff not actually related to the app"
updateObject would be a completely generic function
notificationUtilityFunction would be business-y
[06:49 PM] naw: I suppose I wouldn't have thought the distinction between updateObject and notificationUtilityFunction is an important one to draw
Backing up --- the goal is to help people understand the different patterns for building reducers
[06:50 PM] acemarke: right
[06:51 PM] naw: So I suppose it's a question of granularity ---
or precision
which is subjective of course
I suppose what's hard to know is which particular distinctions will actually be helpful
[06:52 PM] acemarke: yeah
[06:53 PM] naw: So some reducers see all actions, while others only see some actions. Is that an important distinction
[06:53 PM] acemarke: I don't think so
[06:54 PM] naw: Some reducers are designed to be used in composition with other reducers, so they see the changes other reducers already made for the exact same action
like reduceReducers, does that for example, although just because you use reduceReducers, doesn't mean you really need that composition behavior
[06:55 PM] acemarke: right - in my case, I'm using it for the "multiple functions at the same level of handling" aspect, not the "sequential" aspect
[06:55 PM] naw: right
So, another way to approach your document might just be: Here's a particular problem you need to solve, and here are 3 ways to solve it, and then give some broad names to those patterns
Here's another common problem, and here are 2 ways to solve it
[06:58 PM] naw: So if someone has a problem , you can refer them to the document and say "you need something similar to example 5.1, which uses reduceReducers in conjuction with combineReducers"
over time, people might start calling that the "reducecombine-pattern"
[06:58 PM] acemarke: yeah, I'm certainly planning on having a number of use cases and examples, was just hoping to be able to name the types of functions that show up in those examples
[07:01 PM] acemarke: part of me feels like I'm kinda over my head trying to explain this stuff
I'm certainly no FP guru
[07:02 PM] naw: me either
[09:18 PM] Shados: it's also important to note how it's already only a set of problem that occures when you make certain design decisions (have a lot of logic in reducers). You can have all that logic in selectors that pull data in mapStateToProps, that the components will then pass to actions (keeping actions 1 liners) and then the reducers don't need to do this.

Or you can have the logic in thunks/sagas/epics (whatever your side effect models of choice calls them) to pick the data from the state and keep your reducers simple too.

So it basically needs to be prefaced with: "Given a design decision to dump most of your logic in reducers, here is ways to handle that logic"
(I don't mean its an invalid design decision: Elm does it that way, but it IS still a consequence of a specific design decision).
if you're, let say, using Sagas or redux-observable AND have "thick reducers" on top of it, you're seriously doing it wrong, for example.

[10:36 PM] Steven: ok so selectClass is not an action creator, it's a thunk action creator, on the basis that it returns a function with (dispatch, getState) as params, vs returning an object, and redux-thunk detects that and handles it "appropriately"
[10:36 PM] acemarke: yes
[10:37 PM] Steven: This is the inherent problem with refactoring copied code where you don't totally understand how it's working
[10:37 PM] acemarke: yup
you may want to go read Dan's SO posts on the reasoning for thunks and middleware
see https://github.com/markerikson/react-redux-links/blob/master/redux-side-effects.md
[10:38 PM] Steven: I'm pretty sure I've read them, but I'm taking in so much new information right now
I think things are getting lost in the mix
[10:38 PM] acemarke: seriously, go read those two SO posts
[10:39 PM] Steven: doing so now
in the quick here and now
in this specific case
i need to dispatch the SELECT_CLASS action myself inside of the thunk ac right
[10:40 PM] acemarke: yep
[10:40 PM] Steven:

export const selectClass = playerClass => {  
    return (dispatch, getState) => {  
        dispatch({  
            type: SELECT_CLASS,  
            playerClass  
        });  
        const state = getState().get('deckBuilder');  
        //if (!localCards) localCards = localStorage.getItem('cards') ? JSON.parse(localStorage.getItem('cards')) : {};  
        if (!state.hasIn(['cards', playerClass])) dispatch(fetchCards(playerClass));  
        else dispatch(receiveCards(state.getIn(['cards', playerClass]), playerClass));  
    }  
};  

[10:41 PM] acemarke: assuming you want that done first, yeah
[10:41 PM] Steven: right well the behavior is this
the button has a selected state based on the playerClass in redux state
but if i wait for the response from the server on that call
the button doesn't "select" until the data is loaded
so you click and nothing happens
so i need to update the playerClass first
but now i wonder why i'm returning a fetch

function fetchCards(playerClass) {  
    return (dispatch, getState) => {  
        const state = getState().get('deckBuilder');  
        if (!state.get('isFetching')) {  
            dispatch(requestCards());  
            return fetch(`https://omgvamp-hearthstone-v1.p.mashape.com/cards/classes/${playerClass}?collectible=1&locale=jaJP`, {  
                headers: {  
                    'X-Mashape-Key': 'A2zvrqgiQ4msh5cnvLkBgXgXryjep1PtUv4jsnQRYGwJejeHCl'  
                }  
            })  
                .then(response => response.json())  
                .then(json => {  
                    dispatch(receiveCards(parse(json), playerClass));  
                    //localStorage.setItem('cards', JSON.stringify(localCards));  
                });  
        }  
    }  
}  

[10:43 PM] acemarke: sure. just pointing out that behavior inside the thunk is standard synchronous execution flow, as is everything with Redux unless you have middleware altering things
[10:44 PM] Steven: i'm not doing anything with that returned fetch, unless there's something going on behind the scenes
that's pulled directly from the reddit example
[10:44 PM] acemarke: so that's what I was saying earlier. Perhaps you hypothetically might want to chain something else off that call somehow
since the thunk is returning that promise, it becomes the return value for the outer dispatch call
so returning a promise from inside a thunk is a reasonably common sight
[10:45 PM] Steven: AH HA!

  componentWillReceiveProps(nextProps) {  
    if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) {  
      const { dispatch, selectedSubreddit } = nextProps  
      dispatch(fetchPostsIfNeeded(selectedSubreddit))  
    }  
  }  

he's dispatching it in the component
[10:46 PM] acemarke: mmm... not in that sample
[10:46 PM] Steven: no what i'm saying is
he's returning the fetch
in the thunk action creator
and the reason is because he's dispatching it in the component there
in compnentWillReceiveProps
[10:47 PM] acemarke: sure, but in that snippet the returned promise is being ignored
[10:48 PM] Steven: yeah so why is he returning it
[10:48 PM] acemarke: why in the example? I don't know
[10:48 PM] Steven: well wait
the promise eventually resolves
[10:48 PM] acemarke: in general? because you might want to chain some async behavior in the component
[10:49 PM] Steven: and chaining async behavior is what thunks are used for
ok i'm going to read those articles now
thanks for pointing this out to me. i'm glad i was confused. now i can learn.
[10:49 PM] acemarke: sure
[10:51 PM] Steven: so you're saying there's no point in returning that fetch?
in the reddit code example
[10:51 PM] acemarke: based on the snippet you showed me, no
[10:51 PM] Steven: http://redux.js.org/docs/advanced/ExampleRedditAPI.html
[10:52 PM] acemarke: http://redux.js.org/docs/advanced/AsyncActions.html

  
    // The function called by the thunk middleware can return a value,  
    // that is passed on as the return value of the dispatch method.  
  
    // In this case, we return a promise to wait for.  
    // This is not required by thunk middleware, but it is convenient for us.  
  
    return fetch(`http://www.reddit.com/r/${subreddit}.json`)  

and it does demo this later on in that page:

store.dispatch(fetchPosts('reactjs')).then(() =>  
  console.log(store.getState())  
)  

[10:53 PM] Steven: i was just about to paste that
alright i've got some reading to do. clearly i missed this part of it.
[10:56 PM] acemarke: and doc suggestions and PRs are always welcome :)
[10:56 PM] Steven: i think it's more like i copied and pasted the "full code" expecting to grok it by reading the code
but skipping the explanations of how we got there
so it's my fault for not RTFM
I looked at the photo of my IKEA desk and thought I could just figure it out without the instructions
but there's magick behind the scenes that makes this code possible and without that context, it's hard to know.
[10:58 PM] acemarke: yeah. ultimately, I really do think most of the core concepts are themselves relatively simple, and even "magic"-less
they're just relatively different than what most people are used to
[10:59 PM] Steven: There's a lot similar with redux and robotlegs and I think I took that for granted, not realizing the differences
Almost bedtime for you. Thanks Mark.
[11:00 PM] acemarke: sure
[03:44 AM] Steven: I'm looking at https://github.com/acdlite/redux-actions
I don't see how you use it with immutable.js
handleActions assumes that the state is a plain Javascript object
[04:41 AM] ooflorent: @Steven don't use handleActions but create your own helper
[04:42 AM] Steven: I figured out that this works

export default handleActions({  
    [requestCards]: state => state.set('isFetching', true),  
    [receiveCards]: (state, action) => state  
            .merge({isFetching: false, playerClass: action.payload.playerClass})  
            .setIn(['cards', action.payload.playerClass], action.payload.cards),  
    [selectClass]: (state, action) => state.set('playerClass', action.payload.playerClass),  
    [fetchCards]: (state, action) => {  
        console.log(state, action);  
        return state;  
    }  
}, initialState);  

the example was a mutating array
[04:42 AM] ooflorent: k
[04:43 AM] Steven: now i'm trying to figure out how to hook up a redux-saga to this
fetchCards currently just logs
but I need it to be handled by a saga
so i might take it out of here b/c I think (think) that either redux-actions handles it OR redux-sagas handles it
but if you know otherwise, please let me know
I was doing this in a thunk

fetch(`https://omgvamp-hearthstone-v1.p.mashape.com/cards/classes/${playerClass}?collectible=1&locale=jaJP`, {  
    headers: {  
        'X-Mashape-Key': 'mykey'  
    }  
})  
    .then(response => response.json())  
    .then(json => {  
        dispatch(receiveCards(parse(json), playerClass));  
    });  

but now i'm fairly certain that final dispatch needs to be a yield put
[04:58 AM] Xeno: I'm gonna play with firebase, but i'm curious what the best approach would be
according to some boilerplates I've seen, firebase and redux are "trying to solve the same problem" so they dont recommend using redux
buuuut they're really not solving the same problem
right?
[05:14 AM] Steven: Absolutely nothing I can find on google about combining redux-actions and redux-sagas and ESPECIALLY nothing about how to get access to the dispatch function inside a redux-action action.
With redux-thunk, it's easy
Everything I've read about sagas is that you don't want to access the state inside a saga
But if you have to, you use a selector (I have selectors)
Basically, I want to know if remote data is already in the state, and if it isn't, then dispatch a fetch to go get it, and if it is, dispatch the receive action.
But this is proving to basically be impossible with redux-actions as-is.
Everything I've read says that thunk is meant to be replaced with sagas, not used together.
So in that case, the saga better support the same think thunk does and pass me the state.
But it doesn't
So I guess I have to use a selector
[05:26 AM] Steven: Actually, I guess I could use my selector in the redux actions
nope b/c I don't have a dispatch there
ugh but then I can't dispatch the action that will trigger the saga
this is a nightmare
[05:32 AM] Steven: there are ZERO articles about using these two libraries together, which leads me to believe they're not compatible
In fact, I would go so far as to say that redux-actions is code smell since it's basically injecting itself as middleware behind the scenes.
[05:36 AM] Steven: OR I'm doing it wrong (probably)
I guess I need to move any actions that require if this then that else other thing where they trigger dispatches to sagas exclusively
So redux-actions is strictly for use with either thunk in place OR don't use it for anything async
[05:56 AM] Steven: i'm so confused right now

  1. You cannot dispatch actions using redux-actions.
  2. You cannot access the state using redux-sagas.
  3. You can access the state using redux-actions.
  4. In order to use selectors, you have to pass the state.
  5. Sagas can't call a selector with the state because of 2.
  6. Redux-actions cannot dispatch an action to pass to redux-sagas.
    [05:58 AM] Alex Zherdev: > Everything I've read about sagas is that you don't want to access the state inside a saga
    could you refer me to things you've read on this?
    [05:58 AM] Steven: 7. What the fuck?
    There are many discussions in closed tickets that I have found
    But it seems that those people have been told they're wrong
    that said I'm still having this issue
    I cannot access the state in a saga
    [05:59 AM] Alex Zherdev: why would saga have a select effect then
    [05:59 AM] Steven: Ok so I use select
    [05:59 AM] Alex Zherdev: I'm sorry I'm not of help on redux-actions
    [05:59 AM] Steven: not my own selectorr
    [05:59 AM] Alex Zherdev: what do you mean, you just go yield select(mySelectorFunction)
    does that not work for you?
    [06:00 AM] Steven: yeah i just found that
    I swear I couldn't find ONE tutorial that explained this
    i googled so many things
    [06:00 AM] Alex Zherdev: I've struggled with that myself, took me a long while to realize that getState is a deprecated/removed API
    and that you needed to use select
    [06:02 AM] Steven: TypeError: (0 , _reduxSaga.select) is not a function
    [06:02 AM] Alex Zherdev: there's a lot of articles mentioning getState in sagas and it's not a thing anymore
    it's in redux-saga/effects
    [06:02 AM] Steven: In fact I'm getting an error in Webstorm saying
    import {select, takeLatest} from 'redux-saga';
    oh
    [06:02 AM] Alex Zherdev: redux-saga exports higher-level stuff like takeLatest, takeEvery, while primitive effects are in /effects
    [06:03 AM] Steven: something is wrong
utils.js:188 uncaught at rootSaga   
 at rootSaga   
 at fetchCards   
 TypeError: Cannot read property 'size' of undefined  
import {createSelector} from 'reselect';  
import {List} from 'immutable';  
  
const getCards = state => state.getIn(['deckBuilder', 'cards']);  
const getFetching = state => state.getIn(['deckBuilder', 'isFetching']);  
export const getPlayerClass = state => state.getIn(['deckBuilder', 'playerClass']);  
  
export const getClassCards = createSelector(  
    [getPlayerClass, getFetching, getCards],  
    (playerClass, isFetching, cards) => !isFetching && cards ? cards.get(playerClass) : List()  
);  
  
function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    let cards = yield select(getClassCards);  
    if (cards.size() === 0) {  
        yield put(requestCards, playerClass);  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards, cards, playerClass);  
}  

No matter what, getClassCards should return a List()
but I'm getting undefined
[06:05 AM] Alex Zherdev: you're using reselect, right? not really familiar with it
[06:05 AM] Steven: yeah
actually this selector takes no arguments
except the state
so
getClassCards(state)
let cards = yield select(getClassCards);
[06:07 AM] Alex Zherdev: that looks fine
[06:07 AM] Steven: Isn't that supposed to pass the state
[06:07 AM] Alex Zherdev: no
[06:07 AM] Steven: https://yelouafi.github.io/redux-saga/docs/api/index.html#selectselector-args
[06:07 AM] Alex Zherdev: in other words, yield select(state => state)
you just pass a function of state
[06:07 AM] Steven: This is right in the docs for sagas right after the select statement
[06:07 AM] Alex Zherdev: any additional arguments to the selector are tacked onto the argument list
[06:07 AM] Steven:

Preferably, a Saga should be autonomous and should not depend on the Store's state. This makes it easy to modify the state implementation without affecting the Saga code. A saga should preferably depend only on its own internal control state when possible. But sometimes, one could find it more convenient for a Saga to query the state instead of maintaining the needed data by itself (for example, when a Saga duplicates the logic of invoking some reducer to compute a state that was already computed by the Store).  

[06:08 AM] Alex Zherdev: i.e. yield select(selector, 1, 2)
[06:08 AM] Steven: sure but where does the store come from
isn't "select" sugar for that?
[06:08 AM] Alex Zherdev: well saga is a middleware so at the time of execution it is supposed to have access to the state
same as redux-thunk and everyone else
[06:09 AM] Steven: yeah according to this it should work
[06:09 AM] Alex Zherdev: I'd try to make it work without reselect
just to isolate the problem
[06:10 AM] Steven: yeah ok so
it would appear that the saga runs right away and never "starts over"

export const getClassCards = createSelector(  
    [getPlayerClass, getFetching, getCards],  
    (playerClass, isFetching, cards) => {  
        console.log('getClassCards has been called');  
        return !isFetching && cards ? cards.get(playerClass) : List()  
    }  
);  

that's my selector with a log in it
this is my generator

function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    console.log('about to call getClassCards');  
    let cards = yield select(getClassCards);  
    console.log('cards', cards);  
    if (cards.size() === 0) {  
        yield put(requestCards, playerClass);  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards, cards, playerClass);  
}  

this is the output

getClassCards has been called  
about to call getClassCards  
cards undefined  

[06:11 AM] Alex Zherdev: how is fetchCards called
[06:11 AM] Steven: in that order
from a button click

import {connect} from 'react-redux';  
import {fetchCards} from 'src/modules/deckBuilder';  
import {getPlayerClass} from 'src/selectors';  
import React from 'react';  
import styles from './index.scss';  
  
export const Component = props => (  
    <div className={`${styles.button} ${props.selected ? styles.selected : ''}`} onClick={() => props.handleClick(props.playerClass)}>  
        <span>{props.playerClass}</span>  
    </div>  
);  
  
Component.propTypes = {  
    playerClass: React.PropTypes.string.isRequired  
};  
  
export default connect(  
    (state, {playerClass}) => ({  
        playerClass,  
        selected: getPlayerClass(state) === playerClass  
    }),  
    {  
        handleClick: fetchCards  
    }  
)(Component)  

fetchCards being the action creator

export default function *rootSaga() {  
    yield [  
        watchFetchCards()  
    ]  
}  
  
function *watchFetchCards() {  
    yield* takeLatest(CARDS_FETCH, fetchCards);  
}  
  
function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    console.log('about to call getClassCards');  
    let cards = yield select(getClassCards);  
    console.log('cards', cards);  
    if (cards.size() === 0) {  
        yield put(requestCards, playerClass);  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards, cards, playerClass);  
}  

this is how I read to set this up
is it wrong?
should I do takeEvery?
[06:13 AM] Alex Zherdev: yes
[06:14 AM] Steven: makes no difference
[06:14 AM] Alex Zherdev: but that's a separate problem from the log order
[06:14 AM] Steven: it never calls getClassCards again
[06:14 AM] Alex Zherdev: because reselect memoizes results?
[06:14 AM] Steven: hmm
this worked just fine with thunk
multiple calls
and yeah it memoizes but
these props should be different this time around
hmm
[06:15 AM] Alex Zherdev: yeah I think according to the docs takeLatest also watches all the time like takeEvery
[06:16 AM] Steven: ok so just for a test
to "break" memoization
i tried to set the playerClass in the state first
like this

function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    yield put(requestCards, playerClass);  
    console.log('about to call getClassCards');  
    let cards = yield select(getClassCards);  
    console.log('cards', cards);  
    if (cards.size() === 0) {  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards, cards, playerClass);  
}  

that yield put requestCards
throws the following error
utils.js:188 uncaught at check put(channel, action): argument CARDS_REQUEST is not a valid channel (channel argument must have a put method)
[06:17 AM] Alex Zherdev: yup, yield put(requestCards(playerClass))
you need to actually call the creator
[06:19 AM] Steven: yeah ok
so that's fixed but
that yield in the if statement doens't seem to work
the yield call

function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    yield put(requestCards(playerClass));  
    console.log('about to call getClassCards');  
    let cards = yield select(getClassCards);  
    console.log('cards', cards.size);  
    if (cards.size === 0) {  
        console.log('cards is empty so load');  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards(cards, playerClass));  
}  

[06:21 AM] Alex Zherdev: what's loadCards?
another saga?
[06:21 AM] Steven:

about to call getClassCards  
cards 0  
cards is empty so load  
cards undefined  
getClassCards has been called  

one sec
nope just a fetch
does it need to be a generator?
the flow i'm trying for is

  1. Check if the cards for a playerClass have already loaded.
  2. If they haven't, load them, parse them, and then dispatch that they've been received.
  3. If they have, then dispatch that they've been received.
    [06:24 AM] Alex Zherdev: makes sense
    yeah, call should work for promise-returning functions
    hearthstone, nice 🙃
    [06:26 AM] Steven: well is it returning a promise at the end there?
    I think it is
    oh
    maybe it needs to be
    [06:26 AM] Alex Zherdev: nope, you're not returning
    [06:26 AM] Steven: return fetch?
    [06:26 AM] Alex Zherdev: return fetch
    [06:26 AM] Steven: ok
    halfway there
    now i loads
    [06:26 AM] Alex Zherdev: I'm still wondering why the getClassCards isn't logged when you select
    [06:27 AM] Steven: yeah so here's the problem
    it keeps thinking that cards is empty
    let me see if i can figure out why
    this all worked with thunks btw 😃
    [06:27 AM] Alex Zherdev: I'd still advise to bypass reselects just in case
    btw I'm doing a board game and redux+sagas is a godsend for game logic
    I'm still pretty new to all of this though
    [06:29 AM] Steven: ah
    isFetching isn't getting set back to false
    i wonder why
    oh
    i'm dumb
    or am i
    wait
    yeah
    i knew it
    race condition
    so
    the memoization is causing problems
    but it's because i combined two things together that shouldn't be
    I just have to figure out how to break it up again
    so it used to be that I would call

selectClass

And that would determine whether or not there needed to be a requestCards
But now I mixed it into one call "fetchCards"
and so it's not setting the class before I check to see if I should load
So I'm going to fix that right quick
[06:34 AM] devvmh: Hey @Steven I was working a few weeks ago on a somewhat-similar use of redux-saga, but I did a bit of a different approach. I put most of my logic in selector functions, and very little in the sagas. If you want to take a look the code is in a repo at https://github.com/uniqueway/redux-crud-store (saga stuff at https://github.com/uniqueway/redux-crud-store/blob/master/src/sagas.js - you'd need to dig around in a few files to see how it all fits together). Maybe you can get ideas... or maybe you'll have suggestions for my code haha
[06:34 AM] Steven: Thanks!
[06:34 AM] Alex Zherdev: npnp
[06:37 AM] Steven: fixed it!
Saga for fetch cards

function *fetchCards(action) {  
    const playerClass = action.payload.playerClass;  
    yield put (selectClass(playerClass));  
    let cards = yield select(getClassCards);  
    if (!cards || cards.size === 0) {  
        yield put(requestCards(playerClass));  
        cards = yield call(loadCards, playerClass);  
    }  
    yield put(receiveCards(cards, playerClass));  
}  

First I select the class
THEN I check
That's how it used to work
And I have to say, sagas made it super simple to add one more step into the flow
So I guess that's why it's so awesome
[06:39 AM] Alex Zherdev: 👍
[06:40 AM] Steven: Thanks for helping me through that Alex!
And thank you for the link @devvmh
[06:42 AM] Alex Zherdev: you're welcome, learning for me too
[06:42 AM] Steven: I mean, I'm not 100% sure this is the best architecture, but at least it's working now 😃
[06:42 AM] Alex Zherdev: haha feeling the same currently
[06:43 AM] Steven: I know how to make a saga that makes some synchronous calls AND some asynchronous ones.
That's a check in the win column.
And I also am using redux-actions
[06:44 AM] devvmh: i just want to not make the same decision that someone else has already made, y'know? but there's so much noise on the internet it's impossible to find a good consensus. so you kind of have to just go for it like you did
[06:44 AM] Steven: And once I get them down pat, I'll write a blog post to help people who want to use both because I found zero on this combination.
Yeha
Alright I'm out until tomorrrow
Peace!
[06:45 AM] Alex Zherdev: gn
[06:45 AM] Steven: I bookmarked devvmh's link so I can read it later
[06:49 AM] Alex Zherdev: @devvmh I haven't seen selectors being used like that
but that doesn't mean it's bad
[06:49 AM] devvmh: used like what?
[06:49 AM] Alex Zherdev: just a new thing to me
like you pass actions into it and then branch depending on what's in the action
[06:50 AM] devvmh: the major goal of this particular library was reducing the boilerplate
like we had files for 10 different models with the exact same code
[06:50 AM] Alex Zherdev: btw are you requiring the entire lodash?
[06:50 AM] devvmh: good question, i don't remember
import isEqual from 'lodash.isequal'
just isequal
[06:51 AM] Alex Zherdev: yeah
[06:53 AM] Alex Zherdev: so many things I haven't used yet, reselect, flow, immutable, arrgh
[06:54 AM] devvmh: this doesn't actually use reselect, just the concept. if more people used it there are a few things I would improve about the library (all in the README)
ah, i see what you mean about select now. That's actually a feature implemented by a guy on github named jesse hallett, to reduce boilerplate even further. there's a v3.1.0 that uses the original way with no branching https://github.com/uniqueway/redux-crud-store/releases/tag/v3.1.0
the select function is pretty brilliant though, it lets you cut even more code out
(and there's also no flow annotations in 3.1.0)
[06:58 AM] Alex Zherdev: nah, that was just a general comment that there's so little time to keep up with all of the things
[06:59 AM] devvmh: i know exactly how you feel. i felt that way for like 6 weeks straight. so much to learn at once
[07:04 AM] Xeno: https://www.youtube.com/watch?v=UHJq5NOtNG4
this seems to be flowing the right direction
after a few hours of researching and playing with getting firebase and redux to place nicely
[09:08 AM] Steven: What's fire base?
[09:10 AM] devtist: some kind of database
[09:12 AM] Steven: Once I figure out sagas well enough, I'm going to learn react-ui
For the decorator aspect of it
[09:38 AM] ghigt: Hi, maybe a noob question but I have a problem with perf on my redux app. In my page, I have a (long) list of blocks. Each block can be edited with several inputs and stufs. Problem is, the onChange of the input changes the block in the global store which triggers a render of all my blocks and this causes the input to be very (very) slow. I can't see how to solve correctely my problem with redux.
[09:44 AM] ghigt: I would like to know if it's possible to re-render only the component corresponds to the block that has changed
[10:00 AM] Alex Zherdev: I believe this looks like a textbook case for React's shouldComponentUpdate
so not really redux-specific
[10:18 AM] Steven: Unless you use redux connect()
This is why the whole react render down chain is such a bad idea
It falls apart with forms
Are you using Immutable.js?
If not, you need to in order to prevent parents from redrawing.
Otherwise they think their props have changed.
Or you have to put shouldComponentUpdate in every. single. component. All the way down.
Bucket brigade fail
Not sure if you've looked at redux form?
https://github.com/erikras/redux-form
[10:51 AM] ghigt: yes, we use immutable
we looked at redux-form, but we have our own validations so it's a bit hard to use it
[11:03 AM] Steven: You can probably add something to the state that says you're currently editing so every component should check for that in the state and shouldNotUpdate if so
[11:03 AM] jimbolla: @ghigt You can avoid re-renders on components that haven't changed by implementing shouldComponentUpdate. Assuming your components have their propTypes defined, an optimal way is to use recompose's onlyUpdateForPropTypes() https://github.com/acdlite/recompose/blob/master/docs/API.md#onlyupdateforproptypes
[11:03 AM] Steven: I hate that solution, but that's the way it works if you don't use connect() only within components that need access to state.
Yeah that works too 😃
[11:12 AM] jimbolla: @ghigt Do you have 1 component at the top that does connect() and passes all the state down to each block, or does each block do it's own connect() to pull its details?
[11:20 AM] utro: @ghigt you can your list items, and slice only neccessary part of state, like that js const mapStateToProps = (state,ownProps) =>({ item : state.items[ownProps.id] })
[11:22 AM] utro: @ghigt then in list component you may render only ids of items js const mapStateToProps = state =>({ ids : state.posts.itemsIds })
[11:29 AM] ghigt: @jimbolla yes, we have a component with connect() and inside it, we map() on the blocks
[11:35 AM] devtist: quick question; if I want to connect my container to a component, and all I want to pass on to the component is a dispatch action, meaning I only have a mapDispatchToProps and not a mapStateToProps
it seems that there is no signature for connect without mapStateToProps? seems weird that I am obliged to map some state to props?
[11:35 AM] Steven: But I still hate the entire notion of shouldComponentUpdate
I think that redux connect() fixed what was a major design flaw
@ghigt that's how "react" says you do it, but redux connect solves that bucket bridge problem
And it is a problem
As you can see
This whole notion of smart and dumb containers is outdated as of redux connect().
It's like iterating through an array to find the last item vs just having a key to look it up.
Except in this case, you have to manually add a function to every single component down the chain when the very last one is the only one updating.
"Don't render because my deeply nested child is the only one that is updating"
And yes, there are solutions like recompose to limit which props you update on
[11:42 AM] utro: @devtist just use connect(), it will pass dispatch
[11:42 AM] Steven: But the best solution is to use redux connect() and mapStateToProps only on the components that update.
connect() actually IS the "smart" container now. If your component doesn't have it, then it's a "dumb" component.
And won't ever update unless some connect()ed component above it changes a composed component's props.
[11:43 AM] utro: @devtist export default connect()(TodoApp)
[11:43 AM] Steven: Yup
[11:44 AM] ghigt: https://gist.github.com/ghigt/0deb97aa0cd8ffe5ddc1209fb7df82a7
this is from the Redux egghead tutorial
When a todo is checked, all todos are re-rendered
it's exactly my problem
[11:47 AM] devtist: I was using this format:
connect("normallyMapStateToProps", {updateActionCreator}(MyComponent);
[11:48 AM] ghigt: what I can't understand is why the FilterLink is only updated if he's changed. I would like to do it also for todos
[11:50 AM] utro: @devtist you just need dispatch() func in component?
@devtist there are examples of possibilities https://github.com/reactjs/react-redux/blob/master/docs/api.md#examples
[11:59 AM] acemarke: @ghigt : this is the one major downside of directly driving your entire form through Redux, and trying to dispatch an action on every input event
two thoughts:

  1. it looks like you're still using a single array to store your todos, rather than normalizing the data. In that case, yes, you absolutely will be causing all Todo components to re-render, because the parent list will have to re-render
  2. You can use local component state in your form UI to buffer input changes and only dispatch an action when done
    [12:01 PM] ghigt: @acemarke I store directly what I receive from my API
    [12:01 PM] acemarke: 3) You're probably over-optimizing too early if you're worrying about this for a TodoMVC demo
    well, you'll want to transform that data into a normalized structure before you put it into your store, then
    [12:02 PM] devtist: @acemarke but that's only if you have (deeply) nested data in your state as I understand
    [12:03 PM] acemarke: nope
    [12:03 PM] pke: how do I put an action into the store from within an eventhandler?
    [12:03 PM] acemarke: it exactly fixes this situation
    [12:03 PM] ghigt: @acemarke I just show it as an example on the todolist, my project is much bigger and that's why I see the problem
    [12:03 PM] devtist: I used redux-form
    [12:03 PM] acemarke: let me paste a link to a discussion I had with @Steven recently
    https://gist.github.com/markerikson/53735e4eb151bc228d6685eab00f5f85
    [12:04 PM] devtist: and currently not normalizing my data, because we have agreed that the data sent from the API will have a flat structure (aka is already "normalized")
    [12:05 PM] acemarke: also a link to several articles on Redux performance: https://github.com/markerikson/react-redux-links/blob/master/react-performance.md#redux-performance
    [12:05 PM] devtist: thanks, I'll have to read up on that
    [12:05 PM] acemarke: and finally, a form wrapper component I put together that demonstrates buffering input changes before dispatching an action: https://gist.github.com/markerikson/554cab15d83fd994dfab
    [12:05 PM] ghigt: I have to go, thanks for your help, I'll try it
    [12:07 PM] devtist: is it an option to work without this setup until you run into performance issues and tackle them then?
    or would that be hell?
    [12:09 PM] acemarke: well sure, I think it's almost always better to get something working until you run into problems
    [12:10 PM] devtist: I tried looking at normalization before, but I didn't get it to work, we're combining quite a lot of things: typescript, immutable.js, redux, ...
    which makes everything a tad more difficult to implement it seems
    [12:12 PM] acemarke: sure. yeah, adding TS and Immutable definitely complicate things
    I'm personally not a fan of Immutable.js
    [12:12 PM] devtist: yeah, I'm still not very sure if it is worth it
    [12:13 PM] acemarke: I think it adds more mental overhead than it helps with
    [12:13 PM] devtist: yes, I might have to re-discuss with the team after reading more about it
    thanks for the links etc.
    it's also a pain with third-party libs that want to keep their own stuff in the state-three
    always have to do modifications because the data has to be converted to immutable data and vice versa
    [12:16 PM] acemarke: right
    but yeah, overall normalization is a very key concept. It makes your update logic simpler, and it allows more targeted connected components, resulting in perf wins
    which is why it's one of the things I'm hoping to cover in the "Structuring Reducers" doc that I am finally about to start working on here in a bit
    [12:24 PM] devtist: cool, yes, but my idea about it was the following:
  • if you have deeply nested data, normalizer will make the structure flat and work with the IDs to reference objects
  • if you can already keep your data flat when placing it into the state (like when doing API calls, don't have the endpoint send nested objects but rather do multiple API calls for the different objects and place them in a flat state)

so that's why I thought normalizing didnt make much sense if we dont receive any deeply nested data, but as I understand from your explenation it also has to do with updates due to changes of the state that can be handled better/more performant
(but I think I'll wait until we run into perf issues before looking further into it, I already get the feeling that the application is so verbose with typescript + redux + immutable + react-intl + react-router-redux + redux-saga + redux-form...)
[12:26 PM] acemarke: ai-yi-yi
yeah
frankly, you've picked some relatively heavy-weight tooling, both conceptually and code-wise
they all have reasons for existence, obviously, but that's definitely non-trivial
[12:27 PM] devtist: I'm rather new to React but I kinde have the idea that Angular is a much more complete eco-system, with much more Google-supported frameworks, while React seems to have less third party stuff
yes :p the struggle is real
I'm glad you're saying that, so I know it's normal it takes some time to set it all up
thought it was maybe slow-learning on my side
[12:27 PM] acemarke: I feel like a lot of the "JS Fatigue" comes down to a combination of the "pick-and-choose" ecosystem plus people trying to do ridiculously complex things
"I want an isomorphic server-side rendered async-loaded app that dynamically......."
well, yes, that IS going to be more complex than showing some static HTML. Go figure!
[12:29 PM] devtist: yes, I can totally believe that
and anytime I want to implement a third-party lib it's like:

  1. cool they seems decent documented
  2. wait... have to check how I can use it with redux
  3. wait... have to work with redux
  4. wait... need typings for the typescript
    ... :p
    thanks for your info, I' going to call it a day before the JS fatigue kicks in
  5. should've been work with immutable of course
    oops
    [12:50 PM] Steven: Don't bother with Angular
    It's just older
    Angular is the JS answer to Flex
    And flex was adobe's awful answer to Java
    [12:55 PM] Steven: Honestly, I don't know if I need all of these extras like redux-sagas, redux-actions, react-ui, reselect, recompose, etc
    But I'm learning them all at least at a basic level so I understand the tools in the box and a better understanding of the box itself.
    And then I can decide what's appropriate for the project I am about to build against a Serverless backend.
    [01:16 PM] acemarke: Reselect is a borderline must. Recompose is maybe interesting if you look doing FP-style composition with Higher Order Components. Not familiar with React-UI. Redux-Actions is just a utility wrapper. Redux-Sagas aren't needed for most cases, but are a pretty powerful tool
    [01:44 PM] Steven: I'm doing reselect for sure. I'm not writing anything except pure stateless function components.
    I don't want to have anything to do with react state.
    I see shouldComponentUpdate as a relic of the time before redux.
    [01:46 PM] Cole: Is there a way to re-connect?
    [01:47 PM] Steven: Haha
    Are you making a react joke?
    [01:47 PM] Cole: No
    For example, my global state doesn't have a certain property until my component is mounted
    So when that property is updated, my component never updates
    [01:48 PM] Steven: Put that in the state
    And map state to props
    ignoreUpdateFlag(state) // selector
    I'm confused though
    [01:49 PM] Cole: Let me clarify
    [01:49 PM] Steven: You have a component that mounts and when it mounts you set a dirty flag so it won't update?
    [01:49 PM] Cole: I have multiple Feed components, each with their own unique ID
    They share the feed reducer from redux [0: [item1, item2], 1: [item3, item4]]
    The index (0, 1) isn't available until the component mounts
    I want to avoid using local state of the components
    Option B) I could store the items (item1, item2, item3, item4) in global state and store the references in local state but I don't know how to do that
    That being to dispatch the action (fetch Feed items) and get the response in the component
    If I could get the response from the redux action it would solve my problem entirely (ie: let item_ids = this.props.actions.fetchFeedItems();
    [02:00 PM] devtist: undefined "don't bother about angular", we'll have to wait and see because I think Angular 2, it's ecosystem and community will be huge
    it looks more like React than like Angular 1, it is component based, you can apply redux etc.
    more "professional" libraries than React, e.g. Ionic 2 developped with help/feedback of the team at google, same for angular material 2 etc.
    angular CLI
    [02:06 PM] Flippant: if my app has a single api call, how do i split up the data into separate places in the state tree?
    [02:06 PM] devtist: you could use normalizr if the data returned is nested data
    or, you could just dispatch multiple actions with parts of the data, they have their own reducers so they can be put where you want
    [02:08 PM] Flippant: interesting... so have one async action that calls mutiple sync actions in the callback?
    [02:09 PM] naw: A 3rd option is dispatch a single action like RECEIVED_API_DATA with a payload that contains the entire API response --- then a reducer for a given slice just pulls out the data it cares about from the entire API response.
    [02:09 PM] devtist: normally for api calls you would work with something like redux-thunk or redux-saga, you could indeed do an API call and after the async call, dispatch different actions (for as far as I know, I'm also pretty new to the React universe)
    [02:11 PM] Flippant: cool thanks... that seems like a simple solution
    [02:18 PM] Cole: Is there a way to get the response from a redux store dispatch inside of a react component, beyond mapStateToProps
    [02:20 PM] naw: by response, I assume you mean the new state, rather than the actual return value of the dispatch call?
    [02:20 PM] Cole: The action return value
    In this case a server API response
    [02:21 PM] naw: you want to get your hands on the raw response from your AJAX call?
    [02:21 PM] Cole: Yeah
    Hopefully while keeping the component itself pretty dumb
    [02:23 PM] Steven: mapDispatchToProps
    [02:23 PM] Cole: I'm using that but it only returns the Action object itself
    [02:24 PM] Steven: Those API calls are meant to be put into the state
    That's the whole point of redux, it's your model.
    So to speak
    The store is the one true source
    Everything feeds the store
    [02:24 PM] Cole: I may be asking the impossible so let me start over
    [02:24 PM] Steven: The store drives the view
    Redux is like the MC part of MVC
    [02:25 PM] Cole: I have multiple Feed React components. Each Feed has many Item components, with some overlap. Ideally, the Item props will be stored in my redux store, and hopefully I can store each of the unique Item id's in my Feed reducer, OR if not, then the state of Feed - How can I use redux and redux-saga to solve this problem?
    [02:26 PM] Steven: You need normalizr
    https://github.com/paularmstrong/normalizr
    Or, if you're feeling really fancy, Falcor.
    But Falcor is bleeding edge Netflix tech
    [02:26 PM] Cole: uh
    What if
    Redux's feed reducer doesn't know Feed component exists until it's mounted
    in Feed Im using a "constructFeed" action to tell my store that there's a new Feed component
    [02:27 PM] Steven: Redux shouldn't know anything about your view lauer
    As far as redux is concerned, there is no view.
    The view, on the other hand, connects to redux.
    [02:28 PM] Cole: Bummer
    I was hoping to keep my component as dumb as possible
    [02:28 PM] Steven: And says "I care about X, Y, Z. Let me know when they update."
    [02:29 PM] naw: I think you may be talking past each other.
    [02:29 PM] Steven: Yes that's what connect() is for.
    [02:29 PM] Cole: ^^
    [02:29 PM] Steven: By using mapStateToProps AND reselect
    [02:29 PM] naw: it's fine for redux store to hold a hash of feeds like this: { feeds: [ { itemIds: [1,2,3] }, { itemIds: [3,4,7] } }
    [02:30 PM] Steven: Your component won't know anything about the structure of the state or how it gets updated when it changes, just that it does.
    [02:30 PM] Cole: @naw - I've thought about that, and then "subscribing" to the feeds object all together
    Im wondering what's the performance impact of that
    Because then all feeds would rerender
    [02:30 PM] Steven: Depends on how many feeds there are
    [02:30 PM] Cole: true
    [02:31 PM] Steven: And how many items in each feed
    [02:31 PM] Cole: In practice it's likely to only be one, I'm just a man that likes to have options
    [02:31 PM] Steven: And whether you're using a "smart scroller" where you only render the visible rows
    I wouldn't worry about optimizing the performance of the render until it's a problem. The DOM is stupid fast.
    You can render 100 rows super fast.
    [02:32 PM] naw: So react-redux has pretty smart ability to determine when you need to rerender, as long as your mapStateToProps is defined correctly
    So performance is not likely to be an issue
    [02:32 PM] Steven: That's right
    [02:32 PM] naw: So suppose you had something like this: <Feed id={myFeedIt} />
    [02:33 PM] Steven: You're attempting to fix a bottleneck you don't have yet.
    [02:33 PM] Cole: @naw I've been designing around using
    [02:34 PM] naw: Then your mapStateToProps for Feed could look something like this: return { items: state.feeds[existingProps.id].items.map( (itemId) => state.items[itemId] ) }
    [02:34 PM] Cole: That would be the easy solution
    Would solve my problem immediately
    I'm being stupid about not wanting to use an id prop
    My heart's desire is to be able to use indiscriminately
    [02:35 PM] naw: Well you have to have some way to identify which feed the component is supposed to render for
    [02:35 PM] acemarke: fyi, I am finally starting to work on this: reduxjs/redux#1784
    [02:35 PM] Cole: Right now Im using defaultProps with id and I increment that with each constructor
    [02:36 PM] acemarke: feedback wanted as I go. (also means I'm not going to be answering questions in here for a while)
    @Cole : yeah, sounds like you're constraining yourself too much. Passing in some ID as an outside prop to the connected component is the right way to do it
    @naw is dead-on
    and with that, outta here to focus
    [02:38 PM] naw: @acemarke I'd be happy to chat about the structuring stuff if there's anything specific that would be helpful to discuss
    [02:39 PM] Cole: okay this is a lot of good food for thought, thank you undefined, @naw & @acemarke
    [02:39 PM] naw: @Cole Where are you incrementing the id?
    [02:39 PM] acemarke: biggest issues atm are just trying to actually write something, and also figure out how to coordinate all these different related topics
    [02:39 PM] Cole: @naw Feed defaultProps
    static defaultProps = { index,
    [02:39 PM] acemarke: pushing my WIP updates to https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md
    [02:39 PM] Cole: and then with each Feed component I increment the index in defaultprops
    so everytime it's constructed, the defaultProps' id is different
    [02:42 PM] naw: @Cole Are you iterating over some kind of feeds array to render multiple feed components?
    [02:45 PM] Cole: No iteration, the Feeds are explicit but there's no unique identifying UID
    [02:45 PM] naw: So do you have <Feed/> hardcoded for the number of feeds you have?
    Like <Feed/><Feed/><Feed/> for 3 feeds?
    [02:46 PM] Cole: I have
    where apiURL would return independent result sets
    [02:48 PM] devtist: @acemarke nice! I agree that the documentation/examples concerning reducers could be improved, for me it feels/felt like magic while it really isn't
    [02:49 PM] Cole: Essentially I could do an unlimited number of some with the same props
    All they would share is state.items but have unique state.feeds[]
    Maybe I could mutate state.feeds from mapStateToProps
    [02:50 PM] naw: @Cole Yes I understand better now. give me a minute
    [02:50 PM] Cole: 💓
    [02:55 PM] naw: If you want the redux store to hold the array of itemIds for any given feed, then there must be a way for any arbitrary instance of the Feed component to know where to find its itemIds inside the store
    [02:55 PM] Cole: yes
    [02:55 PM] naw: For your scenario, the id can be completely arbitrary
    but there still must be some kind of id (or index)
    [02:56 PM] Cole: yes, id aka index is solely for mapping to the store state
    [02:56 PM] naw: Would you agree so far?
    [02:56 PM] Cole: It means nothing to the component or parent itself
    [02:57 PM] naw: Typically in cases like this, I would generate a random uid() when the component is mounted, and pass that into the fetch actions so that redux knows where to store the results, and so your component knows where to find the results
    [02:58 PM] Cole: The only problem with that is that the component never re-renders
    Because when connect is fired, the store state is not prepared
    So I imagine I either need to install a default unused "feed" state or mutate the store within mapStateToProps
    [02:59 PM] naw: I'm not following your last train of comments
    [02:59 PM] devtist: @naw while on this topic, does this sounds like a decent/good practice:
    --> have a user object in the state that has an array with ID's, for example articles
    --> an other part in the store keeps articles, which have an uuid field
    getting the articles for the user by looping over the articles and check where one of the uuid's matches an ID from the articles id list of the user
    [03:01 PM] naw: @devtist: Yes. So you would have something like this: { user: { articleIds: [uuid1, uuid2, uuid3] }, articles: { uuid1: articleObject1, uuid2: articleObject2, uuid3: articleObject3 } }
    @Cole: So your <Feed> component is connected. It has a mapStateToProps that just gets the correct item objects from the store based on an array of ids that are stored for a particular feed object that is also in the store. The feed object needs to be referenced by an (arbitrary) id, which any particular instance of the <feed> component would have generated for itself
    [03:04 PM] Cole: @naw I've done like you said by generating a random uid() when the component mounts, and I tell the store "hey I have a new feed with this id" but this is post connect() and therefore the component never updates because the state <==> props mapping was called before this
    [03:04 PM] devtist: @Cole it currently is more like this:
    { user: { articleIds: [uuid1, uuid2, uuid3] }, articles: { 0: articleObject1{uuid: 13124,...}, 1: articleObject2{uuid: 4525,...}, 3: articleObject3{uuid: 98098,... } }
    as it seems that reducers just use incrementing counter for keys (?)
    is there a way to decide what the keys have to be without any third party things like normalizr?
    @Cole so you don't update the state, so there is no re-render as I understand?
    [03:06 PM] Cole: { articles: { article1: { id: "article1", title: "Article 1" }, { article2: { id: "article2", title: "Article 2" }}, "feeds" : { "feed1" : ["article1", "article2"], "feed2" : ["article1"] } }
    [03:07 PM] devtist: (sorry for throwing my question up in between your discussion)
    [03:07 PM] naw: @Cole I think what you're saying is mapStateToProps doesn't know what the uid is?
    [03:08 PM] Cole: @devtist I think our problems lie on the same line of thinking
    @naw mapStateToProps doesn't have state.feed[uid] so any state updates aren't observed
    [03:08 PM] naw: @Cole right, yes, I agree that's a problem. There are two solutions:
  1. the uid() needs to be generated and passed as a prop, so that mapStateToProps can get its hands on it as the 2nd argument existingProps).
    [03:10 PM] Cole: I have it in ownProps but state needs to be mutated during or before mapStateToProps in order for it to observe
    [03:11 PM] naw: How are you getting the uid() into ownProps?
    [03:11 PM] Cole: defaultProps
    After each instance of Feed I increment defaultProps.id
    The only way I can think of mitigating this is to install a placeholder feed in state
    and to construct a new placeholder each time it's incremented
    so that mapStateToProps can observe the placeholder
    [03:14 PM] naw: mapStateToProps is called every time anything in the redux store changes. So if the store does not yet have any data for your particular id, then mapStateToProps should return an empty hash, and your component should handle that gracefully. Once data is added for that particular id, the store updates, so mapStateToProps is called again, which means it can return actual data, which means your component will re-render
    [03:14 PM] Cole: aha
    Okay
    Im only seeing one mapStateToProps call
    when it's constructed
    when I update the state, for some reason mapStateToProps is not called again
    [03:15 PM] naw: so to be clear ----react_redux has some optimizations
    sometimes it is only called once
    If it thinks that the result will never change
    So what is your mapStateToProps function actually look like? is it referencing the state ?
    [03:15 PM] Cole: That wherein lies my problem
    [03:16 PM] naw: @devtist You have a reducer that is placing data into the articles Array, right? Well, the articles array could be a hash instead of an array --- it's your choice
    [03:17 PM] devtist: okay i see
    [03:17 PM] naw: @devtist but it doesn't really matter if it is an array or a hash -- you can use either ---- just when you're pulling data out of the store by ID, it's easier if it is a hash --- otherwise you have to use Array.find
    [03:17 PM] Cole: @naw http://pastebin.ubuntu.com/18467882/
    zine is our word for "Article"
    Because return {} is not mapped explictly to state, I dont think mapStateToProps is called again
    And the state is for sure updating, I see it
    [03:20 PM] naw: @Cole: And you're only seeing the console.log once for each feed?
    [03:20 PM] Cole: yes
    the first time
    [03:20 PM] devtist: @naw, that makes sense, I will check tomorrow to see what happens if I use a hash
    thanks
    [03:25 PM] acemarke: quick question: how many people actually read the existing "Reducers" page in the docs? http://redux.js.org/docs/basics/Reducers.html
    [03:26 PM] Cole: I read it, but Ill admit I was confused about combineReducers because of syntax such as this import * as reducers from './reducers'
    What's out of sight is out of mind
    [03:26 PM] acemarke: that's just ES6
    [03:26 PM] Cole: Right, but you don't know what "./reducers" entails until you see it
    [03:26 PM] acemarke: well, you know it's doing a bunch of named exports, probably of functions
    and you're requesting all of them together in one object
    [03:27 PM] Cole: But it doesn't show what the exports are until later in the article
    [03:27 PM] acemarke: anyway, I just re-read the page myself, and realized I'm basically duplicating half of what it says so far
    it talks about the core requirements for reducers, splitting up logic / "reducer composition", etc
    I feel like either people aren't actually reading that page, or are struggling applying those basic principles to real-world code
    [03:28 PM] Cole: I experienced a combination of both
    You have to read the whole page before you understand the code excerpts from the top of the page
    It's not linear
    [03:28 PM] acemarke: (I also feel like half the confusion about Redux is people not understanding ES6 yet...)
    [03:28 PM] Cole: mmm that could be part of it
    I understood ES6 and still had to read through twice
    I found myself googling combinereducers and going for outside sources
    [03:31 PM] naw: @Cole: To be clear --- do you see the console.log once for each feed, or once altogether?
    [03:31 PM] Cole: @naw I put an alert() instead of console.log and only saw it once per pageload
    No alert() after the state update
    [03:32 PM] naw: @Cole What code are you using to pass mapStateToProps into your component? (e.g. what does your code with connect look like?)
    [03:32 PM] Cole: @connect(stateToProps, dispatchToProps)
    ES7 decorator
    [03:33 PM] naw: Do you have more than one Feed on the page?
    [03:33 PM] Cole: not yet
    One feed isn't responding to the state change
    [03:35 PM] naw: So to reiterate, mapStateToProps should be called every time the state changes. So from a troubleshooting standpoint, we need to determine whether or not that's actually happening, and if not, why not. The overall strategy you're using as well as the stateToProps function seem fine
    [03:35 PM] Cole: right
    It's not happening at this point
    I should clarify that my Feed component extends a shouldComponentUpdate HOC
    let me try removing that extension
    [03:38 PM] Cole: Nope, even with an extensionless component it's not called more than once
    [03:39 PM] naw: if it were me, I would add a debugger statement inside your local copy of react_redux to prove/disprove whether it's optimizing or not
    [03:40 PM] Cole: Let me find where mapStateToProps is called
    [03:40 PM] naw: To be clear, this is what I'm talking about: https://github.com/reactjs/react-redux/blob/master/src/components/connect.js#L118
    I would put a debugger statement immediately below that and check whether it's true or false
    [03:45 PM] Cole: doing that now
    [03:47 PM] naw: Another thing i would try is having 2 feeds --- to ensure that stateToProps is called twice (i.e. once for each instance, rather than once for the whole page).
    Otherwise, you could imagine a small mistake like this: connect(mapStateToProps(), mapDispatchToProps)(component)
    [03:48 PM] Cole: Ill verify that but I'm not executing it
    [03:48 PM] naw: Personally I don't know how ES7 decorators work
    [03:48 PM] Cole: It transpiles to connect(a, b)(class);
    ok I've verified that connect(); syntax is not the prob
    ok so
    this. doStatePropsDependOnOwnProps = true
    When my state changes
    it's called after mapStateToProps ends
    [03:52 PM] naw: Ok that's good. Now you might check https://github.com/reactjs/react-redux/blob/master/src/components/connect.js#L239 and see if that's being called when the state changes.... then you can just step through the logic and see whether or not it's calling what it's supposed to be calling
    [03:53 PM] Cole: ok sec
    [03:54 PM] naw: to be clear, we want to know if this.updateStatePropsIfNeeded is being called (which will indirectly call mapStateToProps): https://github.com/reactjs/react-redux/blob/master/src/components/connect.js#L251
    [03:55 PM] Cole: Im adding alerts to the return statements as well
    ok it's not called because....
    https://github.com/reactjs/react-redux/blob/master/src/components/connect.js#L246
    It thinks the store state hasn't changed?
    [03:59 PM] naw: Just to cover all bases, how do you know the state has changed?
    [03:59 PM] Cole: Im using redux-saga with a logger saga that console.log's all actions and tells me the state after
    [03:59 PM] naw: Are you sure your reducers are managing state in an immutable way?
    [03:59 PM] Cole: I inspected prevStoreState and the state during the change and for some reason it's the same
    [04:00 PM] naw: It sounds like you might be writing on top of the old state, rather than returning a new object for the old state. Do you know what I mean?
    [04:00 PM] Cole: I believe so, take a look at this
    http://paste.ubuntu.com/18470288/
    I think the problem might be
    that I need to do Object.assign({},
    IE: create a new state object like you said
    [04:02 PM] naw: Line 5 of your paste is the problem
    [04:02 PM] Cole: Im mutating the old state?
    [04:02 PM] naw: yes
    [04:02 PM] Cole: Woohoo!
    Also bummer on my part
    I missed that part of the documentation
    Let me take care of that
    [04:03 PM] naw: Do you mean it's not clear from the docs that the reducer needs to be immutable, or is it not clear that a statement such as line 5 is mutating the state?
    [04:03 PM] Cole: the former, from what I took away from the documentation example - you mutate the state object that is passed to the reducer
    hallelujah, mapStateToChange is called twice
    [04:06 PM] naw: So, just for clarity, it's definitely not OK to mutate a reducer's arguments. Perhaps there is an example somewhere that makes that unclear?
    [04:07 PM] Cole: There's a lot of information in the documentation, and all the information is displayed in a manner of equal value
    TBH I would highlight this doc:
Things you should never do inside a reducer:  
  
Mutate its arguments;  
Perform side effects like API calls and routing transitions;  
Call non-pure functions, e.g. Date.now() or Math.random().

Give it a border, a yellow background, make the font size bigger
it looks like standard text and tbh I skipped over it
[04:08 PM] naw: yeah that makes sense ---- that's helpful feedback
[04:09 PM] Cole: I'm sorry to have wasted your time with my lack of understanding
[04:09 PM] naw: I'm not sure if that's a common pitfall or not, but I can't imagine you're the first
[04:09 PM] Cole: one last question for you @naw you got a paypal or bitcoin address?
No is not an answer 😛
[04:09 PM] naw: LOL, really that's not necessary
[04:09 PM] Cole: don't be bashful you really helped me
[04:12 PM] Cole: Do you have a paypal at your @gladtocode.com address?
[04:15 PM] Cole: thanks again @naw!
[04:15 PM] naw: you're welcome
[04:27 PM] naw: @acemarke it seems the existing reducers page already does a pretty good job of developing the concept of combineReducers. So, it's as if people forget that you can go back do first principles rather than always being stuck with combineReducers
[05:21 PM] acemarke: yeah, I know
was just complaining about that on Twitter
just pushed up another WIP page update
so far almost all I've done is reiterate what's in the Reducers page and Dan's tutorials
[05:24 PM] acemarke: also, on the mutation thing: seems like that itself falls into a few categories. People who don't realize that's a requirement; people who don't know what "mutating" means or how to do "immutable" updates; and people who think that saying var x = state.y actually makes a copy and lets them mutate X all they want
[05:26 PM] Cole: right
Would be helpful to demonstrate that
This might be a dumb question, but could it be possible to pass the reducers an immutable copy of the object with setters that throw errors?
[05:27 PM] acemarke: there's a number of devtool addons that do just that
https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md
[05:28 PM] Cole: I mean from a framework standpoint
[05:28 PM] acemarke: anyway, I've cranked out a good chunk of writing in the last few hours, but I haven't even gotten up to what I really want to cover because I sorta feel like I have to start from ground zero
that's not Redux's job
Redux doesn't know or really care what your actual data is
could be plain objects, could be Immutable.js Maps, could be something else
and Redux aims to keep as minimal a core as possible
[05:29 PM] Cole: In what scenario would mutating the previous store state from a reducer be acceptable?
[05:30 PM] acemarke: in very rare circumstance, it hypothetically maybe could be useful if you're trying to do things faster (less object creation, less garbage collection, etc)
however, React Redux's connect() assumes that shallow-equality of mapState results means "don't need to re-render"
but the point is that it's not core Redux's job to do that check
[05:31 PM] Cole: What about a development notice to console.log?
[05:32 PM] acemarke: not in the ore
that's why there's those addons I linked to
all core Redux really does is manage the dispatch process and the subscriber notifications
anything beyond that is up to you
[05:33 PM] Cole: sure
that's just one noob's observation of my own missed understanding of the immutability of the previous store state
[05:34 PM] naw: What's interesting is the immutability isn't really a requirement of redux per se, but rather a requirement of other addons/bindings (like react-redux, time travel, etc.)
[05:34 PM] acemarke: yup
[05:34 PM] Cole: Yeah, sorry I may be preaching to the wrong choir
[05:35 PM] acemarke: SOOOO much of what people complain about really falls out naturally from the basic concepts of "time travel debugging" and "basic software engineering"
time travel -> serializability -> plain JS actions and state -> string constants -> deduplication -> myConstants.js
[05:35 PM] naw: Not really trying to disagree with you @Cole --- seems like if a tool needs immutability to function properly, it would be nice if that same tool could enforce it somehow
[05:36 PM] Cole: yes
[05:36 PM] naw: redux _doesn't need immutability to function properly, but react-redux, does
[05:36 PM] acemarke: again, bit of a distinction between "how Redux actually works", and "How Redux is usually used"
[05:36 PM] Cole: For example the development version of react will throw invariant violations
[05:36 PM] acemarke: yup
also note that React's development version is like 600KB...
[05:37 PM] Cole: mhmm
[05:38 PM] acemarke: awright, that just encouraged me to add a "basic concepts of immutable updatse" section to this doc
[05:38 PM] Cole: In the very least, I would suggest some highlight of the docs that deal with mutation
for numbskulls like me 😛
[05:38 PM] naw: I think that's good feedback
[05:38 PM] Cole: maybe a troubleshooting part too?
"Is your mapStateToProps connect() call not firing more than once?"
Stop me if this is the wrong channel, I didn't see a #react-redux
[05:39 PM] naw: Yeah redux is probably used with react-redux most of the time
[05:39 PM] acemarke: totally the right channel
that said...
the docs already have http://redux.js.org/docs/Troubleshooting.html and http://redux.js.org/docs/FAQ.html#react-not-rerendering
[05:40 PM] Cole: Egg on my face 😛
[05:40 PM] acemarke: not picking on you personally, but this is sort of highlighting what I was whining about earlier
[05:40 PM] Cole: It's true tho, I completely missed it
I generally search google like "redux mapStateToProps not being called"
[05:40 PM] acemarke: while the examples in the docs are obviously basic (yay, MORE TodoMVC snippets!), there is so much that's broadly applicable
then again, Dan was asking on Twitter the other day why so many people ignore the React docs too
[05:42 PM] Cole: I think React needs to be it's own section
Instead of a subsection of basics
[05:42 PM] acemarke: no, as in, React's actual docs
[05:42 PM] Cole: oh
[05:43 PM] acemarke: like, people go jump into tutorials about "isomorphic server-rendered blah blah", rather than actually reading the React docs
[05:43 PM] Cole: Im one of those people
I learn by doing
Which often leads to mistakes
and me wasting your kind people's time
[05:43 PM] acemarke: don't get me wrong, I'm totally a hands-on learner too
but I also try to dig up as much info on a topic first
so that by the time I dive in, I've got a decent idea what's going on
[05:45 PM] Cole: mmmm can't speak for the rest of us noobs but some of the react & redux docs are highly technical
Or the gist' might skip over what mutability means
For example the react docs
like 40 pages of mixed technical explanations
Not making an excuse but who has time for that?
So I tend to start with the basics, look at demo code, get a sense of how it functions
And pickup the other doc pages along the way
Would say that real world examples, like that Todo list or Timer - have worked wonders for me
[05:49 PM] Cole: Ill stop talking now tho before I dig my grave any deeper 😛 Thanks again @acemarke and @naw
[05:49 PM] acemarke: sure
would actually be interested in your feedback on https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md (along with anyone else's)
early early WIP, just what I've typed up so far this afternoon
[05:51 PM] Cole: hmm
I had to read over the first paragraph three times
[05:54 PM] Shados: that write up could really benefit from object/array spread, and splitting the todos between a map of todo data and an array of todo for ordering.
else its overly complicated.
[05:54 PM] acemarke: that was deliberate
I haven't hit normalization yet, and I'm trying to show deliberately dense-ish code
(I'm also just now adding a section on immutable updates in there)
[05:55 PM] Shados: Ive read a lot of studies explaining how, if you show "the wrong way" of doing something to people, unless you very very quickly show "the rigth way", people remember the wrong way more than the right way.
take with a grain of salt, but it seems to be a lot of "the wrong way" to go through if the right way only comes later
as opposed to, eg: the egghead videos, that get to the "right way" very quickly
(or if not quickly, with very little code in between)
[05:56 PM] Cole: yeah I usually see 1. Good Example. 2. Good Example. 3. Bad Example
[05:56 PM] acemarke: that chunk was partially inspired by this: https://github.com/kjbekkelund/writings/blob/master/published/understanding-backbone.md
(this is also literally first draft stuff so far)
[05:57 PM] Cole: However, there are a number of common patterns that are widely used when writing reducers, as well as a number of related topics and concepts to be aware of. As an application increases in complexity, many of these patterns play a crucial role in managing reducer code complexity, handling real-world data, and optimizing UI performance.
This doesn't feel so much as an introduction
[05:58 PM] Shados:

function toggleTodo(state, action) {  
    const newTodos = state.todos.map(todo => {  
        if (todo.id !== action.id) {  
          return todo;  
        }  
  
        return updateObject(todo, {completed : !todo.completed});  
    });  
  
    return updateObject(state, {todos : newTodos});  
}  

if I ever saw that in redux documentation I'd switch to Angular 2 before you have time to say "Flux"
😉
(if I was a newbie)
[05:59 PM] acemarke: I started with code from the Reducers page, and the transcribed examples from Dan's videos

    case 'TOGGLE_TODO':  
      return state.map(todo => {  
        if (todo.id !== action.id) {  
          return todo;  
        } else {  
// for the todo that matches the action id return all other information the same  
// but change the completed property to the opposite of what it was previously  
          return {  
            ...todo,  
            completed: !todo.completed  
          };  
        }  
      });  

but moved up to be part of the root state
[06:00 PM] Shados: yeah, at least its not extracting sub methods, and its using spreads
but it is pretty dense even then.
[06:01 PM] acemarke: again, that's sort of the point - I just described three types of functions you could extract, and I'm trying to illustrate them
[06:02 PM] Shados: yeah, i think its that kind of thing that make people run away to MobX
just my 2 cents.
[06:02 PM] acemarke: alternative suggestions?
just show the final result?
[06:02 PM] Shados: or get to it very quickly
and use object spreads and array spreads gallore whenever it makes thinks thiner
[06:03 PM] Cole: your entire application really only has one single reducer function is this statement still true when you use combineReducers?
[06:03 PM] Shados: @Cole yeah
[06:03 PM] Cole: on a technical level - yes - but on a conceptual level?
[06:03 PM] acemarke: this also goes with one of my other points: I think a large amount of confusion with Redux is people not knowing ES6. Or, in the case of object spreads, Stage 2
yup
one function. you pass one function to createStore
what goes on inside that function isn't Redux's business
[06:04 PM] Shados: fair. Then you just start with showing Object.assign, then quickly explain spreads
then go back to spreads.
[06:04 PM] acemarke: yeah, I was just starting to insert a section before that code regarding immutable updates
[06:04 PM] Shados: Functional programming in ES5 is literally non-viable.
[06:04 PM] Cole: combineReducers is from redux tho
[06:04 PM] Shados: unless you use Ramda or something
[06:05 PM] Cole: I think that's valuable to know within that section
[06:05 PM] Shados: combineReducers is pretty much just sugar.
[06:05 PM] acemarke: @Cole : what I mean is that Redux does not know or care what internal work goes on inside your reudcer
that's a huge part of what I'm trying to explain in this doc
it just calls the one function and gets back a result
[06:06 PM] naw: Exactly --- there is a difference between what's happening conceptually and what's happening technically, and forgetting that limits users' abilities to solve problems technically that deviate from the common conceptions.
[06:06 PM] Shados:

function myReducer(state, action) {  
   return {  
          stateA: reducerA(state, action),  
          stateB: reducerB(state, action),  
          stateC: reducerC(state, action)  
   };  
}  

you end up with 1 reducer function in the end.
[06:06 PM] Cole: exactly @naw - I think conceptual explanations are more pertinent than technical
[06:06 PM] acemarke: it's the conception that's the problem
[06:07 PM] Cole: Basics > Concepts > Technics
[06:07 PM] acemarke: people think that combineReducers is the only way to do things
[06:07 PM] Cole: Would you say combineReducers is the most popular and/or efficient way to organize a reducer?
[06:08 PM] acemarke: and it's not. it's so totally not.
bad timing there
[06:08 PM] Shados: most popular.
[06:08 PM] acemarke: yes, it's absolutely the most popular way
[06:08 PM] Shados: efficient.....not so sure.
[06:08 PM] acemarke: which is the probably
[06:08 PM] Shados: a lot of Redux's problems come to combineReducer being a little too naive
[06:08 PM] naw: People understand combineReducers conceptually pretty well already but they totally miss the technical aspect and are therefore unable to extrapolate without someone holding their hand
[06:08 PM] Shados: eg: the thunk/side effect problem is very easy to solve if you used a different combineReducer
[06:09 PM] naw: So I think one of the goals for the document is to help people see both the concept and the technical aspect at the same time
[06:09 PM] acemarke: there was actually a PR recently to let combineReducers pass down a third arg
[06:09 PM] Shados: yeah. combineReducer is kind of a black sheep in a way
[06:09 PM] acemarke: Dan let it go for a while, but decided it was probably too involved
[06:10 PM] Shados: for everythign else...action creators, reducer switch, etc, Redux has no opinion and let you go on your own, and if you want to reduce boilerplate, you use stuff like redux-action, etc
but combineReducer is the one "boilerplate reduction" thats built in
[06:10 PM] acemarke: stuff like "what would the other arg actually be? top state? next-level-up state if this was used nested?"
[06:10 PM] Shados: I always found that kind of weird
[06:13 PM] naw: Immutability adds a fair bit of "noise" to reducers --- so not only are you concerned with how the state is changing (e.g. toggle a boolean), but also the lower-level immutable implementation of that change (which then involves Object.assign, object spreads, etc.)
[06:13 PM] acemarke: nice point
[06:13 PM] Shados: yeah
[06:13 PM] naw: Personally I try to separate those two concerns
which ultimately means helper functions
[06:13 PM] Shados: without making flat normalized reducers OR using a library (eg: immutablejs, or something with lenses like Ramda and lodashFP), reducer code is downright offensive
normalized state reducers are quite beautiful...and immutablejs or lenses sidestep the problem altogether.
[06:14 PM] Cole: @acemarke https://github.com/colepatrickturner/redux/blob/0f2e77d775a7d7b75645c87e7e244b2731073433/docs/recipes/StructuringReducers.md
[06:14 PM] Shados: (which is kind of a problem in a way...since deep state reducers add a lot of complexity and are harder to test and maintain)
[06:15 PM] Cole: I forked and put together what I think helps someone like me understand Conceptual => Technical
diff is here https://github.com/markerikson/redux/compare/structuring-reducers-page...colepatrickturner:patch-1
However, there are a number of common patterns that are widely used when writing reducers, as well as a number of related topics and concepts. I felt like the common patterns and related topics were non-sequitars but would be valuable when covered elsewhere
[06:17 PM] acemarke: that's because I am writing those patterns :)
functional decomposition/reducer composition, normalization, etc
[06:17 PM] Cole: that ties into what undefined was saying about examples
[06:17 PM] acemarke: I just haven't gotten through everything yet
I suppose I could say "This document will illustrate those patterns", but I hate the way that sounds
[06:19 PM] Cole: "However, there are a number of common patterns - such as combining reducers, or x or y or z - and a number of related topics (link to related topics)"
give examples
[06:20 PM] acemarke: I'm... getting there...?
[06:20 PM] Cole: I mean in the sentence
[06:20 PM] acemarke: (the hazards of putting out early unfinished WIP)
[06:20 PM] Shados: you did ask for feedback 😉 set yourself up for it
[06:20 PM] Cole: lol
[06:20 PM] acemarke: yes, yes, I know :)
[06:20 PM] naw: right --- it might just be too early for feedback
[06:20 PM] acemarke: awright, lemme at least hammer out this "immutable updates" chunk, and then I need dinner
[06:22 PM] Cole: Beyond combineReducers and the sole reducer, are there other patterns I haven't seen? For my own curiosity
[06:22 PM] Shados: a few.
[06:22 PM] acemarke: normalization, reducing reducers, ...
[06:23 PM] Shados: you can combine reducers using normal function composition, where the output of one affects the next

reducerA(reducerB(reducerC(state, action), action), action)
[06:23 PM] acemarke: oh hey, lemme get thoughts on this terminology:
[06:23 PM] Shados: you have reducers that handle side effects, as in redux-loop
(thats the Elm way)
[06:23 PM] acemarke: Since a Redux reducer is just a function, the same concept applies. You can split some of your reducer logic out into another function, and call that new function from the original "higher-level" reducer function. These new functions would typically fall into one of three categories:

  1. Small utility functions containing some reusable chunk of logic that is needed in multiple places (which may or may not be actually related to the specific business logic)
  2. Functions for handling a specific update case, which often need parameters other than the typical (state, action) pair
  3. Functions which handle all updates for a given slice of state. These functions do generally have the typical (state, action) parameter signature

The initial, "top-level" reducer function is typical referred to as a "root reducer", since it handles the top of the state tree. While there aren't widely used specific terms for the other types of functions, for the purposes of this article we will refer to them as:

  1. utility functions (reusable logic that is not business-related) and utility reducers (reusable logic that is business-related)
  2. case reducers
  3. slice reducers

[06:23 PM] Cole: much better!
[06:23 PM] Shados: you have combining reducers while passing additional data
etc
[06:23 PM] Cole: the first paragraph is perfect
[06:23 PM] Shados: higher order reducers
and more.
[06:23 PM] Cole: numbers helpt oo
[06:23 PM] Shados: its a recucer party~
[06:23 PM] acemarke: (that's still the same location, just more contents)
[06:26 PM] naw: I wouldn't call anything a reducer unless it has the (state/slice, action) => state/slice signature
So depending on what you mean by a utility reducer, I probably wouldn't call it a reducer
rootReducer --- good term for the single function
[06:27 PM] acemarke: which is sort of the issue at hand - a lack of distinct terminology
I've used "sub-reducers" in the past
but sorta generically
[06:27 PM] naw: sliceReducer -- fine term for a function which expects a single slice and returns a single slice however, I think we often refer to these as subReducers on github
[06:27 PM] acemarke: Dan also has used the phrase "reducer composition" in the past to generically mean any smaller function, but he agreed it makes more sense to restrict that to slice updates
so I'm actually trying to more specifically identify things, if only for use in this doc
can see the argument that only (state, action) funcs should have the word "reducer" in the name
so what would you call handleSpecificCase(state.a, state.b) ?
[06:29 PM] Shados: honestly? just "a function that does XYZ"
[06:29 PM] Cole: reducer fragment
[06:29 PM] acemarke: or doThingThatHappensInsideSeveralCases(chunkOfData) ?
[06:29 PM] Shados: its an uncommon enough case that its not even worth naming.
[06:30 PM] acemarke: well, no, that's more or less the classic "crossing slices of state" example that everyone asks about
might need the action, might not
[06:30 PM] Shados: no, thats (state, action, state)
which is just a specialization of (state, action)
[06:31 PM] acemarke: I've got a couple cases like that in my app right now
I've already stored which entity is the current "selected" one
when the user hits the "edit" button, I duplicate that entity's relations and copy them to a "work-in-progress" section of state
the higher-level reducer knows that for that case, it does:

export default function globalEditingReducer(state, action) {  
    switch(action.type) {  
        case EDIT_ITEM_START: {  
            const selection = editItemStart(state.selection, state.entities);  
  
            return {  
                ...state,  
                selection,  
            };  
        }  
        case EDIT_ITEM_APPLY: {  
            const entities = editItemApply(state.selection, state.entities);  
  
            return {  
                ...state,  
                entities  
            };  
        }  
        default :  
            return state;  
    }  
};  

(lousy function naming, in the middle of cleaning things up atm)
[06:33 PM] Shados: yeah, those are just normal helper functions.
they dont need special names, and its not the case people ask about so often
[06:33 PM] naw: One problem I've seen is that people call helper functions reducers
[06:33 PM] acemarke: that sure seems like exactly the case people are asking about, just that in this particular example passing action isn't needed
well, okay, lemme backtrack a bit
[06:34 PM] naw: So I think the idea of using a helper function to help you calculate the new state is a common pattern, and giving it some terminology would help
[06:34 PM] Shados: its not needed because you flipped it over
you nested it inside of an action instead of nesting an action in it
making it just a complex state mutation instead of being an action handler
you could have a reducer that only handles EDIT_ITEM_START doing that state mutation and then you wouldn't need to nest an action
[06:36 PM] acemarke: not sure I followed that
the usage winds up as const rootReducer = reduceReducers(normalCombinedReducer, globalEditingReducer)
[06:37 PM] Shados: the only other common case for reducers is the "I need state from reducer A's output to calculate reducer B's output, within a single action" (usually a code smell, but there's a few cases where its needed)
in which case its just the way you combine your reducers change a bit to reducerB(reducerA, action), action) and thats about it.
[06:43 PM] naw: understanding how to mix and match reducers and/or helper functions to build a single rootReducer that handles a variety of scenarios is not trivial. I believe the goal is to create a document that will help newbies grasp some of those concepts/techniques.
[06:43 PM] acemarke: pretty much
[06:44 PM] naw: So if someone asks "What if I need my subReducer for notifications to be able to read state from a different slice"
The answer should be something like: "If your notifications subReducer needs state from another slice, then you need to rebuild it as something a little bit different than a subReducer"
There are a few different patterns that would solve such a scenario

  1. A top-level reducer called from the rootReducer using some kind of reduce reducers pattern
  2. A helper function called from the rootReducer, whose return value is manually inserted into the state by the rootReducer
    (note: 1 is really just a special case of 2)
    (also: a subReducer is really just a special case of a helper function)
    [06:51 PM] naw: I think there are 3 broad patterns:
  3. the rootReducer manually creates the new state object, delegating to helper functions for specific pieces of the state.
  4. the rootReducer is created by combineReducers, where the assumption is that there is a helper function for each top-level state key. You could write this manually, but combineReducers takes all the boilerplate away. Each helper function expects one slice and returns the new slice.
  5. the rootReducer delegates to helper functions which expect the whole state, and return the whole state, but the helper functions are run in sequence (i.e. with reduce-reducers)
    Any other pattern is likely just a combination of those
    however, at a higher level there are additional patterns you can use to construct those reducers.
    Notice that (2) and (3) are really just special cases of (1)
    [06:58 PM] naw: To clarify, I think there can be other patterns than (2) or (3) ---- really you can imagine any number of abstractions all of which construct (1).
    The various additions people have suggested for combineReducers are perhaps good examples of other abstractions
    You could imagine some hybrid between reduce-reducers and combineReducers with an API like this:
    [07:00 PM] Cole: imho subReducer rootReducer sliceReducer are confusing to an outsider like me
    I look at the doc and see " but don’t specify how the application’s state changes in response. This is the job of a reducer"
    They're all "reducers"
    [07:01 PM] naw: My recommendation is we (by we, I mean the whole community) standardize some terminology so at the very beginning you can be told "A rootReducer is .....", "A subReducer is .....", and the terminology become consistent throughout docs, issues, stackoverflow, discord, etc.
    [07:03 PM] Cole: Going back to "What if I need my subReducer for notifications to be able to read state from a different slice"
    When I came across this problem I came across a clear solution that told me to bind the same action across multiple reducers
    [07:04 PM] acemarke: "bind the same action"?
    [07:04 PM] Cole: errr
    "acknowledge"
    acknowledge the same action name
    [07:05 PM] acemarke: right, multiple reducers handling the same action
    which is totally valid, you just have to put that "shared" data into the action itself when you create it
    [07:05 PM] naw: Right. There are two solutions:
  6. Put all necessary date into the action itself.
  7. Move the logic higher up in the overall reducer tree
    [07:06 PM] Cole: there you go, you explained it without extensive jargon such as sub or root or slice reducer
    jargon obfuscates the learning process
    a noob is halfway through a tutorial wondering "wait what's the difference between a sub and root reducer again?"
    [07:08 PM] naw: Right, so we need common terms so that when someone asks a question, we can use terminology and reference its definitions/examples without starting from scratch every time
    [07:09 PM] acemarke: (I'll also point out that that item is in the FAQ....)
    [07:09 PM] naw: For example, "adverb" has a meaning, so if someone is struggling with grammar, I can tell them you're using an adjective instead of an adverb and opint to the references
    [07:09 PM] Cole: or you may find yourself having to re-explain subreducer"
    [07:09 PM] acemarke: http://redux.js.org/docs/FAQ.html#reducers-share-state
    [07:10 PM] naw: ^ That's a great FAQ
    [07:10 PM] acemarke: :) :) :)
    [07:10 PM] Cole: yeah the use of "slice" is clear
    [07:11 PM] acemarke: for that matter, the "Reducers" page even introduces that terminology:

Note that todos also accepts state—but it’s an array! Now todoApp just gives it the slice of the state to manage, and todos knows how to update just that slice. This is called reducer composition, and it’s the fundamental pattern of building Redux apps.
[07:14 PM] acemarke: what I really wanted to cover in this doc is stuff like "combineReducers isn't the only way to do things", "here's how you can use reduceReducers to do cross-slice data", and "here's how you normalize your state and write update logic for that shape"
but I'm feeling like I have to start from the ground up
[07:15 PM] naw: Perhaps you can start with a intro paragraph that says something like "This page assumes you have a working knowledge of .... Read this, this, and this first"
[07:15 PM] acemarke: (which is sort of what I was afraid of - there's so many overlapping concepts involved here... )
may try that
[07:16 PM] Cole: I like the "As previously seen on (TV SHOW)" approach
🙃
[07:17 PM] Shados: yeah, especially since you have 3 distinct concepts being dealt with there... Immutable updates (which vary wildly depending on which tooling you pick and will even change how you shape your state), state shape, and state logic.
decisions you make on one affect the other
[07:17 PM] acemarke: yep
[07:18 PM] Shados: eg: if you have logic-light reducers, you can completely flatten your reducers and then immutable updates become trivial with basic JS constructs.
if you have logic heavy reducers, then things like immutable.js start becoming very important.
etc etc.
[07:20 PM] acemarke: I'm starting to think this doc could easily become several sub-pages
[07:23 PM] acemarke: intro page ("go read these other pages first"), basic structure and state shape, immutable updates to plain JS data, decomposition terms, move all that refactoring example to its own page, normalization would get one, etc
[07:23 PM] Shados: yeah
cuz you will have a situation where like, "If you make this design decision in your app, actually, this entire page is now irrelevent to you"
so you don't want to bog people down with things that may not apply to them.
[07:23 PM] Cole: 👍
[07:24 PM] acemarke: oh man. 7:30 already.
[07:24 PM] Shados: like, if you pick to use immutablejs, then keeping immutable data is trivialized (you now have OTHER problems, but not that one, hehe)
[07:24 PM] acemarke: cranked out 430 lines of Markdown in the last few hours
[07:25 PM] Shados: I always find the thining more time consuming than the producing, when i write blog posts or doc.
my first drafts get pumped out in an hour or two, and are a bazillion pages long...then i need weeks (or months) to trim it back down to something meaningful.
[07:26 PM] acemarke: I usually have fairly passable first drafts of stuff
[07:26 PM] Shados: Im always jealous of those great bloggers who can bring down complicated concepts to a few sections
a skill I unfortunately do not have
[07:26 PM] Cole:
[07:26 PM] acemarke: yeah
[07:41 PM] acemarke: awright, I'm done with that for the day. Need exercise, food, and time spent blowing up virtual spaceships.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment