Tutorial

Build A Media Library with React, Redux, and Redux-saga - Part 1

Draft updated on Invalid Date
author

Rowland Ekemezie

Build A Media Library with React, Redux, and Redux-saga - Part 1

This tutorial is out of date and no longer maintained.

Introduction

Building applications with React can be overwhelming even after you’ve understood the elegant philosophy behind it. More so, managing large applications with React can be confusing at first. The ecosystem has grown with great libraries to save you some nightmares. But that also makes it difficult at first to figure out which library to use.

In this two-part tutorial, we’ll build and deploy a media library app. The application allows users to search and display images and short videos from external services (Flickr and Shutterstock). It would also allow users to select images and videos for preview.

We will build this application with:

  • React: core infrastructure
  • Redux: state management
  • Redux-saga: demystifying asynchronous processes

We will be using Yahoo’s Flickr API and ShutterStock API for images and short videos respectively.

This tutorial assumes you have a basic understanding of JavaScript and React. Don’t worry if you have none. We will walk through and build the application from the ground up.

What We’ll Build

Part 1 of this tutorial would cover basic React setup with create-react-app package, organizing our project workflow, defining routes, and of course testing it out.

In Part 2, we will be using Redux and its async libraries; we will set it up and then integrate it into our application. Finally, we will deploy our application to Heroku for sharing with our friends. Our application would look thus when we’re done.

Our app will be structured to allow you to either contribute to it or use it as a sample boilerplate for bootstrapping your React/Redux applications.

Project Setup

There are loads of React boilerplate out there to help you get started with React. But we’ll be using create-react-app authored by the Facebook team. It allows you to create React applications with no configuration. create-react-app provides developers with the benefits of a more complex setup out of the box.

Let’s get started…

First, install the package globally:

  1. npm install -g create-react-app

Then, create the media-library application:

  1. create-react-app media-library

Bam. Our React basic setup is complete with scripts to start, build, and eject. Take a look at your package.json.

Let’s test it out.

  1. cd media-library
  2. npm start

Now, we can structure our project directory and add other dependencies.

  1. npm install --save redux redux-saga react-router@2.4 react-redux

Then, remove the default sample app:

  1. rm -rf src/**

Project Directory

Media-library
    - public
        - favicon.ico
        - index.html
    - src
        - Api
            - api.js
        - actions
            - mediaActions.js
        - common
            - Header.js
        - components
            - HomePage.js
            - PhotoPage.js
            - VideoPage.js
        - constants
            - actionTypes.js
        - containers
            - App.js
            - MediaGalleryPage.js
        - reducers
            - imageReducer.js
            - index.js
            - initialState.js
            - videoReducer.js
        - sagas
            - mediaSaga.js
            - index.js
            - watcher.js
        - styles
            - style.css
        - store
            - configureStore.js
        - routes.js
        - index.js
    - package.json

If the project directory looks verbose, just be patient, and let’s walk-through. The intent of the project structure is to allow you to extend the application’s functionality beyond this tutorial. This would help you stay organized moving forward.

Note: If you’re new to Redux, I recommend Lin Clark’s article on A Cartoon Intro To Redux..

What the heck is happening up there?

  1. View layer is our React component. It makes a request for action based on interactions on/with the application.
  2. Action, commonly called action creator returns a formatted object of the action type and optional payload which is then dispatched to the store.
  3. Redux-saga is a Redux middleware for handling async operations like fetching photos and videos from our API. It makes asynchronous operations look like standard JavaScript synchronous code making it easy to read, test, and reason.
  4. APIs are resource locations to fetch photos and videos in our own case.
  5. Reducers are simply pure functions whose purpose in life is to accept the state tree and an action from the store; make a copy of the previous state, transform it and then return a new state to the store.
  6. Store is a single object that holds the complete state of your application. It delegates the reducer with the responsibility of changing state when an action is dispatched.

When the store receives an updated state, it transmits to the view layer to be rerendered.

Now that we understand the workflow, let’s dive into coding.

common/Header.js
import React from 'react';
import { Link, IndexLink } from 'react-router';

const Header = () => (
  <div className="text-center">
    <nav className="navbar navbar-default">
      <IndexLink to="/" activeClassName="active">Home</IndexLink>
      {" | "}
      <Link to="library" activeClassName="active">Library</Link>
    </nav>
  </div>
);

export default Header;

Link allows you to navigate to different routes in your application.

IndexLink is the same as Link with the exception of OnlyActiveOnIndex prop set on it.

components/HomePage.js
import React from 'react';
import { Link } from 'react-router';

// Home page component. This serves as the welcome page with link to the library
const HomePage = () => (
  <div className="jumbotron center">
    <h1 className="lead">Welcome to Media Library built with React, Redux, and Redux-saga </h1>
    <div>
      <Link to="library">
        <button className="btn btn-lg btn-primary"> Visit Library</button>
      </Link>
    </div>
  </div>
);

export default HomePage;
containers/App.js
import React, { Component, PropTypes } from 'react';
import Header from '../common/Header';

// The parent component renders the Header component and component(s) in the
// route the user navigates to.
class App extends Component {
  render() {
    return (
      <div className="container-fluid text-center">
        <Header />
        {this.props.children}
      </div>
    );
  }
}
App.propTypes = {
  children: PropTypes.object.isRequired
};

export default App;

App component is the parent component of our app. Every other component is a child to it. this.props.children is where other child components are rendered.

We will implement the library route and the component that maps to it in Part 2 of this tutorial.

You would notice that for Header and HomePage components, we’re using stateless functional component. This approach allows us to separate our presentational components from the container components.

It’s a good practice as it enforces functional composition and component reusability. Whereas container components are responsible for your business logic and connecting with the store, presentational components are responsible for the look of your view.

Simply put, presentational components are components whose purpose in life is to render values to the DOM. Container components also known as smart components provide props and behavior to presentational components.

Let’s wire up our project routes.

routes.js
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './containers/App';
import HomePage from './components/HomePage';

// Map components to different routes.
// The parent component wraps other components and thus serves as  the entrance to
// other React components.
// IndexRoute maps HomePage component to the default route
export default (
  <Route path="/" component={App}>
    <IndexRoute component={HomePage} />
  </Route>
);

Now let’s add the entrance to our application - index.js.

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import routes from './routes';

// We require the routes and render to the DOM using ReactDOM API
ReactDOM.render(
    <Router history={browserHistory} routes={routes} />,
    document.getElementById('root')
);

We pass in our routes and browserHistory as props to Router here. browserHistory uses your browser’s History API to create a clean and real URL without the gibberish that comes with using hashHistory. hashHistory has its use case, though.

Router is a high-level API that keeps your UI and URL in sync. It ensures that required props are passed whenever you change URL.

ReactDOM is the API for mounting our application on the DOM node(root, in our own case).

Two more things before we test our app.

Add a bootstrap link to a CDN in our public/index.html.

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

Let’s add some custom styling.

styles/style.css
body {
  margin: 0;
  padding: 0;
  font-family: Helvetica, Arial, Sans-Serif, sans-serif;
  background: white;
}

.title {
  padding: 2px;
  text-overflow-ellipsis: overflow;
  overflow: hidden;
  display: block;
}

.selected-image, .select-video {
  height: 500px;
}

.selected-image img, .select-video video {
  width: 100%;
  height: 450px;
}

.image-thumbnail, .video-thumbnail {
  display: flex;
  justify-content: space-around;
  overflow: auto;
  overflow-y: hidden;
}

.image-thumbnail img, .video-thumbnail video {
  width: 70px;
  height: 70px;
  padding: 1px;
  border: 1px solid grey;
}

Let’s test our app now…

  1. npm start

Navigate to http://localhost:3000 on your browser.

Bam!!! We’re up again

Conclusion

Building application with React gets better as you understand the flow. In this part, we did:

  1. Set up configurations.
  2. Set up the project structure.
  3. Complete Header, Homepage, and App (parent) components.
  4. Map routes to different components.
  5. What else? We tested our app.

In the second part of this tutorial, we will be exploring the power of Redux, Redux-saga, and separating our state management system from the React components for scalability and maintainability.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Rowland Ekemezie

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more