This tutorial is out of date and no longer maintained.
Scotch is going to help you learn an awesome library in a really fun way. What could be more fun than making a music app?
Yeah, I get it! You have been hearing React
thrown around these days but you have had no reason to learn it yet or rather you have tried but it just didn’t work. Together we will build a fairly complex but easy-to-understand music app with React, Soundcloud, and Electron which is going to cover everything you need to know about React and its best practices.
It is difficult to convince people to learn a new library or framework and I understand that. I am coming from an Angular background and I consider myself a professional for that matter. For that reason, I never considered learning React until I came across this video.
A quick gif:
The working CodePen
http://codepen.io/christiannwamba/pen/aNjjPE
Basic knowledge of HTML and JavaScript (ES6) is enough for you to understand this tutorial. On the other hand, the only fairly advanced topic you need is an understanding of the mystries of this
keyword.
Browserify helps us use client JS libraries like React and jQuery with Node’s require()
and makes bundling easy:
- npm install -g browserify
If you want to learn more about Browserify, check out Peleke’s tutorial
Not all browsers have EcmaScript 2015 (ES6) support, therefore a transpile tool will be needed in such case. JavaScript transpilers are important.
To install Babel (which is the transformer) and its squad of tools called presets
, we will include them in our package.json
which we will address soon.
We also need to create ./.babelrc
file to inform babel which presets we are using:
"presets": ["es2015", "react"]
Electron is a tool for building cross-platform desktop apps with web technologies. This means that you do not have to learn an OS native language so as to build an app that runs natively on computers. The amazing aspect actually is that you write ones and build the same code for different platforms (OSX, Windows, Linux). We have written an article on Angular and Electron and can learn more from Jasim’s tutorial.
Our app is expected to run as a standalone app and not in a browser. Electron is in my opinion the most popular tool for building desktop apps with web technologies (HTML, CSS, JS):
- # Clone the Quick Start repository
- git clone https://github.com/electron/electron-quick-start scotch-player
-
- # Go into the repository
- cd scotch-player
The starter has two important files: main.js
and index.html
. These files serve as entries to an Electron project.
To see the expected blank workspace, run:
- # Launch the App with Electron
- npm start
Below is the directory structure of what we are building and will serve as a guide down the journey:
- |---app #All React projects goes here
- |----components # Presentation Component Directory
- |------details.component.js
- |------footer.component.js
- |------player.component.js
- |------progress.component.js
- |------search.component.js
- |----containers # Container Component Directory
- |------app.container.js
- |----app.js
- |---public # Client Files here
- |----css
- |------global.css
- |----img
- |------logo.png
- |------soundcloud.png
- |----js
- |------bundle.js
- |---index.html # Electron Default View
- |---main.js # Electron entry point
- |---package.json
- |---.babelrc # Babal's Configurations
package.json
{
"name": "scotch-player",
"productName":"Scotch Player",
"version": "1.0.0",
"description": "Scotch Demo Player",
"main": "main.js",
"scripts": {
"start": "electron main.js",
"watch": "watchify app/app.js -t babelify -o public/js/bundle.js --debug --verbose"
},
"author": "Scotch",
"license": "MIT",
"dependencies": {
"axios": "^0.9.1",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babelify": "^7.2.0",
"classnames": "^2.2.3",
"electron-prebuilt": "^0.36.0",
"electron-reload": "^0.2.0",
"jquery": "^2.2.3",
"react": "^0.14.8",
"react-autocomplete": "^1.0.0-rc2",
"react-dom": "^0.14.7",
"react-sound": "^0.4.0",
"soundmanager2": "^2.97.20150601-a"
}
}
Our concerns are the scripts
and dependencies
sections. The scripts
has two commands, the firsts (start
) starts the app and the second (watch
) tells browserify to watch the app folder and bundle the JS content to public/js/bundle.js
Go ahead and install the dependencies:
- npm install
React is really a simple and small library. It only echoes one word, Components
. React is simply a UI library that helps web designers/developers build reusable UI components. Reusable from as small as a button to as complex as navigation menus. See this series for more on getting started with React.
A simple component could be:
// ES6 Component
// Import React
import React from 'react';
// Search component created as a class
class Search extends React.Component {
// render method is most important
// render method returns JSX template
render() {
return (
<form>
<input type="text" />
<input type="submit" />
</form>
);
}
}
// Export for re-use
export default Search
We will see more of that while we build the app. The only weird thing here is the XML-like content in our JavaScript. It is called JSX and is just a convenient way to write HTML in JavaScript. You have the option to go hardcore with document.createElement
.
A little exception could be made though - components are not just for UIs, they can be used to manage state of UI components. Let’s have a deeper look:
These components are simple and very straightforward. They just present UI details and nothing much. It should never manage state but should only receive properties to be bound to its UI.
Events also are handled as callbacks via properties and not in components.
One more thing, for no reason, should a presentation component be aware of how data sent to it came about. It should be possible to isolate it.
A simple example:
import React from 'react';
class Search extends React.Component {
// Props is received via the constructor
constructor(props) {
//...and props is sent back to the parent Component
//class using super()
super(props);
// Initial State of the component is defined
// in the constructor also
this.state = {
value:''
};
}
handleSubmit() {
//postRequest is assumed to be the function
// that makes an Ajax request
postRequest(this.state.value);
}
handleChange(e) {
// Bind value state with current input
this.setState({value: e.target.value});
}
render() {
return (
<form
onSubmit={this.handleSubmit.bind(this)}>
<input type="text"
value={this.state.value}
onChange={this.handleChange.bind(this)}/>
<input type="submit"/>
</form>
);
}
}
export default Search
The above example is doing exactly what we do not want a presentation component to do. It knows how values are manipulated and sent to the server. Let’s refactor:
import React from 'react';
// Simplified component
class Search extends React.Component {
render() {
return (
<form
onSubmit={this.props.handleSubmit}>
{/* Notice how values and callbacks are passed in using props */}
<input type="text"
value={this.props.searchValue}
onChange={this.props.handleChange}/>
<input type="submit"/>
</form>
);
}
}
Now the component is extra lean and is focused on presenting content depending on the values passed to it via this.props
. It also handles events with functions passed to it via this.props
too. So where do the states and those event handlers go to? They are moved to the container components.
Note: On props
and states
:
Properties and states join forces to make React an amazing and awesome tool. Properties are just read-only values passed from one component to another or a component to its UI elements. Props are accessed with just this.props
because they are read-only.
States on the other hand are writable and that is the way we update our application data (state). States are accessible via this.state
and because they are writable, can be updated with this.setState({value: 'value'})
.
This works with the concept of service provider
. They tend to expose APIs for the presentation components. This is where you can get your hands dirty and do the dirty jobs of updating states and defining application behaviors. This is what takes care of all those jobs we asked presentation components to drop.
To better understand container components, let us have a look at one that compliments our above Search Component
:
import React from 'react';
import Search from './search';
class AppContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
value:''
};
}
// Makes a request to the server (simulated for this tutorial)
handleSubmit() {
postRequest(this.state.value);
}
// React input update (binding) is manual which
// makes it rubust. This is how you keep the input box
// in sync with keystroke inputs
handleChange(e) {
// New values are availbale from the event object
this.setState({value: e.target.value});
}
// Container components wrap presentation component
render() {
return (
<Search
handleSubmit={this.handleSubmit.bind(this)}
handleChange={this.handleChange.bind(this)}
searchValue={this.state.value}/>
);
}
}
export default AppComponent
See how the above component abstracts the state update and event handling from the presentation component. Once an event is called on the presentation component, it asks its parent container component to handle the event, and if any change in state, pass it back.
Do not panic, we learn better with examples and there are few examples in the demo we will talk about.
It is very simple to use React in an Electron project. You can bundle with Browserify or Webpack but in our case, we will use browserify. The following checklist is good for setting React in any platform including Electron:
package.json
or .babelrc
: [✔]watch
script to package.json
: [✔]start
and watch
scripts to start Electron and bundling respectivelyAs you can see, we have accomplished 1 to 4 and that is why they are checked. Let us have a look at 5 and 6.
Create app.js
in the app
folder as shown in the directory structure with the following:
// ES6 Component
// Import React and ReactDOM
import React from 'react';
import ReactDOM from 'react-dom';
// Search component created as a class
class Search extends React.Component {
// render method is most important
// render method returns JSX template
render() {
return (
<form>
<input type = "text" />
<input type = "submit" />
</form>
);
}
}
// Render to ID content in the DOM
ReactDOM.render( < Search / > ,
document.getElementById('content')
);
Something new is ReactDOM which is used to render components to the DOM. The index.html
in our case:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Scotch Player</title>
</head>
<body>
<div id="content"></div>
<script src="public/js/bundle.js"></script>
</body>
</html>
We are pointing to a nonexisting file, bundle.js
. It will contain our bundled app therefore create the file and leave it empty then run:
- npm run watch
Browserify comes in two flavors: browserify
and watchify
. The difference is that watchify just waits for change and re-creates the bundle while browserify
bundles only when you issue the command.
It is painful to continue running npm start
for every change just to update Electron with the changes. We can automate it by adding the following in the main.js
:
require('electron-reload')(__dirname);
We have already installed the package using package.json
.
Hopefully, you have a fair knowledge of React, how it is useful, and the two types of components. In the next tutorial, we will design our presentation components. See you then…
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!