Skip to content

Instantly share code, notes, and snippets.

@acambas

acambas/blog.md Secret

Created December 9, 2017 18:25
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 acambas/8b7688a81de48af4abfe2ae2a966c5f1 to your computer and use it in GitHub Desktop.
Save acambas/8b7688a81de48af4abfe2ae2a966c5f1 to your computer and use it in GitHub Desktop.

Integrate React and Redux better with middleware

In the last few years there was a lot of new libraries, tools and frameworks that changed the way we develop web application. Backbone.js, angular, ember, react, webpack... But the one that find the most fascinating one is redux and how it can be combined with React to make great apps.

I wont try to talk about what redux is, many other people did that job better then i can do, what I'd like to talk about are some patterns for organizing redux code in your project.

When I started using redux I've read up on what is the best way of organizing the code and saw that a popular solution is to keep reducers, actions, selectors and constants in 4 separate folders, feeling happy that i now know how to build redux apps I've applied my new knowledge to my projects believing that everything is gonna work great. How wrong was I :). Very quickly those 4 folders contained too many files and doing simple things always required me to navigate at least 4 files. Because of frustration I started cheating, put actions in container files, didn't use constants, make reducers bigger then they should be...

Luckily I spotted a great article about my problems which led me to redux ducks. ReDucks :) is a way of organizing your redux code and in its core is very simple, you should keep actions, constants, selectors and reducers that are domain connected in the same folder. This made building, testing, changing and extending redux part of the app a lot easier. Many thanks to erikas for publishing this genius pattern.

But as my app grew i found that some other issues that started to appear. I've grouped my redux part of the code into properly split modules(I've got my ducks in a row) but as my apps grew bigger there was more and more a need for my redux modules to act in a synchronized way and i needed to figure out a way how to solve this problem.

After trying some ideas and tools i came up to an idea that i would like to share so that it might help other people and to open a discussion to improve it. So...

It's time for an example

(the example is simplified so some of the things might seem like an overkill but please go with it)

Lets say we have a React-Redux app that has a page with panel 1 and panel 2, and it needs to show foo data, and bar data in those panels. When the app opens it needs to get foo and bar data and then show the new data in those panels.

https://gist.github.com/56dd2f9a841e28883f55d3066dc80083

As you can see from the code we have a App container in index.js file that gets foo and bar data from reducer state and dispatches the actions to get the data. The redux code is split into foo.js and bar.js that have their constants, reducers and actions in them.

I like to treat react components as html renderers as much as i can and not as a glue that binds my business logic because i don't want to test my business logic by looking what my React components rendered. To me React components should as much as they can be a representation of redux state and nothing else.

Here are some of the issues i have with <App /> component

  • it needs to know the to get foo and bar data
  • it needs to know what parameters it needs to pass
  • it needs to know the order of getting the data
  • if these actions need to be called with something from the state i will have to import potentially unnecessary information just to call these actions

To me non of these things should be here

https://gist.github.com/5f6ecd788fc70303fb02a3136e3f6a45

So lets try to improve this situation, as i wrote in the app description when app opens it needs to load foo and bar data so we can abstract these to another action that can call these to actions and we can call this action something like appIsOpened. You can do this in several ways but i find redux-thunk works great for this It would look something like this

https://gist.github.com/02f6be9f508fcf01a81a617c6514b8d4

This looks a bit cleaner and i can test the action separately from the component. There is still a nagging issue that i have which is that foo.js and bar.js still don't have any knowledge about this use case between them but OK I'll put up with it for now.

Now my boss comes and says that in panel 2 on the page I need to add buzz data and to get buzz data i need to use bar data. OK so now i need to add buzz duck module with it's actions, reducer and constants and then i need to update appIsOpenedAction action to load buzz data as well. Ok lets start coding

https://gist.github.com/167c6e861d1a0d30748e407cfc4dac61

Now that nagging issue that i had about my code organization came back to haunt me. This action here needs to know so many things about my app, and every time i want to add another source of data to show on the page this action will keep growing until it becomes one of those GOD methods that nobody wants to change and everybody just believes that it works great(Oh and testing it becomes a nightmare).

So my idea was that i need to make foo.js, bar.js and buzz.js have the their part of the logic on how to load the data that app needs when it opens.

My simple solution

custom middleware

We can create a redux middleware for each of the ducks modules that will listen for actions and respond to them if necessary for that module.

For Example

https://gist.github.com/860d16a06144d2c32d41b7aeecdb34e1

This code organization gives me ability that if i wanted to add a new data source that needs to be loaded when the app opens i only need to plug a new module in and that module only deals with it's own logic. Of course this pattern can be applied for many other scenarios not just when the app is opened, you can use it for other times when you need different parts of your business logic to properly respond to an event coming from your React(or other UI library) code.

Nice side effect of having custom middleware is that you don't need redux-thunk because you can use dispatch and getState inside the middleware that you create.

Part of the inspiration for this i got from using redux-saga which i liked but the concept but it always felt like using a sledgehammer when i needed a small hammer.

So this is it do you like, dislike, is the solution clear let me know in the comments.

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