> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Managing data flow on the client-side
- URL: https://madewithlove.com/blog/managing-data-flow-on-the-client-side/
- Published: 2015-10-21T16:11:00.000Z
- Updated: 2026-06-02T12:21:55.000Z
- Author: Bram Devries
- Tags: Engineering, Architecture, Tutorials, JavaScript, Frontend

I like to think of client-side applications as empty shells that get populated with data and where the source of that data can be anything: an object in your javascript code, an HTTP request, a JSON file, …

Making sure this data flows correctly through your application is one hell of a job. As your application grows it becomes harder and harder to manage the different data sources and how they are used to display content on the screens of your users.

This blog post is filled with examples. The syntax used is ES7\. You can use [Babel](https://babeljs.io/docs/en/) to compile it.

## State vs. Props

On the last couple of projects we’ve been using [React](https://facebook.github.io/react/) to build interactive user interfaces. React components have the concept of **props** and **state** to respectively pass data down to child components or manage data internally. Here’s a simple component:

```
// index.js
import React, {Component} from 'react';
import {render} from 'react-dom';

class Counter extends Component {
  static defaultProps = {
    increase: 1
  }

  state = {
    count: 0
  }

  handleClick() {
    this.setState({
      count: this.state.count + this.props.increase,
    });
  }

  render() {
    return (
      <div>
        <p>The button has been clicked {this.state.count} times.</p>
        <button onClick={::this.handleClick}>+{this.props.increase}</button>
      </div>
    );
  }
}

// Usage
render(<Counter />, document.getElementById('app'));
```

Whenever we click the button it will increase the counter by 1\. The `Counter` component has a `count` state which keeps a record of the amount of times the button has been clicked. This state is internal, if I add 2 `Counter` components on a page each counter will start at 0 and they will increase separately.

This `Counter` component also contains a prop called `increase`. This allows us to set different increment values for each instance.

```
<Counter increase={2} />
```

The easiest way to think of **props** vs **state** is that the former can be seen as the initial configuration of a component, while the later is used to mutate the component over time.

A prop can be anything, in our `Counter` component we have a `<button>` with an `onClick` prop which contains a reference to `Counter.handleClick`.

## Flux

For small applications you can rely on props and state, but once those applications start getting complex you might feel the need for a more structured approach. This is where [Flux](https://github.com/facebookarchive/flux) comes in.

Flux is a pattern designed by Facebook, it favors a uni-directional data flow and one-way data bindings.

![Managing data flow on the client-side](https://madewithlove.com/blog/content/images/2026/05/flux-1024x310.png)

With Flux all data of a particular domain is kept in a **store**. A store has to register itself with the **dispatcher**. When an **action** is triggered by the dispatcher a callback will be executed on the store. The store makes the **view** aware of the new data by using an `onChange` event. The view then updates it’s own internal state which causes a re-render of the child views.

There are a lot of implementations of this pattern. Our favorite is [Redux](http://redux.js.org/), which isn’t really considered an implementation of the Flux pattern. It took the best parts of Flux and [elm](http://elm-lang.org/).

For instance: Redux does not have the concept of a dispatcher, instead it relies on pure functions which brings it closer to a functional programming style. A second important difference is that Redux assumes your data is immutable, meaning you are always returning a new object or array.

Redux still uses actions to pass data from your components to your store, but we still need a way to define the changes happening to our state. This is where the **reducer** comes into play.

A reducer is a function that receives the previous state and the triggered action, and returns the next state.

There are a couple of things to keep in mind when writing a reducer:

- You should never mutate the arguments
- Add new data through an API call.
- Add a certain randomness (using `Math.random()` or `Date.now()`)

The rule of thumb for a reducer should be **Given the same arguments, it should calculate the next state and return it. No surprises**

The following steps are very similar to a typical Flux implementation. The store updates a controller view which then propagates the new state to its child components.

## Dumb vs. Smart components

Both Redux and Flux have the concept of dumb and smart components.

**Smart components** are wrappers around dumb components, they are also called controller views. These components are aware of Redux and subscribe to a store. A smart component generally does not output any DOM.

**Dumb components** are not aware of Redux at all. They receive their data trough props via smart components and do output DOM.

[The creator of Redux](https://github.com/gaearon) wrote a very nice [article](https://medium.com/@dan%5Fabramov/smart-and-dumb-components-7ca2f9a7c7d0) about how he handles smart and dumb components.

If you know something about design patterns this might sound familiar to you. Flux shares similarities with the [commander pattern](https://en.wikipedia.org/wiki/Command%5Fpattern). Each action can be considered as a command and a reducer is the command handler.

## In practice

Let’s rewrite our earlier counter example to a Redux application step by step.

### Setup

Redux requires some boilerplate setup:

```
npm install --save redux react-redux redux-thunk
```

The first thing we will do is create our reducer:

```
// Reducers/Counter.js
export default function counter(state = 0, action) {
  return state;
}
```

Our initial reducer is very simple. It’s just a function that receives a `state` and `action`. For now we will let the reducer return the state. We also define a default state of 0.

```
// Reducers/index.js
import {combineReducers} from 'redux';
import counter from './Counter';

export default combineReducers({
  counter
});
```

This is some boilerplate Redux code. Typically each store will have its own reducers, but do note that an action will get passed down to every reducer. This allows you to handle a certain action differently. A good example would be a search component. You can manage the filtering of the results based on the query in one reducer, and update the search input in another.

The next step is creating our store:

```
// Stores/index.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../Reducers';

const createStoreWithMiddleware = applyMiddleware(
  thunk
)(createStore);

export default function store(initialState) {
  return createStoreWithMiddleware(reducers, initialState);
}
```

Again, this is some boilerplate code. In this step we’re essentially linking our store with our reducer.

Middlewares can be used for logging. By adding a logging middleware to all reducers, one can log all your application’s actions. Another example is a middleware for error reporting.

We’re also adding the [redux-thunk](https://github.com/gaearon/redux-thunk) middleware. This package allows you to execute async actions. If for some reason we decide to sync our counter to the server over an API we will need to make use of this.

A more [advanced use-case](http://redux.js.org/docs/advanced/AsyncActions.html) of async actions is to dispatch other actions in another one. For example, when performing an API request our UI needs to be updated with a loading indicator before the request is started.

We also need to create our controller view:

```
// Containers/App.js
import React, {Component} from 'react';
import Counter from '../components/Counter';

export default class App extends Component {
  render() {
    return (
      <Counter />
    );
  }
}
```

And then finally we modify our bootstrap file so that it renders the `App` top level component instead of the `Counter` component.

![Redux devtools](https://madewithlove.com/blog/content/images/2026/05/redux-devtools-1024x650.png)

```
// index.js
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import createStore from './Stores';
import App from './Containers/App';

const store = createStore();

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('app'));
```

Our structure after setup looks like this:

```
├── Components
│   └── Counter.js
├── Containers
│   └── App.js
├── Reducers
│   ├── Counter.js
│   └── index.js
├── Stores
│   └── index.js
└── index.js
```

### Connecting to a store

You might be wondering how we can connect our `App` top level component to our store. There’s two ways to do this, both very simple:

```
// Containers/App.js
import React, {Component} from 'react';
import {connect } from 'react-redux';
import Counter from '../components/Counter';

@connect(state => ({
  count: state.counter
}))
export default class App extends Component {
  render() {
    return (
      <Counter count={this.props.count} />
    );
  }
}
```

In this example we wrap our top level component with a `connect` decorator. We pass a callback that receives the `state`, this state is an object that contains the previously created `Counter` reducer. We return an object in which we map the reducer’s state to a `count` value. Our `App` component will receive this `count` via its props, which we can then pass to our “dumb” `Counter` component.

Now that our `Counter` component is using the data in our store we can make some changes to it:

```
// Components/Counter.js
import React, {Component} from 'react';

export default class Counter extends Component {
  static defaultProps = {
    increase: 1
  }

  render() {
    const {count, increase} = this.props;

    return (
      <div>
        <p>The button has been clicked {count} times.</p>
        <button>+{increase}</button>
      </div>
    );
  }
}
```

We got rid of the internally managed state and are now using the `count` passed via the props.

### Adding interactivity

Our last step is to add interactivity to our counter. Like I mentioned before Flux (and Redux): use actions to modify the state. An action is a simple javascript object. So for our counter it could be as simple as this:

```
{
  type: 'increment',
  amount: 1,
};
```

Now obviously this is kind of static and we can’t really do much with it yet, we need to have a way to trigger these actions. This is where action creators come in. These are simply functions that return an action. Like this:

```
// Actions/Counter.js
import Api from '../Api';

export function increment(amount = 1) {
  return {
    type: 'increment',
    amount,
  };
};

export function sync(count) {
  return dispatch => {
      Api.sync(count).then(() => {
        dispatch({
          type: 'sync',
        });
      });
  };
}
```

Then we need to make our application aware of the action. We can do that like this:

```
// Containers/App.js
import React, {Component} from 'react';
import {connect } from 'react-redux';
import Counter from '../components/Counter';
import {increment} from '../Actions/Counter';

@connect(state => ({
  count: state.counter
}))
export default class App extends Component {
  render() {
    const {dispatch} = this.props;

    return (
      <Counter
        count={this.props.count}
        increment={amount =>
          dispatch(increment(amount))
        }
      />
    );
  }
}
```

For small apps this works fine, but imagine having a lot of actions. Managing that would become a nightmare. Luckily Redux has a helper that solves this for us, called `bindActionCreators`.

```
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect } from 'react-redux';
import Counter from '../components/Counter';
import * as actions from '../actions/counter';

@connect(state => ({
  count: state.counter
}))
export default class App extends Component {
  render() {
    return (
      <Counter count={this.props.count} actions={bindActionCreators(actions, this.props.dispatch)}/>
    );
  }
}
```

This will bind all our action creators to the dispatcher and expose them via an object. Our `Counter` component will now receive an `actions` object via it’s props containing the `increment` action creator. Now all that is left to do is trigger the action when clicking the button:

```
// Components/Counter.js
import React, {Component} from 'react';

export default class Counter extends Component {
  static defaultProps = {
    increase: 1
  }

  render() {
    const {count, increase, actions: {increment}} = this.props;
    return (
      <div>
        <p>The button has been clicked {count} times.</p>
        <button onClick={() => increment(increase)}>+{increase}</button>
      </div>
    );
  }
}
```

And then finally we need to add this action to our reducer:

```
// Reducers/Counter.js
export default function counter(state = 0, action) {
  switch(action.type) {
    case 'increment':
      return state + action.amount;
    default:
      return state;
  }
}
```

And that’s it, we now have an application where the state is kept in a single source that can only be mutated by triggering actions.

## Benefits of Redux

Because we use Redux we can now use some very nice tools that improve our development.

### Redux devtools

One of those tools is [redux-devtools](https://github.com/gaearon/redux-devtools) which gives you a visual overview of all the actions happening in your application. Adding it to our application requires some minor changes:

```
npm install --save-dev redux-devtools
```

First of all we need to adapt our store:

```
// Stores/index.js
import {compose, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import {devTools} from 'redux-devtools';
import reducers from '../Reducers';

export default function store(initialState) {
  let middlewares = [
    applyMiddleware(thunk),
    devTools(),
  ];

  const store = compose(...middlewares)(createStore);

  return store(reducers, initialState);
}
```

You can see how we added the `devTools` middleware to our stack.

The devtools come with their own React component which we can include, we will create a new top level component for this.

```
// Containers/ReduxDevtools
import React from 'react';
import {DevTools, DebugPanel, LogMonitor} from 'redux-devtools/lib/react';

export default function devtools(store) {
  const stateSelector = state => state;

  return (
    <DebugPanel top right bottom key="devtools">
      <DevTools select={stateSelector} store={store} monitor={LogMonitor} />
    </DebugPanel>
  );
}
```

And finally we need to render the devtools in our browser:

```
// index.js
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import createStore from './Stores';
import App from './Containers/App';
import devtools from './Containers/ReduxDevtools';

const store = createStore();

render(
  <div>
    <Provider store={store}>
      <App />
    </Provider>
    {devtools(store)}
  </div>,
  document.getElementById('app'));
```

We can now track all the actions with their payloads and how they affect the state in this monitor. Do note that you should disable this in your production environment, since it impacts the performance. Our favorite way of doing this is by using environment variables with [webpack](https://madewithlove.com/blog/webpack-your-bags/).

---

All of the above examples are available on Github:

- [React](https://github.com/madewithlove/redux-example/tree/react)
- [Redux](https://github.com/madewithlove/redux-example/tree/redux)
- [Redux-devtools](https://github.com/madewithlove/redux-example/tree/redux-devtools)