JavaScriptLA
Slides.com website for JavaScriptLA -- use this place to find our meetup group presentations. Want more? Visit our website at: https://www.javascriptla.net to get on our mailing list.
Presented by Vijay Menon
JavaScriptLA
Join Us On Slack!
http://javascriptla.herokuapp.com for an invite
Our first "Hello World" app
First, let's set up our project:
Here's what we need:
<!DOCTYPE html>
<html>
<head>
<title>Simple Hello World Website</title>
<script src="https://npmcdn.com/react@15.3.0/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom@15.3.0/dist/react-dom.min.js"></script>
<script src="https://npmcdn.com/babel-core@5.8.38/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
)
</script>
</body>
</html>
Here's what your code should look like:
Important
And here's what you should see:
And while you're at it, explain JSX
Here's the scoop:
<!DOCTYPE html>
<html>
<head>
<title>Simple Hello World Website</title>
<script src="https://npmcdn.com/react@15.3.0/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom@15.3.0/dist/react-dom.min.js"></script>
<script src="https://npmcdn.com/babel-core@5.8.38/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
'use strict';
ReactDOM.render(React.createElement(
'h1',
null,
'Hello, world!'
), document.getElementById('example'));
</script>
</body>
</html>
Without JSX, we'd have to write code like this :/
JSX allows us to write neat components using "HTML" like syntax but has super powers and is reusable
Revisiting our code sample:
Project for Tonight:
Building A Simple Movie Search App
Let's start off by using our default project structure (but slightly modify it):
Here's what we need:
Next, let's install Webpack and some dev dependencies:
Here's what we need:
Here's the scoop:
You already saw Babel before:
Let's write some React!
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(
<h1>Hello World</h1>,
document.querySelector('.container')
)
src/index.js
<!DOCTYPE html>
<html>
<head>
<title>Simple Movie Search App</title>
</head>
<body>
<div class="container"></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
index.html
We'll get this from webpack
Note: we're using ES6 this time :)
Ready to compile? Let's use Webpack!
More info on Webpack -- check out:
https://medium.com/@dabit3/beginner-s-guide-to-webpack-b1f1a3638460#.usek81716
Issues:
Here's our output via "npm start":
Notice the dev server =)
Back to This:
How Do We Organize This Into Code?
Let's Break This Diagram Up
Into Components:
App
Search Form
MovieListing
MovieListings
Next:
Let's write these components in React
Here's what we need:
import React, { Component } from 'react'
import SearchForm from './searchform'
import MovieListings from './movielistings'
export default class App extends Component {
render(){
return (
<div className="app">
<h1>Search for Movies</h1>
<SearchForm />
<MovieListings />
</div>
)
}
}
import React, { Component } from 'react'
export default class SearchForm extends Component {
render(){
return (
<div>I am the search form</div>
)
}
}
src/js/app.js
src/js/searchform.js
We need to wrap up our components or React will throw an error
import React, { Component } from 'react'
export default class MovieListings extends Component {
render(){
return (
<div>I am the movie listings</div>
)
}
}
import React, { Component } from 'react'
export default class SearchForm extends Component {
render(){
return (
<div>I am the search form</div>
)
}
}
src/js/movielistings.js
src/js/movielisting.js
Here's our output via "npm start":
Starting to see a pattern yet?
Let's Start With the Search Form Component:
import React, { Component } from 'react'
export default class SearchForm extends Component {
render(){
return (
<div className="searchform">
<form>
<input type="text" placeholder="Search Movies" />
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
src/js/searchform.js
Also, let's create a sass file & use gulp to create our default style.css which we'll link in index.html
Center the app with your stylesheet
Next, let's build the movielistings as a table
import React, { Component } from 'react'
import MovieListing from './movielisting'
export default class MovieListings extends Component {
render(){
return (
<div className="movielistings">
<table>
<thead>
<tr>
<th>Poster</th>
<th>Title</th>
<th>Year</th>
</tr>
</thead>
<tbody>
<MovieListing />
</tbody>
</table>
</div>
)
}
}
src/js/movielistings.js
Again, fix styles if needed and gulp
Now, let's build the movielisting as a table row
import React, { Component } from 'react'
export default class MovieListing extends Component {
render(){
return (
<tr className="movielisting">
<td>
<p><img src="" />Poster</p>
</td>
<td>
<p>Title</p>
</td>
<td>
<p>Year</p>
</td>
</tr>
)
}
}
src/js/movielisting.js
Fix styles if needed and gulp
Our components are looking good, but currently they don't do anything other than look pretty
Next we need to:
Back to searchform.js
src/js/searchform.js
export default class SearchForm extends Component {
constructor(props){
super(props)
this.state = {
term: ''
}
//without binding, our this value would be the div not the component
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e){
e.preventDefault();
console.log('submitted the form');
}
render(){
return (
<div className="searchform">
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Search Movies" />
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
We are instantiating our component here with any properties and their values from parent components aka 'props', as well as use this space to handle any creation of "state" or binding of functions (so their this value is correct and not a DOM element deep somewhere)
Let's also modify app.js to have "state" in its constructor -- again we'll come back to this
src/js/app.js
export default class App extends Component {
constructor(props){
super(props)
this.state = {
movies:[]
}
}
render(){
return (
<div className="app">
<h1>Search for Movies</h1>
<SearchForm />
<MovieListings movies={this.state.movies}/>
</div>
)
}
}
Let's try the app out now:
Does it work for you? If not, check your code, you might have a typo somewhere
Let's also get the actual input value from our searchform, and then set it on our searchform's state object for "term". We'll then submit "term" to our App Component, which will handle the AJAX call.
import React, { Component } from 'react'
export default class SearchForm extends Component {
constructor(props){
super(props)
this.state = {
term: ''
}
//without binding, our this value would be the div not the component
this.updateTerm = this.updateTerm.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateTerm(e){
let term = e.target.value;
console.log(term)
this.setState({term:term})
}
handleSubmit(e){
e.preventDefault();
console.log('submitted the form', this.state.term);
}
render(){
return (
<div className="searchform">
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Search Movies" onChange={this.updateTerm}
value={this.state.term}/>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
src/js/searchform.js
Your Output Now:
From searchform.js, let's send up our input term submitted (available through this.props.onSubmit)
handleSubmit(e){
e.preventDefault();
console.log('submitted the form', this.state.term);
this.props.onSubmit(this.state.term);
}
src/js/searchform.js
In app.js, let's create a property called onSubmit, that will be available to searchform (via this.props)
<SearchForm onSubmit={this.findMovies}/>
src/js/app.js
this.props allows components to access parent component properties and methods, through a 'property' on itself which can be useful as seen here. It's not quite 'state', because state is actual data, that changes with time -- and based on change, re-renders the application.
We also need to write the findMovies method in our App component
constructor(props){
super(props)
this.state = {
movies:[]
}
//don't forget to bind our findMovies function to our component
this.findMovies = this.findMovies.bind(this);
}
findMovies(term){
let url=`http://www.omdbapi.com/?s=${term}&r=json`;
$.ajax({
url:url,
dataType:'json',
cache:false,
success:function(results){
console.log(results)
let movies = results.Search
this.setState({movies:movies});
console.log(this.state.movies);
}.bind(this),
error:function(xhr, status, err){
console.error(status, err.toString());
}.bind(this)
});
}
Your Output Now:
Pass the state to the MovieListings & MovieListing components
render(){
let movies = this.props.movies.map((movie) => {
if(movie.Poster == undefined){
return
}
return (
<MovieListing poster={movie.Poster}
title={movie.Title} year={movie.Year} key={movie.imdbID}/>
)
})
src/js/movielistings.js
<tbody>
{movies}
</tbody>
Let's edit our MovieListings component to utilize the 'movies' props filled now with movies from our AJAX call at the App level.
Using a "fat arrow function" (from ES6) aka anonymous function, we can create a new array of MovieListing components anytime state changes (e.g. we do a new search) that will then get passed to the table below.
Once the components are created, React will always recreate the view for us!
src/js/movielisting.js
One last step! Update MovieListing so it also can access each property given to it via this.props upon its creation in MovieListings.
import React, { Component } from 'react'
export default class MovieListing extends Component {
render(){
return (
<tr className="movielisting">
<td>
<p><img src={this.props.poster} /></p>
</td>
<td>
<p>{this.props.title}</p>
</td>
<td>
<p>{this.props.year}</p>
</td>
</tr>
)
}
}
Your Output Now:
Congratulations! If you made it this far, you are a React hero :)
Try It Again -- This Time Yourself!
Summary & Overview
Next Steps:
Additional Resources
Stay tuned for Part II (React & Redux)
By JavaScriptLA
A simple intro to React for JavaScript learners
Slides.com website for JavaScriptLA -- use this place to find our meetup group presentations. Want more? Visit our website at: https://www.javascriptla.net to get on our mailing list.