no trees, just rocks Redux has become one of the most popular Flux implementations for managing data flow in React apps. Reading about Redux, though, often causes a sensory overload where you can’t see the forest for the trees. Presented below is a straightforward and opinionated workflow for implementing real life apps with Redux. It is shown by example with a step by step implementation walkthrough of an actual app. It attempts to apply the principles behind Redux in a practical way and detail the thought process behind every decision. An opinionated approach for idiomatic Redux has become more than just a library, it’s an entire . One of the reasons behind its popularity is its ability to accommodate different writing styles and many different flavors. If I’m looking for asynchronous actions should I use ? or maybe ? or ? Redux ecosystem thunks promises sagas There isn’t one right answer to which flavor is “best”. And there isn’t one right way to use Redux. Having said that, too much choice is . I want to present an flavor that I personally like. It’s robust, can deal with complicated real life scenarios — and most of all — it’s . overwhelming opinionated simple So let’s build an app! We need a real life example to walk through. As long as we’re being opinionated, the most interesting place on the Internet is . Let’s make an app that shows the most interesting posts there. Reddit On the first screen, we’ll ask the user for 3 topics they’re interested in. We’ll pull the list of topics from Reddit’s list of default front page . subreddits After the user makes a choice, we’ll show the list of posts from each of these 3 topics in a filterable list — all topics or just one of the 3. When the user clicks on a post in the list, we’ll show its contents. Setup Since we use React for the web (we might add React Native in a future post), our starting point will be , the official starter kit. We’ll also npm install , and . The result should be something like . Create React App redux react-redux redux-thunk this To get the boilerplate out of the way, let’s also quickly initialize the Redux store and hook up thunk middleware in : index.js The Flux circle of life in a Redux app One of the main things often missing from Redux tutorials is the grand picture and where Redux fits in. Redux is an implementation of the architecture — a pattern for passing data around in a React app. Flux Under classic Flux, app state is held within . Dispatched cause this state to change, afterwhich the that listen to these state changes will re-render themselves accordingly: stores actions views Flux simplifies life by making data flow in a direction. This reduces the as the codebase grows and becomes more complex. single spaghetti effect One of the difficulties with understanding Redux is the plethora of unintuitive terms like , and . It’s easier to see where they all fit in by placing them on the Flux diagram. These are simply the technical names of the various Redux constructs that implement the different parts of the cycle: reducers selectors thunks As you’ve probably noticed, other terms from the Redux ecosystem like and , are absent. This is intentional as they won’t play a significant role in our workflow. middlewares sagas Project directory structure We will organize our code according to the following top-level directory structure under : /src “Dumb” React components that have no knowledge of Redux /src/components “Smart” React components that are to our Redux store /src/containers connected Abstraction facades for external API (like backend servers) /src/services All Redux-specific code goes here, including all of our app /src/store business-logic The directory is organized by , each containing: store domain Reducer as a default export with all selectors as named exports /src/store/{domain}/reducer.js All the domain action handlers (thunks and plain object creators) /src/store/{domain}/actions.js A state-first approach Our app has two screens, we’re going to start with the first and let the user choose exactly 3 topics. We can begin implementing any point of the Flux cycle, but I’ve discovered it’s usually easiest for me to start with the . State So what does our screen need? app state topics We’ll need to hold the list of topics retrieved from the server. We’ll also need to hold the ID’s of the topics chosen by the user so far (max 3). It will be nice to hold them in the order selected, so in case we already have 3 and another is chosen, we can simply drop the oldest one. How will we structure this app state? There’s a list of actionable tips in my previous post — “ ”. Following the tips, this would be an appropriate structure: Avoiding Accidental Complexity When Structuring Your App State The topic URL will serve as a unique ID. Where will we hold this state? In Redux, the is the construct that holds state and updates it. We will organize our code by , so the natural place for this reducer will be: reducer domains /src/store/topics/reducer.js There’s some boilerplate to create a reducer, you can see it . Notice that in order to enforce of our state (as required by Redux), I’ve chosen to use an immutability library called . here immutability seamless-immutable Our first scenario After modeling state, I like to take a user scenario and implement it from start to finish. In our case let’s create our screen and display some topics as soon as it shows up. This component will be connected to our reducer, meaning it’s a “smart” component that’s aware of Redux. We’ll place it in topics /src/containers/TopicsScreen.js There’s some boilerplate to create a connected component, you can see it . Let’s also display it as the content of our . Now, when everything is set up, we can fetch some topics. here App component Rule: Smart components are not allowed to have any logic except dispatching actions. The scenario starts on the view’s . Since can’t run logic directly on the view, we will dispatch an action that will fetch the topics. This action is asynchronous of course, so it will be a : componentDidMount thunk In order to abstract the Reddit server API, we’ll create a new that does the actual network fetch. Its methods will be asynchronous so we can for the response. In general, I love the so much that my code hasn’t seen a direct use of in a long time. service await async await API promises The returns an , but our state structure stores the topics in a . The action body is a good place to do the conversion. In order to actually store the data in the state, we must invoke our reducer by dispatching a standard plain object action — . service array map TOPICS_FETCHED The full source for this stage is available . here A few words about services Services are used to abstract external API — in many cases server API like the one by Reddit. The benefit of this abstraction layer is that API’s change and we want to decouple our code as much as possible from them. If in the future Reddit decides to rename endpoints or change field names, we can hopefully contain the impact on our app to the service alone. provided Rule: Services must be completely stateless. This is a tricky rule in our methodology. Imagine what would happen if our Reddit API required login. We might be tempted to hold this login state in the by instantiating it with the login details. service This isn’t allowed in our methodology because all app state must be contained in the . Holding state in a would be a state leak. The acceptable approach in this case would be to provide every service function with login information as argument and hold the login state in one of our . store service reducers Implementing the service is fairly straightforward, you can see it . here Completing the scenario — reducer and view The plain object action arrives at our reducer and contains the freshly fetched as parameter. Our reducer doesn’t need to do much except save this data on the state: TOPICS_FETCHED topicsByUrl Notice the usage of to make this immutable change explicit and straightforward. Immutability libraries are of course optional, I prefer their syntactic sugar to . seamless-immutable object spread tricks After the state updates, our view needs to re-render. This means the view needs to listen on the part of the state it cares about. This is done with : mapStateToProps I decided that our view will render the list of topics using a separate component that takes a map and a (inspired by ). I’m using to prepare these two props in (they will later be passed on directly to the ). The two props can be derived from our state. Notice something interesting, I don’t access the state directly.. ListView rowsById rowsIdArray React Native mapStateToProps TopicsScreen ListView Rule: Smart components should always access state through selectors. are one of the most important constructs in Redux that people tend to overlook. A selector is a pure function that takes the global state as argument and returns some transformation over it. Selectors are tightly coupled to and are located inside . They allow us to perform a few calculations on data before it’s being consumed by the view. In our methodology, we take this idea even further. Every time anyone needs to access part of the state (like in ), they need to go through a selector. Selectors reducers reducer.js mapStatetoProps Why? The idea is to encapsulate the internal structure of the app state and hide it from views. Imagine that we decide later on to change the internal state structure. We wouldn’t want to go over all the views in our app and refactor them. Passing through a selector will allow us to confine the refactoring to the only. reducer This is what does our look like: topics/reducer.js The entire current state of our app, including , can be seen . ListView here A few words about “dumb” components is a good example of a “dumb” component. It is not connected to the store nor aware of Redux at all. Unlike the “smart” connected components that are located in , these components are located in ListView /src/containers /src/components “Dumb” components receive data from their parents through props and may hold local component state. Assume you’re implementing a component from scratch. The blinking caret position is an excellent example for local component state that should not find its way into your global app state. TextInput So when do we need to move from a “smart” component to a “dumb” one? Rule: Minimize view logic in smart components by extracting it into dumb components. If you look at the implementation of , you will see it contains view logic like iterating over rows. We want to avoid having this logic in our smart component. This keeps our smart components as wirings only. Another benefit is that the logic is now reusable. ListView TopicsScreen ListView Next scenario — multiple topic selection We’ve completed our first scenario. Let’s move on to the next one — having the user select exactly 3 topics from the list. Our scenario starts with the user clicking on one of the topics. This event is handled by but since this smart component cannot contain any business logic, we’ll dispatch a new action — . This action will also be a , placed in . As you can see, almost every action we export (to be dispatched by views) is a . We usually only dispatch plain object actions from within a thunk in order to update the reducer state. TopicsScreen selectTopic thunk topics/actions.js thunk An interesting aspect about this thunk is that it needs to access the state. Notice how we keep the rule that every state access goes through a selector even here (although some may claim it’s going a bit too far). We’ll have to update the reducer to handle the action and store the new selected topics. There’s an interesting question whether needs to be a at all. Alternatively, we could make a plain object action and move this business logic to the reducer itself. This is a valid strategy. Personally I prefer to keep the business logic in thunks. TOPICS_SELECTED selectTopic thunk selectTopic Once the state updates, we need to propagate the topic selection back to our view. This means adding the selected topics in . Since the view needs to query whether every is selected or not, it is more convenient to pass this data to the view as a map. Since the data has to go through a anyways, this will be a great place to do the transformation. mapStateToProps rowId selector After implementing the above, and refactoring the background color change due to row selection into a new dumb component — — our app looks like . ListRow this A few words about business logic One of the goals of a good methodology is achieving proper separation between views and business logic. Where was our business logic implemented so far? All business logic was implemented under Redux in the directory. Most of it was inside in and some it was inside in . This is actually an official rule: /src/store thunks actions.js selectors reducer.js Rule: Place all business logic inside action handlers (thunks), selectors and reducers. Navigating to the next screen — the posts list When we have more than one screen we need a way to navigate. This is usually achieved using a navigation component like . I want to deliberately avoid using a router in order to keep our example simple. Opinionated external dependencies like routers tend to draw attention away from the conceptual discussion of methodology. react-router Instead, let’s add a state variable , , telling us whether the user completed topic selection or not. Once the user selects 3 topics, we will display a button that once clicked — will finalize selection and move to the next screen. Clicking the button will dispatch an action that sets this state variable directly. selectionFinalized This is all fairly similar to what we’ve been doing so far, the only interesting part is knowing when to display the button (as soon as at least 3 topics are chosen). We may be tempted to add another state variable for this purpose, but this variable can actually be derived from data we already have in the state now. This means we should implement this business logic as a : selector The full implementation of the above is available . In order to do the actual screen switch, we’ll need to change into a connected component and have it listen on in its . The full implementation is available . here App selectionFinalized mapStateToProps here The posts screen — once again state first Since we’re now well experienced in the methodology, we can run through implementing the second screen a bit faster. This new screen deals with a new domain — . In order to make our app modular as possible, we’ll give this domain a separate and separate app state. posts reducer Reminder — the screen’s purpose is to display a list of posts that can be filtered according to topic. The user can click on a post in the list and see its content. Following our structuring , this would work: tips And our new reducer is . posts born First scenario — showing the posts list without filter As usual, when our state is modeled, we move to a simple user scenario and implement it from start to finish. Let’s start with showing the full post list without any filter applied. We need a new smart container to show the posts, we’ll call it and have it dispatch a new action called when it mounts. The action will be a under our new domain in PostsScreen fetchPosts thunk posts/actions.js This is very similar to what we did before, the implementation is . here At the end of the thunk we dispatch the plain action that carries the posts to the reducer. We’ll have to modify our reducer to store the data. In order to show the list in , we need to hook up its to a selector providing this part of the state. We can then display the list by reusing our component. POSTS_FETCHED PostsScreen mapStateToProps ListView Nothing new as well, the implementation is . here Next scenario — filter the post list This scenario starts with showing the user the available filters. We can pull this data from the reducer state using an existing selector. When a filter is changed, we will dispatch an action that will change it directly in the reducer. topics posts The interesting part is applying the filter to the post list. In our app state we currently hold all and the . We don’t want to hold the filtered result in the app state as well because it can be derived from them. Business logic for deriving data runs in right before arriving at the view in . Our selector therefore will be: postsById currentFilter selectors mapStateToProps The full implementation for this stage is available . here Last scenario — showing post details This scenario is actually the simplest one yet. We have an app state variable holding the . All we have to do is update it when the user clicks on a post in the list by dispatching an action. needs this state variable in order to show the post details, which means we’ll need a to drive it in . currentPostId PostsScreen selector mapStateToProps Take a look at the detailed implementation . here And we’re done! This also wraps up the implementation of our entire example app. The full source code of the app is available on GitHub: https://github.com/wix/react-dataflow-example Summary of our opinionated workflow rules App state is a first class citizen, structure it like an in-memory database. Smart components are not allowed to have any logic except dispatching actions. Smart components should always access state through selectors. Minimize view logic in smart components by extracting it into dumb components. Place all business logic inside action handlers (thunks), selectors and reducers. Services must be completely stateless. Remember, Redux offers a lot of room for personal style. There are many alternate workflows with different sets of rules. Some good friends of mine come to mind who prefer instead of and like to place all of the business logic inside only. redux-promise-middleware thunks reducers If you want to share a different methodology that works for you, feel free to PR your own implementation for the above project and we’ll provide it as a branch for comparison.