This tutorial is out of date and no longer maintained.
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:
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.
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.
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:
- npm install -g create-react-app
Then, create the media-library
application:
- 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.
- cd media-library
- npm start
Now, we can structure our project directory and add other dependencies.
- npm install --save redux redux-saga react-router@2.4 react-redux
Then, remove the default sample app:
- rm -rf src/**
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?
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.
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.
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;
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.
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.
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…
- npm start
Navigate to http://localhost:3000
on your browser.
Bam!!! We’re up again
Building application with React gets better as you understand the flow. In this part, we did:
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.
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!