Redux: Redux screencast series on Egghead

Created on 24 Nov 2015  Ā·  69Comments  Ā·  Source: reduxjs/redux

If you're following Redux repo but haven't dived into it yet, or are confused about some of its fundamental aspects, you'll be happy to learn that Egghead has just published my Getting Started with Redux series.

It covers the same topics as the ā€œBasicsā€ part of the docs, but hopefully dives a little bit deeper and ensures you _really_ understand the Redux fundamentals.

I plan to create more content about Redux on Eggheadā€”this time, for subscribers only. It will feature more advanced topics. If you have particular suggestions, please let me know in this thread!

feedback wanted

Most helpful comment

@markerikson thanks for bearing with me on this line of questioning. At the risk of coming across as argumentative, I _do_ want to push back a little bit on the idea that this is "app level" vs. "UI level".

Note: For convenience I'm going to use thunk to represent an abritrary middleware approach to async, whether it actually be redux-thunk or redux-promise, or whatever.

Your UI layer just wires user interactions to handlers. When someone clicks a button, your UI is rigged to call _some_ handler. Often, the component receives these handlers as props --- perhaps a bound action creator, for example. The UI is unaware what happens when it calls a handler --- it just calls it.

It makes no difference to the UI layer whether the "handler" dispatches a function (to be handled by middleware) or whether it executes an async call and _then_ dispatches a plain action --- the UI is completely agnostic (or at least, it _can_ be agnostic)

A large part of your "app" is in these "handlers" whether you're using thunks or not. In a typical react/redux app, these "handlers" are often action creators of some kind. You could write all of your async stuff as thunks, and dispatch them. Or, you could write all of your async stuff as functions that accept dispatch as an argument. From the perspective of the component, it's either someHandler(dispatch) OR dispatch(someHandler()), or in the case of bound action creators passed down from higher up, it's just someHandler() in both cases. You could build a version of bindActionCreators which would completely mask this difference from the UI layer.

If you give me a react/redux application written with action creators using redux-thunk, I could completely swap out redux-thunk and use a non-middleware approach without _fundamentally_ changing any of the UI layer. (note: I'm glossing over where/how you inject getState, but I _believe_ that's a minor detail here).

Therefore, I'm having trouble accepting that the distinction between "inside" and "outside" is "app level" or "UI level".

I appreciate the discussion, and I hope I'm not coming across negatively.

All 69 comments

:+1:

  1. Local component's state vs. global state
  2. Action handled by multiple reducers vs. 1-to-1 action-reducer relation
  3. Handling action chains (especially async ones), when second action should be triggered right after the first one is finished.
  4. Optimization technics to prevent unnecessary rerenering (batching actions, reselect etc.).

:+1:

I've just watched the first 16 episodes in the series and think they're an excellent resource that we'll likely be using for internal training at the company I work for. However, the fact you only ever used plain objects and deep-freeze for your state-tree, and never use or refer to immutable libraries like immutable-js makes me wonder if you are encouraging people down this path, or whether this just makes presenting the ideas easier?

Would be great to know your thoughts on this.

Throwing Immutable into the mix would confuse beginners who'd need to learn to differentiate between Redux and Immutable API, may assume Immutable is required, etc. It's a good idea for a couple of advanced lessons though!

Throwing Immutable into the mix would confuse beginners who'd need to learn to differentiate between Redux and Immutable API, may assume Immutable is required, etc. It's a good idea for a couple of advanced lessons though!

Okay, makes sense. Just good to know your thinking behind this decision :-).

What @smashercosmo said :+1:

Unit testing. TDD.

What @cateland said :+1:

I must say Iā€™m very impressed by you skill set, being able to create excellent js libraries with extensive documentation and concise tutorials in this manner.

After viewing all the videos I have a bunch of feedback on the specific videos that Iā€™ll send by mail. The conclusion though is that this is so much more than an introduction to Redux. You cover code practices and component architecture and more. It is really really awesome and very pedagogical. I think the videos can be divided into three groups though. Redux basic, redux behind the scenes, and redux with react.

For upcoming videos Iā€™d like to see less react specific concepts and more on async actions and tests.

Remarkably well done, keep it up! :)

In video 21 you note that the TodoApp component no longer needs to be a class, it can be a function. It would be great if you could explain how you came to this realisation -- why is this a suitable candidate to be a functional component, and what benefit does this give?

This is so good.

Item 3 in @smashercosmo 's list is something I'd like to know as well.

what @wpannell said! Unit Testing/TDD!

Would also love to see videos on the topics covered in the Advanced docs.

What specifically are you interested in with regards to unit testing?
Lessons 5, 11, 12 give an idea of how to unit test the reducers.

...that's a good question. Would the process change very much when they're mocha tests?

Not really. But I guess that's a good topic for a series of advanced lessons. Unit testing reducers, action creators, components etc.

Firstly, great videos. Already understand redux but it was refreshing.
If you're creating more advanced videos, may i suggest async actions / middleware.
Don't think unit testing really needs any coverage. You can just called your reducer functions and assert on them?

Yeah, I guess it really isn't much else other than wrapping the expects in mocha tests. :thumbs up:

Thankfully everything is so simple!

Immutability with object ID hashes (e.g. [post._id]: {...post}).

I find myself relying too heavily on a reduce function to take an array of API entities and produce an ID hash with it. I know that normalizr will handle some of this, but I'd like videos similar to the EggheadIO videos where you take us from point A to B. It is not solely a Redux-based thing, but is heavily intertwined.

@raquelxmoss

In video 21 you note that the TodoApp component no longer needs to be a class, it can be a function. It would be great if you could explain how you came to this realisation -- why is this a suitable candidate to be a functional component, and what benefit does this give?

That was actually some sloppy lesson planning on my part. I didn't realize until too late that I could've made it a functional component from the very start. The benefit for using functions over classes is simplicity, so do this anytime you can. Basically if you don't need lifecycle hooks like componentDidMount or state, use functional components.

I would find a lesson on how we can split these pieces of code up into an actual directory structure to be helpful. I know that as long as everything is there it's probably going to work but maybe recommended conventions of naming folders (component / container / store / reducer etc), what kinds of files go into which folder, and so on would be nice.

I'd also like advanced reducer composition.

Reusing complex components (that are comprised of a reducer or multiple reducers, a set of actions, action creators, maybe accessing some server-side API, several React components -- like redux-form, but more real-app-specific). This also includes organizing modular directory structure.

Watched the whole thing, very good! I appreciated the use of ES6/7 syntax (Object.assign-like), React 0.14 function components and avoiding Immutable things.

Perhaps a video explaining the recommended code architecture.

And updating the doc to use ES6/7 syntaxes? (are PR welcome in that direction?)

Unit testing, creating API Middleware, doing OAuth/some type of user authentication with Redux, using Immutable with redux (how to get it set up, best practices etc.)

This series serves as a great introduction to Redux, the single-atom state, and related philosophies. I thought you did a nice job of homing in on the core principles whilst avoiding inducing cognitive overload. The environment you worked in is also easily replicable, which makes the hands-on portion all the more approachable.

@gaearon What do you think of structuring actions in the example videos as a wannabe-standard { type: string, payload: Object } right from the beginning? I'm talking about the counter list example, where the payload is put onto the action object itself; { type: string, index: number }. This looks like an anti-pattern to me.

What do you think of structuring actions in the example videos as a wannabe-standard { type: string, payload: Object } right from the beginning? I'm talking about the counter list example, where the payload is put onto the action object itself; { type: string, index: number }. This looks like an anti-pattern to me.

It's not an anti-pattern in any way. It's a normal action object. FSA is fine but it's a _convention_. Nothing in Redux itself depends on or benefits from this convention, so we don't want to enforce it.

People used to think all sorts of magic things about payload, source in original Flux documentation. They blindly copied these things without understanding why they exist and carefully assessing whether they even need them. Later they complained about Flux being complex and verbose, when really in many cases they copied the verbose (but non-essential) parts themselves.

In these lessons I only teach the essential parts of Redux. Note how I don't introduce constantsā€”because people concentrate on them too much, and miss that they don't really matter. You're more likely to understand the benefits of constants after you make a few typos in strings, rather than if I put them into the tutorial videos from the start. I think the same goes for other conventions like FSAā€”by all means, use it if you find it convenient, but I won't preach it if the lessons don't demand it.

@gaearon OK, I'm with you then, in hope that those who got used to the simple unstructured approach won't make bigger apps unmanageable by not applying any convention.

@sompylasar

I think examples display conventions better than video tutorials. Examples is the opinionated land of ā€œhereā€™s how Iā€™m going to structure some code around those principlesā€, and videos are about those principles themselves. This is why youā€™ll see different conventions in different examples, and before starting a project most people look through different examples to get a taste for different ways to do that.

Also, thereā€™s absolutely nothing unstructured about not following FSA. { type: 'STUFF', id: 1 } is not inherently worse than { type: 'STUFF', payload: { id: 1 } }. Itā€™s just a matter of taste and (sometimes) tooling convention. Keeping action objects payloadless is not making them harder to work with.

We've got a handful of redux unit testing lessons coming out soon on egghead šŸ˜„

We held off ANY Redux lessons for some time in order for Dan to have first crack. Totally worth the wait, and then some.

"Build an Application with Idiomatic Redux" would be a wonderful advanced course šŸ‘

@joelhooks those both sound amazing!

I'd like to see you setup a project using webpack and hot reloading instead of using jsbin. Take this into the real world. I know it's not specific to redux but I think it would fit well and you're the right man to teach this :)

@kevinrenskers it's not a video series, but if you feel like dissecting something, there are a few really great example boilerplates you can reference!

Those who have asked for a project structure for Dan's example and Webpack configuration.

Please check this out https://github.com/urbanvikingr/todo.

I've committed to update Redux with React doc to be aligned with Dan's code from the videos. It'll be done within next two weeks - my Holiday project :) - stay tuned.

My wish list to Egghead.io videos:
action / reducer testing with Jasmine
deep-dive on middleware (thunk / promises)

The screencasts are a very useful introduction to Redux. What I'd like hear is the Redux-way to do routing and server-side rendering

@grigio You might interested in this discussion https://github.com/rackt/redux/issues/1145 about routing

@urbanvikingr thanks, subscribed. The poll seems closed, but I would have voted #1168

The lessons are actually pretty good, but it's distracting that when you say "store", it sounds like "chore".

Everyone here has noticed and been distracted by it. They're just too politically correct to bring it up. So yeah, I decided to be _that_ guy, in case no one else ever would :)

I'll get better at speaking English eventually. For now this is the best I can do ;-)

@gaearon You have done a damn good job in explaining Redux. Kudos to you and your open source achievements.

Amazing set of lessons. I especially liked how you re-implemented each of the core Redux functions from scratch in a "read the source" way. Seconding someone else who was impressed at how clear the tutorials and all of the documentation is for Redux. So far, as as someone catching up on two years of frontend advancements, the concepts have been hard to wrap my mind around, but the docs have been amazingly helpful at doing so. Keep it up, and thanks!

(Also don't listen to @jugimaster, not everyone was distracted by your accent and was "too politically correct to bring it up", or even cared that you have an accent.)

@ianstormtaylor
It's not an accent, by the way :)

I didn't "care" either, but I sure was distracted!

But on the other hand, as someone who's studied six foreign languages, I _have_ always cared about speaking a language correctly, and to that end, I personally appreciate someone pointing out my mistakes.

Hey @gaearon,

Absolutely loved your course. Nice work! It was very helpful.

I added three Redux testing video lessons to my recent Egghead course:
https://egghead.io/series/react-testing-cookbook

My hope is they will be complimentary to all the amazing work you have done!

Solid course!

Improves not only Redux knowledge but modern practices knowledge overall! Keep on doing such a good stuff :+1: :tada:

hey, just realized there is not a link/reference to the code in the videos. Maybe obvious, and perhaps, simple enough users could just copy the videos; but I think a lot of folks could benefit from a repos with the exact code in the videos- why not?

Code snippets for every lesson are available to Egghead subscribers. :-)
The videos are free but the platformā€™s gotta make money so it can invest into more courses, send equipment to the instructors, host the videos, improve the website, and so on.

That said we have an examples/todos folder that pretty much matches the course.

... cool, I'm missing it then? Looking for link(s) ...

@gaearon apologizes, just revisited the videos. the code is right there :)... watched the videos, bought membership, watched others... just went back to the redux videos actually logged in. cheers.

By the way, a few people complained that transcripts are inaccurate.
Please send PRs to fix them: https://github.com/eggheadio/egghead-redux-transcripts

@gaearon I decided to use redux, and then found the redux vids on egghead. The videos really helped me start to learn redux. Going forward it would be great to see more real world examples.

So my suggestion for redux video training going forward would be advanced composition, a deeper dive into refactoring, and definitely how to use reselect. You seem to have an intuition on when to refactor. So, since functional programming is so intimately tied to redux, getting some tips from you on when to refactor and how to identify a function that does one thing well would be really useful.

In the app I'm building I have several large data collections and I need to put them into tables and do things like sort and paginate the data. I'm having difficulty deciding when to use selectors and when to use create actions. Currently I have USERS_SORT_TABLE action and SORT_TABLE action because I have the users store "inheriting" some state from the table. I did this because I didn't want a SORT_TABLE action on a todo store getting sorted by the user store.

I know my solution is not DRY, but I'm not sure how to do it correctly. As it stands I'm going to have to create a "SOMETHING"_SORT_TABLE action for every store I want to populate a table, which I know is wrong, but I don't know the right way. Another side effect is that my action name are getting huge because I have to separate different store by prefixing their name to the action. This can't be right.

Here's some example code:

/* actions.js */
// ...
export const USER_MOVE_COLUMN = 'USER_MOVE_COLUMN'

export function userMoveColumn (columnIndex, moveToIndex) {
  return {
    type: USER_MOVE_COLUMN,
    columnIndex,
    moveToIndex
  }
}

export const DATA_TABLE_MOVE_COLUMN = 'DATA_TABLE_MOVE_COLUMN'
// ...

/* reducers.js */
// ...
export default function user (state=userInitialState, action) {
  switch (action.type) {
    // ...
    case USER_MOVE_COLUMN:
      return dataTable(state, assign(
        action,
        {type: DATA_TABLE_MOVE_COLUMN}
      ))
    // ...
    default:
      return state
  }
}
// ...
export default function dataTable (
  state=dataTableInitialState,
  action,
  key='dataTable')
{
  switch (action.type) {
    // ...
    case DATA_TABLE_MOVE_COLUMN:
      return {
        ...state,
        [key]: {
          ...state[key],
          columns: move(
            state[key].columns, action.columnIndex, action.moveToIndex
          )
        }
      }
    // ...
    default:
      return state
  }
}

So you can see I created a dependency between the table and the "model" store that I shouldn't have (the model store must use the collection key for it's array of objects). And the dataTable manipulates the "parent" reducer's state, which it shouldn't. It occurred to me that I need to use a selector there, but at the time I wrote this, I was trying to avoid duplicating a large store just to change what was visible in the UI.

So currently I'm struggling to learn reselect to solve some of these problems and refactoring techniques to solve some of the others. The first Redux course was enough to make me dangerous. Now I'd like to learn how to do it right. :)

I hope that was helpful and not too verbose. Trying to give clear, honest feedback.

For any kind soul who might help me, I've already found /examples/real-world/reducers from another of Dan's comments and I'm currently reworking some of the issues I've outlined above. Didn't want you to waste time trying to help if I'd found a solution.

Thanks for the warning :)

integrating the redux-devtools within my project was a huge pain for me .. i would have (and still will) appreciated a egghead series that describes what is out there and how / when to use it. I read the PR where you describe when to use what .. but I got so confused there are so many things out there hmr, transform 3, redux hot reloader is different from react hot reloader and so on ..

For anyone else struggling with the some of the issues I've outlined above, I created a project that allows you to remove most if not all of the boilerplate in Redux as well as namespace actions. See that here

@granteagon Doesnā€™t this couple reducers to action creators? One of the core design principles in Redux is that _reducers should be decoupled from actions_. Any reducer can listen to any action. There is no 1:1 mapping between them. Otherwise, itā€™s hard for different parts of the state tree to independently change their state in response to the same actions.

I've noted that most people creating wrappers or abstractions on top of action/reducer creation tend to assume that they will always be directly coupled together. Admittedly, in my app, so far _most_ of my actions have exactly one corresponding chunk of handling logic, but there definitely have been several where multiple parts of the tree need updated.

@gaearon @markerikson It does couple reducers to action creators. However, 90% of the time, that's okay or even desired. The other 10% of the time you can still use a hand-coded approach. I will think about what you've said though and consider it for future development.

@granteagon @gaearon

Having used a local abstraction that is similar to reduxr, I would argue there isn't any added coupling here. Nothing is forcing 1:1 mapping between actions and reducers. You can still have two reducers in two different slices listening to the same action:

const counterReducersA = {
  // this counter increments by 1 each time
  increment: (state, action) => state + 1
}

const counterReducersB = {
  // this counter increments by 2 each time
  increment: (state, action) => state + 2
}

const counterA = reduxr(counterReducersA, 0);
const counterB = reduxr(counterReducersB, 0);

const rootReducer = combineReducers({
  counterA: counterA.reducer,
  counterB: counterB.reducer
});

store.dispatch(counterA.action.increment());  // increments both counters

Of course, if you have more than one "reducer" function named the same thing (i.e. responding to the same action), implicitly they both need to "expect" the action payload will be in a certain shape --- which is completely analogous to having two reducers in vanilla redux both handling the same type constant --- both have to expect the same action shape.

Perhaps I've misunderstood what you mean by coupling, @gaearon ?

I think showing an asynchronous flow without middleware before showing the thunk implementation could be helpful.

Somewhat related, but while the documentation has been insanely helpful, this statement on the async flows page really threw me off track in hindsight: "Without middleware, Redux store only supports synchronous data flow."

@battaile : that's because it's true :) Without middleware, any asynchronicity has to happen completely outside of Redux (so, most likely in your UI layer, such as React components). Any time you call store.dispatch, the action would go straight to the reducer function, do not pass Go, do not collect $200, do not make any stops along the way for AJAX calls.

Store enhancers allow you to wrap up functions like dispatch with your own version, and so applyMiddleware provides the abstraction of a "middleware pipeline" before something reaches the real store's dispatch function. That basically provides a loophole where you can jump out and do whatever asynchronous stuff you want, _inside_ of the standard Redux flow.

So, without middleware, you could still totally do async stuff... it would just all have to happen entirely separate from anything actually related to Redux.

that's because it's true :)

I didn't say it was false, I said it threw me off track :)

I just wanted to do something like the following, which it seemed to imply that I could not:

const mapDispatchToProps = (dispatch) => ({
  onclick(searchTerm) {
    dispatch(actions.requestOrders(searchTerm));

    return fetch('http://localhost:49984/Order/Search?search=' + searchTerm)
      .then(response => response.json()).then(response => {
        dispatch(actions.receiveOrders(searchTerm, response));
      })
      .catch((err) => {
        dispatch(actions.receiveOrdersError('An error occurred during search: ' + err.message));
      });
  },
});

I realize this could easily get ugly, but conceptually I think its useful to see. Or at least it was in my case.

I agree that "Without middleware, Redux store only supports synchronous data flow." is misleading.

Technically if you want to draw a distinction between "inside" and "outside", the statement might be true, but if it leads people to believe that the only way to do async is by adding middleware, perhaps we can reword it or elaborate on it.

Yeah, the difference there is that the async behavior is technically happening more at the component level, rather than "inside" of dispatch. A minor distinction, but a valid one.

I don't think anyone is actually arguing that the statement isn't technically correct.

@markerikson Just curious if you have any concrete examples where the distinction between inside and outside matters?

One example might be if you want middleware in the chain _before_ your async middleware to be able to see your dispatched thunk (or promise, etc). I'm not sure _what_ that middleware would _do_, but I suppose it's feasible to want such a thing.

Mmm... not sure about "concrete" examples specifically. Overall, the distinction between "outside" and "insie" is a question of whether it happens _before_ you call dispatch the first time, or _after_. If it's "outside" and "before", then all your asynchronicity and logic is more tied to the view layer, whether that be React, Angular, or something else. If it's "inside" and "after", then your asynchronicity and logic is at the store level, and _not_ tied to the view layer.

This is actually much of the point I was just trying to make in a Reddit discussion earlier today: https://www.reddit.com/r/reactjs/comments/4spbip/has_anyone_inserted_a_controllerpresenter_layer/ .

The question of "what actions do I dispatch and when do I dispatch them?" is a core part of your business logic, with the other half being "how do I update my state in response to those actions?". If the action management is in thunks and sagas and such, then it really doesn't matter whether that code was kicked off by a React Component, an Angular Controller, a jQuery click handler, a Vue component instance, or something else. Your core logic is outside the UI layer, and the UI layer is really just responsible for pulling in the data it needs out of the store, displaying it, and turning user inputs into an app logic function call.

So, in that sense, I'd say the question of "inside" vs "outside" does matter, because it's a conceptual distinction between whether your logic is living at the app level or the UI level.

@markerikson thanks for bearing with me on this line of questioning. At the risk of coming across as argumentative, I _do_ want to push back a little bit on the idea that this is "app level" vs. "UI level".

Note: For convenience I'm going to use thunk to represent an abritrary middleware approach to async, whether it actually be redux-thunk or redux-promise, or whatever.

Your UI layer just wires user interactions to handlers. When someone clicks a button, your UI is rigged to call _some_ handler. Often, the component receives these handlers as props --- perhaps a bound action creator, for example. The UI is unaware what happens when it calls a handler --- it just calls it.

It makes no difference to the UI layer whether the "handler" dispatches a function (to be handled by middleware) or whether it executes an async call and _then_ dispatches a plain action --- the UI is completely agnostic (or at least, it _can_ be agnostic)

A large part of your "app" is in these "handlers" whether you're using thunks or not. In a typical react/redux app, these "handlers" are often action creators of some kind. You could write all of your async stuff as thunks, and dispatch them. Or, you could write all of your async stuff as functions that accept dispatch as an argument. From the perspective of the component, it's either someHandler(dispatch) OR dispatch(someHandler()), or in the case of bound action creators passed down from higher up, it's just someHandler() in both cases. You could build a version of bindActionCreators which would completely mask this difference from the UI layer.

If you give me a react/redux application written with action creators using redux-thunk, I could completely swap out redux-thunk and use a non-middleware approach without _fundamentally_ changing any of the UI layer. (note: I'm glossing over where/how you inject getState, but I _believe_ that's a minor detail here).

Therefore, I'm having trouble accepting that the distinction between "inside" and "outside" is "app level" or "UI level".

I appreciate the discussion, and I hope I'm not coming across negatively.

This course is great. Closing this out so people can direct their comments to the community notes repo for the course: https://github.com/tayiorbeii/egghead.io_redux_course_notes

Also, be sure to check out the next series Dan put togethe! https://egghead.io/courses/building-react-applications-with-idiomatic-redux

Was this page helpful?
0 / 5 - 0 ratings

Related issues

olalonde picture olalonde  Ā·  3Comments

ramakay picture ramakay  Ā·  3Comments

timdorr picture timdorr  Ā·  3Comments

parallelthought picture parallelthought  Ā·  3Comments

benoneal picture benoneal  Ā·  3Comments