Getting Started With Redux-Saga

Getting Started With Redux-Saga

Redux-Saga is a Redux middleware which makes application side effects more manageable and testable. Switching from thunk to Saga was not that straight forward at first as it introduced some new concepts and patterns. So this article is for people who are doing the same or at least have a solid understanding of how react works with Redux.

Redux saga is a redux middleware which makes application side effects more manageable and testable. These side effects in simpler terms are just apis for data fetching or accessing browser cache which are unavoidable important parts of every app.

I used another popular middleware called Thunk for handling these kind of side effects and it did make my life easy. Switching from thunk to saga was not that straight forward at first as it introduced some new concepts and patterns. So this article is for people who are doing the same or at least have a solid understanding of how react works with redux. I will try to make basic redux saga concepts simpler to understand as a lot of well written and detailed articles on the web are a bit daunting to begin with.

By default, In a plain redux store only synchronous updates are possible with actions that dispatch objects. For asynchronous updates, we need middlewares like Saga or Thunk.

Thunk cleverly allows us to dispatch functions instead of objects which handle our async code and then eventually dispatch objects to the store. I enjoyed this approach until some of our action creators were huge with a lot of promise chains. Our actions became difficult to test and understand as they were no longer pure.

Sagas setup a generator function which intercepts the dispatched action object, pauses and does a network request, waits for it to complete and when done dispatches another action with the desired data. I know its a pretty long statement but I hope it will be clear in a few minutes. Let’s start with generators.

Generators are functions with a star (asterisk) attached to them and were part of Javascript ecosystem since Es6. In a generator function, the function execution can be delayed. They don’t run to completion after they are called like normal functions do but instead can stop midway and continue and stop again and continue again. These are normally built with yield expressions, where each yield is a stopping point. The syntax kind of looks like async/await where a async function can have multiple awaits.

Let’s look at our first saga example where we are fetching a list of authors and saving them to our store.

export function fetchAuthors() {
  return {
    type: 'FETCH_AUTHORS'
  }
}

export function saveAuthorsToList(authors) {
  return {
    type: 'SAVE_AUTHORS_TO_LIST',
    payload: authors
  }
}

With Thunk we would have a single function which would handle our asynchronous fetching of authors and then would save that data to the store using a single action object. But with saga we will do it in a different way.

Here we have two functions defined called fetchAuthors and saveAuthorsToList. Instead of having a single big function we are breaking it into two simple functions which are our action creators. These action creators dispatch pure redux actions which can be easily tested.

Now we need our redux saga code which will look something like this.

import { call, put, takeEvery } from 'redux-saga/effects';

import { saveAuthorsToList } from './actions';
import Api from './apis';

function* fetchAuthorsFromApi() {
  yield takeEvery('FETCH_AUTHORS', makeAuthorsApiRequest);
}

function* makeAuthorsApiRequest(){
  const authors = yield call(Api.requestAuthors);
  yield put(saveAuthorsToList(authors));
}

export default fetchAuthorsFromApi();

You can see two star attached functions in the above example, fetchAuthorsFromApi and makeAuthorsApiRequest, which are our generator functions. These are our wizards and handle all saga magic

The first generator, fetchAuthorsFromApi has one yield statement so this function has one stopping point where it pauses before its completion. It uses a predefined redux saga effect called takeEvery.

takeEvery calls a generator function whenever a dispatched action matches the specified pattern or type.

Our generator function fetchAuthorsFromApi runs in the background and the statement

yield takeEvery(‘FETCH_AUTHORS’, makeAuthorsApiRequest)

will intercept every action dispatched with type FETCH_AUTHORS and calls another generator function called makeAuthorsApiRequest which will handle our async api call. HenceFETCH_AUTHORS type action won’t be handled in a reducer like other normal actions and instead will be intercepted by our saga.

Inside makeAuthorsApiRequest generator we have two yield statements. The first one,

const authors = yield call(Api.requestAuthors)

uses another saga effect called call which like its name, calls a function with or without arguments. In our case it calls a function which has an api request to fetch our authors. It is better if we have a separate file for all our functions with api calls. The generator function’s execution here is paused until our api call finishes and we have our response saved in authors.

Now after we have our authors data the first yield state is done and our second yield statement gets executed which is

yield put(saveAuthorsToList(authors))

This statement uses another predefined redux saga effect called put which is used to dispatch our redux actions. So we call our second action creator saveAuthorsToList with the desired payload which here is our authors data.

So the saga magic ends here and our normal redux flow continues. We will have a reducer to handle SAVE_AUTHORS_TO_LIST action in the store which will take the authors payload and returns the state.

We can also handle any errors that our api call can return by wrapping our yield call in traditional try /catch blocks. We can dispatch a separate action to handle our error in the user state using the same put effect.

import { call, put, takeEvery } from 'redux-saga/effects';

import { saveAuthorsToList } from './actions';
import Api from './apis';

function* fetchAuthorsFromApi() {
  yield takeEvery('FETCH_AUTHORS', makeAuthorsApiRequest);
}

function* makeAuthorsApiRequest(){
  try {
    const authors = yield call(Api.requestAuthors);
    yield put(saveAuthorsToList(authors));  
  } catch (err) {
    yield put(saveAuthorToListError();
  }
}

export default fetchAuthorsFromApi();

Our store.js file is where we connect our redux-saga middleware to the store. This is an example from the official redux-saga repository.

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import mySaga from './saga';

// create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// mount it on the Store with our reducer
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
);

// then run the saga
sagaMiddleware.run(mySaga);

Lastly hooking up saga to our store is pretty straight forward like using any other middleware. We have an extra step where we run or kickstart our saga using sagaMiddleware.run() After this our saga generator function will start running and lay low, silently waiting for any actions that need some saga magic.

So I have briefly explained one of the most common scenarios where a saga can be used. I have talked about basic but important saga effects like takeEvery, call and put . There are many other useful effects and advanced concepts as well that you can read from the official documentation. I hope this article gets you started with saga and helps you understand those other advanced concepts.

Thank you for reading 😀🙏

Why React Hooks + Context API a better choice than React + Redux

Why React Hooks + Context API a better choice than React + Redux

In this React tutorial, I will introduce the React Context API for state management, and show you what makes React Hooks plus the Context API a better solution than React plus Redux

Redux introduces a lot of complexity to our codebase with the excessive amount of code it requires. At best, this makes it an imperfect solution for state management in React applications. And yet, far too many React developers default to Redux for state management without considering other alternatives.

In this article, I will introduce the React Context API for state management, and show you what makes React Hooks plus the Context API a better solution than Redux.

Why we need a state management tool

In typical React, the way to handle data between disconnected components is through prop drilling. Since there is no global state that components can access if, for instance, you want to pass data from a top-level component to a fifth-level component, you’ll have to pass the data as a prop on each level of the tree until you get to your desired component.

This results in writing a ton of extra code, and giving components properties that they will never use also affects their architectural design. In order to solve this problem, we needed a way to provide a global state that all components, no matter how deeply nested they are, could access.

By solving this, Redux, an open-source JavaScript library for managing application state, became the go-to solution for React developers.

How Redux works

The Redux documentation describes it as a predictable state container for JavaScript applications that helps us to write applications that behave consistently, run in different environments, and are easy to test.

One disadvantage of prop drilling is the need for writing a considerable amount of extra code in order to access data from a top-level component. With Redux, this disadvantage is felt even more as a lot of complexity comes with all its extra code required for setting up a global state for our application. Redux requires three main building parts to function: actions, reducers, and store.

Actions

These are objects that are used to send data to the Redux store. They typically have two properties: a type property for describing what the action does and a payload property that contains the information that should be changed in the app state.

// action.js
const reduxAction = payload => {
  return {
    type: 'action description',
    payload
  }
};

export default reduxAction;

The type is usually in all caps, with its words separated by underscores. For example, SIGNUP_USER or DELETE_USER_DATA.

Reducers

These are pure functions that implement the action behavior. They take the current application state, perform an action, and then return a new state:

const reducer = (state, action) => {
  const { type, payload } = action;
  switch(type){
    case "action type":
      return {
        ["action description"]: payload
      };
    default:
      return state;
  }
};

export default reducer;

Store

The store is where the application’s state is housed. There is only one store in any Redux application:

import { createStore } from 'redux'

const store = createStore(componentName);

Since our application can only have one Redux store, in order to create a single root reducer for all our components, we’ll need the [combineReducers](https://redux.js.org/api/combinereducers) method from Redux.

With this long process and considerable amount of code required to set up Redux, imagine what our codebase will look like when we have multiple components to work with. Even though Redux solves our state management problem, it is really time-consuming to use, has a difficult learning curve, and introduces a whole new layer of complexity to our application.

Fortunately, the React Context API solves this problem. When combined with React Hooks, we have a state management solution that is less time-consuming to set up, has an easy learning curve, and requires minimal code.

The React Context API

The new Context API came with React 16.3. Here’s how Context is explained in the React documentation:

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

The React context API is React’s way of managing state in multiple components that are not directly connected.

To create a context, we’ll use the createContext method from React, which accepts a parameter for its default value:

import React from 'react';

const newContext = React.createContext({ color: 'black' });

The createContext method returns an object with a Provider and a Consumer component:

const { Provider, Consumer } = newContext;

The Provider component is what makes the state available to all child components, no matter how deeply nested they are within the component hierarchy. The Provider component receives a value prop. This is where we’ll pass our current value:

<Provider value={color: 'blue'}>
  {children}
</Provider>

The Consumer, as its name implies, consumes the data from the Provider without any need for prop drilling:

<Consumer>
  {value => <span>{value}</span>}}
</Consumer>

Without Hooks, the Context API might not seem like much when compared to Redux, but combined with the useReducer Hook, we have a solution that finally solves the state management problem.

What are Hooks in React?

Hooks are a type of function that enables the execution of custom code in a base code. In React, Hooks are special functions that allow us to “hook into” its core features.

React Hooks provide an alternative to writing class-based components by allowing us to easily handle state management from functional components.

The useContext Hook

If you noticed, when explaining the React Context API, we needed to wrap our content in a Consumer component and then pass a function as a child just so we could access (or consume) our state. This introduces unnecessary component nesting and increases the complexity of our code.

The useContext Hook makes things a lot nicer and straightforward. In order to access our state using it, all we need to do is call it with our created context as its argument:

const newContext = React.createContext({ color: 'black' });

const value = useContext(newContext);

console.log(value); // this will return { color: 'black' }

Now, instead of wrapping our content in a Consumer component, we can simply access our state through the value variable.

The useReducer Hook

The useReducer Hook came with React 16.7.0. Just like the reduce() method in JavaScript, the useReducer Hook receives two values as its argument — in this case, the current state and an action — and then returns a new state:

const [state, dispatch] = useReducer((state, action) => {
  const { type } = action;
  switch(action) {
    case 'action description':
      const newState = // do something with the action
      return newState;
    default:
      throw new Error()
  }
}, []);

In the above block, we’ve defined our state and a corresponding method, dispatch, handling it. When we call the dispatch method, the useReducer() Hook will perform an action based on the type that our method receives in its action argument:

...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)
The useReducer Hook plus the Context API

Setting up our store

Now that we know how the Context API and the useReducer Hook work individually, let’s see what happens when we combine them in order to get the ideal global state management solution for our application. We’ll create our global state in a store.js file:

// store.js
import React, {createContext, useReducer} from 'react';

const initialState = {};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ( { children } ) => {
  const [state, dispatch] = useReducer((state, action) => {
    switch(action.type) {
      case 'action description':
        const newState = // do something with the action
        return newState;
      default:
        throw new Error();
    };
  }, initialState);

  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }

In our store.js file, we used the createContext() method from React that we explained earlier to create a new context. Remember that the createContext() method returns an object with a Provider and Consumer component. This time, we’ll be using only the Provider component and then the useContext Hook when we need to access our state.

Notice how we used the useReducer Hook in our StateProvider. When we need to manipulate our state, we’ll call the dispatch method and pass in an object with the desired type as its argument.

In our StateProvider, we returned our Provider component with a value prop of state and dispatch from the useReducer Hook.

Accessing our state globally

In order to access our state globally, we’ll need to wrap our root <App/> component in our StoreProvider before rendering it in our ReactDOM.render() function:

// root index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { StateProvider } from './store.js';

const app = (
  <StateProvider>
    <App />
  </StateProvider>
);

ReactDOM.render(app, document.getElementById('root'));

Now, our store context can be accessed from any component in the component tree. To do this, we’ll import the useContext Hook from react and the store from our ./store.js file:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  console.log(globalState); // this will return { color: red }
};

Adding and removing data from our state

We’ve seen how we can access our global state. In order to add and remove data from our state, we’ll need the dispatch method from our store context. We only need to call the dispatch method and pass in an object with type (the action description as defined in our StateProvider component) as its parameter:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  const { dispatch } = globalState;

  dispatch({ type: 'action description' })
};
Conclusion

To a good extent, Redux works for state management in React applications and has a few advantages, but its verbosity makes it really difficult to pick up, and the ton of extra code needed to get it working in our application introduces a lot of unnecessary complexity.

On the other hand, with the useContext API and React Hooks, there is no need to install external libraries or add a bunch of files and folders in order to get our app working. This makes it a much simpler, more straightforward way to handle global state management in React applications.

React.js & Redux Crash Course for Beginners - Full Tutorial

React.js & Redux Crash Course for Beginners - Full Tutorial

In this React.js & Redux crash course you will learn what React.js is and the fundamentals such as components, state, props, JSX, events, etc. We will talk about what Redux is and build a React app from scratch and add all of the boilerplate for Redux and work with the store/state, actions, reducers and all of the other fundamentals of the Redux state manager.

React.js Crash Course for Beginners - Full Tutorial

In this crash course you will learn what React JS is and the fundamentals such as components, state, props, JSX, events, etc.

Redux Crash Course for Beginners - Full Tutorial

In this video we will talk about what Redux is and build a React app from scratch and add all of the boilerplate for Redux and work with the store/state, actions, reducers and all of the other fundamentals of the Redux state manager. The actual Redux implementation starts around 22:00.

Understanding React with Redux and Redux Thunk for Beginners

Understanding React with Redux and Redux Thunk for Beginners

Introduction

Redux, according to the offical docs, is a predictable state container for JavaScript apps written by Dan Abramov. It’s a lightweight implementation of Flux, which is another library for managing the state. Basically Redux took the ideas that Flux brought in, leaving out its complexity by “borrowing” things from Elm.

For starters, there are several key concepts to understand: store, actions / action creators, and reducers. The official documentation is pretty straightforward and also plenty of examples and nice analogies can be found on the internet.

Principles

Redux has three fundamental principles:

  • single source of truth

The whole state of the application is stored in an object tree (within a single store). Visualize the state as a “model”, but without setters. As a plus, a single state tree enables us to debug our application with ease.

  • state is read-only

In order to modify state in Redux, actions have to be dispatched. Actions are a plain JavaScript object that describe what changed, sending data from the application to the store.

An action will look like this:

{
      type: 'ACTION_TYPE',
      action_value: string
  }

  • changes are made with pure functions

In order to tie state and actions together, we write a function called a reducer that takes two parameters: the (soon to be previous) state and an action. This pure function applies the action to that state and returns the desired next state.

Example of a reducer:

 export function reducer(state = '', action) {
      switch (action.type) {
          case 'ACTION_TYPE':
              return action.action_value;
          default:
              return state;
      }
  } 

Important: Reducers do not store state, and they do not mutate state. You pass state to the reducer and the reducer will return state.

Tip: As a best practice, even though it’s possible to have a single reducer that manages the transformation done by every action, it is better to use reducer composition - breaking down the reducer into multiple, smaller reducers, each of them handling a specific slice of the application state.

How it works

When one action is dispatched to the store, the combined reducer catches the action and sends it to each of the smaller reducers. Each smaller reducer examines what action was passed and dictates if and how to modify that part of state. You will find an example of a combined reducer a bit later in the article.

After each smaller reducer produces its corresponding next state, an updated state object will be saved in the store. Because this is important, I’m mentioning again that the store is the single source of truth in our application. Therefore, when each action is run through the reducers, a new state is produced and saved in the store.

Besides all of this, Redux comes up with another concept, action creators, which are functions that return actions. These can be linked to React components and when interacting with your application, the action creators are invoked (for example in one of the lifecycle methods) and create new actions that get dispatched to the store.

 export function actionCreator(bool) {
        return {
            type: 'ACTION_TYPE',
            action_value: bool
        };
    }

Fetching data from an API

Now onto our application. All of the above code snippets were just examples. We will now dive into the important bits of the code of our app. Also, a github repo will be available at the end of the article, containing the entire app.

Our app will fetch (asynchronously) data that is retrieved by an API - assuming we already built and deployed a working API, how convenient :) - and then display the fetched data as nice as my UI design skills go (not too far).

TVmaze’s public API contains tonnes of data and we will fetch all the shows they have ever aired. Then, the app will display all the shows, toghether with their rating and premiere date.

Designing our state

In order for this application to work properly, our state needs to have 3 properties: isLoading, hasError and items. So we will have one action creator for each property and an extra action creator where we will fetch the data and call the other 3 action creators based on the status of our request to the API.

Action creators

Let’s have a look at the first 3 action creators

export function itemsHaveError(bool) {
        return {
            type: 'ITEMS_HAVE_ERROR',
            hasError: bool
        };
    }

    export function itemsAreLoading(bool) {
        return {
            type: 'ITEMS_ARE_LOADING',
            isLoading: bool
        };
    }

    export function itemsFetchDataSuccess(items) {
        return {
            type: 'ITEMS_FETCH_DATA_SUCCESS',
            items
        };
    }

The first 2 action creators will receive a bool as a parameter and they will return an object with that bool value and the corresponding type.

The last one will be called after the fetching was successful and will receive the fetched items as an parameter. This action creator will return an object with a property called items that will receive as value the array of items which were passed as an argument. Instead if items: items, we can just write items, using an ES6 syntactic sugar called property shorthand.

To visualize a bit what was described earlier, this is how it looks in Redux DevTools:

Out of the box, action creators can return just actions. That’s where Redux Thunk comes in handy. Thunk allows us to have action creators that return a function instead of an action and dispatch an action only in certain cases.

If it wasn’t for Redux Thunk, we would probably end up having just one action creator, something like this:

export function itemsFetchData(url) {
        const items = axios.get(url);

        return {
            type: 'ITEMS_FETCH_DATA',
            items
        };
    }

Obviously, it would be a lot harder in this scenario to know if the items are still loading or checking if we have an error.

Knowing these and using Redux Thunk, our action creator will be:

 export function itemsFetchData(url) {
        return (dispatch) => {
            dispatch(itemsAreLoading(true));

            axios.get(url)
                .then((response) => {
                    if (response.status !== 200) {
                        throw Error(response.statusText);
                    }

                    dispatch(itemsAreLoading(false));

                    return response;
                })
                .then((response) => dispatch(itemsFetchDataSuccess(response.data)))
                .catch(() => dispatch(itemsHaveError(true)));
        };
    }

Reducers

Now that we have our action creators in place, let’s start writing our reducers.

All reducers will be called when an action is dispatched. Because of this, we are returning the original state in each of our reducers. When the action type matches, the reducer does what it has to do and returns a new slice of state. If not, the reducer returns the original state back.

Each reducer takes 2 parameters: the (soon to be previous) slice of state and an action object:

export function itemsHaveError(state = false, action) {
        switch (action.type) {
            case 'ITEMS_HAVE_ERROR':
                return action.hasError;
            default:
                return state;
        }
    }

    export function itemsAreLoading(state = false, action) {
        switch (action.type) {
            case 'ITEMS_ARE_LOADING':
                return action.isLoading;
            default:
                return state;
        }
    }

    export function items(state = [], action) {
        switch (action.type) {
            case 'ITEMS_FETCH_DATA_SUCCESS':
                return action.items;
            default:
                return state;
        }
    }

Now that we have the reducers created, let’s combine them in our index.js from our reducers folder:

 import { combineReducers } from 'redux';
    import { items, itemsHaveError, itemsAreLoading } from './items';

    export default combineReducers({
        items,
        itemsHaveError,
        itemsAreLoading
    });

Creating the store

Don’t forget about including the Redux Thunk middleware in the configureStore.js:


 import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../reducers';

    export default function configureStore(initialState) {
        const composeEnhancers = 
            window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
                window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
                    // options like actionSanitizer, stateSanitizer
                }) : compose;

        const enhancer = composeEnhancers(
            applyMiddleware(thunk)
        );

        return createStore(
            rootReducer,
            initialState,
            enhancer
        );
    }

Using the store in our root index.js

 import React from 'react';
    import { render } from 'react-dom';
    import { Provider } from 'react-redux';
    import configureStore from './store/configureStore';

    import ItemList from './components/ItemList';

    const store = configureStore();

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

Writing our React component which shows the fetched data

Let’s start by talking about what we are importing here.

In order to work with Redux, we have to import connect from ‘react-redux’:

    import { connect } from 'react-redux';

Also, because we will fetch the data in this component, we will import our action creator that fetches data:

    import { itemsFetchData } from '../actions/items';

We are importing only this action creator, because this one also dispatches the other actions to the store.

Next step would be to map the state to the components’ props. For this, we will write a function that receives state and returns the props object.

const mapStateToProps = (state) => {
        return {
            items: state.items,
            hasError: state.itemsHaveError,
            isLoading: state.itemsAreLoading
        };
    };

When we have a new state, the props in our component will change according to our new state.

Also, we need to dispatch our imported action creator.

    const mapDispatchToProps = (dispatch) => {
        return {
            fetchData: (url) => dispatch(itemsFetchData(url))
        };
    };


With this one, we have access to our itemFetchData action creator through our props object. This way, we can call our action creator by doing this.props.fetchData(url);

Now, in order to make these methods actually do something, when we export our component, we have to pass these methods as arguments to connect. This connects our component to Redux.

    export default connect(mapStateToProps, mapDispatchToProps)(ItemList);

Finally, we will call this action creator in the componentDidMount lifecycle method:

    this.props.fetchData('http://api.tvmaze.com/shows');


Side note: if you are wondering why are we calling the action creator in componentDidMount instead of other lifecycle methods, I have found a couple of good reasons here:

  • You can’t guarantee the AJAX request won’t resolve before the component mounts. If it did, that would mean that you’d be trying to setState on an unmounted component, which not only won’t work, but React will yell at you for. Doing AJAX in componentDidMount will guarantee that there’s a component to update.

  • Fiber, the next implementation of React’s reconciliation algorithm, will have the ability to start and stop rendering as needed for performance benefits. One of the trade-offs of this is that componentWillMount, the other lifecycle event where it might make sense to make an AJAX request, will be “non-deterministic”. What this means is that React may start calling componentWillMount at various times whenever it feels like it needs to. This would obviously be a bad formula for AJAX requests.

Besides this, we need some validations:

    if (this.props.hasError) {
        return <p>Sorry! There was an error loading the items</p>;
    }

    if (this.props.isLoading) {
        return <p>Loading ...</p>;
    }


And the actual iteration over our fetched data array:

 {this.props.items.map((item) => (
        // display data here
    ))}

In the end, our component will look like this:

 import React, { Component, PropTypes } from 'react';
    import { connect } from 'react-redux';
    import { ListGroup, ListGroupItem } from 'react-bootstrap';
    import { itemsFetchData } from '../actions/items';

    class ItemList extends Component {
        componentDidMount() {
            this.props.fetchData('http://api.tvmaze.com/shows');
        }

        render() {
            if (this.props.hasError) {
                return <p>Sorry! There was an error loading the items</p>;
            }

            if (this.props.isLoading) {
                return <p>Loading ...</p>;
            }

            return (
                <div style={setMargin}>
                    {this.props.items.map((item) => (
                        <div key={item.id}>
                                <ListGroup style={setDistanceBetweenItems}>
                                    <ListGroupItem href={item.officialSite} header={item.name}>
                                        Rating: {item.rating.average}
                                        <span className="pull-xs-right">Premiered: {item.premiered}</span>
                                    </ListGroupItem>
                                </ListGroup>
                        </div>
                    ))}
                </div>
            );
        }
    }

    ItemList.propTypes = {
        fetchData: PropTypes.func.isRequired,
        items: PropTypes.array.isRequired,
        hasError: PropTypes.bool.isRequired,
        isLoading: PropTypes.bool.isRequired
    };

    const mapStateToProps = (state) => {
        return {
            items: state.items,
            hasError: state.itemsHaveError,
            isLoading: state.itemsAreLoading
        };
    };

    const mapDispatchToProps = (dispatch) => {
        return {
            fetchData: (url) => dispatch(itemsFetchData(url))
        };
    };

    export default connect(mapStateToProps, mapDispatchToProps)(ItemList);

And that was all !

Our app will look like this:

I wasn’t lying about my design skills, was I ? :)

Last words and other resources

We now have an app that is fetching data asynchronously from an API, using React for our UI and Redux for managing the state of our application. I think this is a good starting point for a personal / small project and also you get to work with new technologies.

This doesn’t mean that Redux is the solution for every problem we face when writing apps in React or that Redux is a a must-use in any Javascript written project, as Dan Abramov states in an interesting article.

Also worth noting, Facebook are preparing React Fiber, a reimplementation of React. They state that its goal is to make it more suitable for animations and gestures and that the key new feature will be incremental rendering which is:

  • the ability to split rendering work into chunks and spread it out over multiple frames