- Explain what ReactJS is and where it fits in our applications' stack.
- Explain the component model of web development.
- Create and render React components in the browser.
- Pass in data to a React component via
props
. - Nest React components.
- Modify the
state
of a React component through events.
React is a JavaScript library used to craft modern day UI and views for the front-end in web applications.
Selling Point: By modeling small compatible components that focus on just rendering a view, we can move business logic out of the DOM, and therefore improve our app's performance, maintainability, modularity and readability.
The first thing most people hear about React is "Facebook uses it."
- First used by Facebook in 2011.
- Then Instagram in 2012.
- Went open source in May 2013.
React was born out of Facebook's frustration with the traditional MVC model and how..
- Re-rendering something meant re-rendering everything (or just a lot).
- That had negative implications on processing power and ultimately user experience, which at times became glitchy and laggy.
If you want to get a taste of what React's all about, here's an introduction from React.js Conf 2015. Recommend starting around the 8:35 mark and watching until 16:30.
React can be thought of as the "Views" layer.
What is the role of a "view" in a front-end Javascript application?
The visual template the user sees, often populated with data from our models.
React can be used agnostically throughout your stack. It's role is just to use data to render a UI. This means that React can also co-exist with other Javascript frameworks. Let the other frameworks handle the models and controllers, and have React sort out the views.
In order to create a new project and to get our development environment setup, we are going to use the Terminal command create-react-app
. It will create a new folder in your current directory for the in-class application.
$ npm i -g create-react-app
$ create-react-app blog-app
$ cd blog-app
$ atom .
$ npm run start
Here you will begin setting up a blog app that you will continue working on during this lesson's exercises. For demonstration purposes, I will be creating a simple "hello world" app.
After running $ npm run start
, we can view the app at http://localhost:3000
create-react-app
provides us with all the necessary tools and configuration necessary to start writing React. npm run start
refers to an included script that starts up the development server.
Along with installing the necessary dependencies such as React, ReactDom, Babel and Webpack, it creates an initial app skeleton that looks like this...
├──README.md
├── favicon.ico
├── index.html
├── node_modules
├── package.json
└── src
├── App.css
├── App.js
├── index.css
├── index.js
└── logo.svg
Most of the important files, which are primarily the ones where we will be working today, are in the /src
directory.
Take some time and look at what's been generated. Specifically look in App.js
and index.js
One of the snarky comments made about React when it was first open sourced was. "Rethinking established best practices". Traditionally we're used to a more MVC approach for separation of concerns. In React, we want to move towards more of a component-based separation of concerns. When taking a look at Facebook, you could think of each status post as a mini-component in React. And a list of those updates, is a component that contains several of those mini-components. You could take that one step further and think of the Facebook app, as one giant component with several components within it. (Things like the list of status updates, the friends list, the header, etc...)
5 minutes exercise. 5 minutes review.
Break into pairs and take a look at CraigsList. Identify the visual "components" the website is comprised of. We suggest using markers to draw these out on your table! So something like this...
As you're drawing this out, think about the following questions...
- Where do you see "nested components"? Where do you not?
- Are there any components that share the same structure?
- Of these similar components, what is different about them?
No need to follow along with this Hello World example. You will have the chance to implement this yourself when you get to the first Blog exercise.
The basic unit you'll be working with in ReactJS is a component.
-
It sounds like a simple word, but using "components" is a pretty different way of approaching web development.
-
Components can be thought of as functional elements that takes in data and as a result, produce a dynamic UI.
Throughout class we have separated HTML, CSS and Javascript.
- With components, the lines between those three become a bit blurry.
- Instead, we organize our web apps according to small, reusable components that define their own content, presentation and behavior.
What does a component look like? Let's start with a simple "Hello World" example...
To start, in our /src/App.js
file, let's remove the contents and in its place add this component definition...
// bring in React and Component instance from React
import React, {Component} from 'react'
// define our Hello component
class Hello extends Component {
// what should the component render
render () {
// Make sure to return some UI
return (
<h1>Hello World!</h1>
)
}
}
export default Hello
Let's recap what's going on.
Often times we write out React components in JSX.
- JSX is a language that compiles to Javascipt that allows us to write code that strongly resembles HTML. It is eventually compiled to lightweight JavaScript objects.
- React then uses these objects to build out a "Virtual DOM" -- more on that in just a bit.
React can be written without JSX. If you want to learn more, check out this blog post.
Let's break down the things we see here...
This is the component we're creating. In this example, we are creating a "Hello" component.
This is the React library class we inherit from to create our component definition.
Every component has, at minimum, a render method. It generates a Virtual DOM node that will be added to the actual DOM.
- Looks just like a regular ol' DOM node, but it's not yet attached to the DOM.
This exposes the Hello class to other files which import from the App.js file. The default
keyword means that any import that's name doesn't match a named export will automatically revert to this. Only one default is allowed per file.
Virtual DOM? How is that different from the usual DOM?
The Virtual DOM is a Javascript representation of the actual DOM.
- Because of that, React can keep track of changes in the actual DOM by comparing different instances of the Virtual DOM.
- React then isolates the changes between old and new instances of the Virtual DOM and then only updates the actual DOM with the necessary changes.
- By only making the "necessary changes," as opposed to re-rendering an entire view altogether, we save up on processing power.
- This is not unlike Git, with which you compare the difference -- or
diff
-- between two commits.
If you're interested in learning more about the Virtual DOM, check this video out.
So we've created the template for our component. Now, let's use /src/index.js
to load in our new component and render it on the DOM...
import React from 'react'
import ReactDOM from 'react-dom'
import Hello from './App.js'
ReactDOM.render(
<Hello />,
document.getElementById('root')
)
In place of
ReactDOM.render
some tutorials will use React.renderComponent, which has been phased out. The change is outlined here.
ReactDOM.render
takes the Virtual DOM node created by extends Component
and adds it to the actual DOM. It takes two arguments...
- The component.
- The DOM element we want to append it to.
What is <Hello />
written in? JSX.
- Similar to XML.
- When we say
<Hello />
, in plain Javascript we are actually sayingReact.DOM.div( null, "Hello world.")
- Basically, a string of React methods that create a virtual DOM node.
NOTE: Whenever you use a self-closing tag in JSX, you MUST end it with a
/
like<Hello />
in the above example.
Our Hello
component isn't too helpful. Let's make it more interesting.
- Rather than simply display "Hello world", let's display a greeting to the user.
- So the question is, how do we feed a name to our
Hello
component without hardcoding it into our render method?
First, we pass in data wherever we are rendering our component, in this case in src/index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
import Hello from './App.js'
ReactDOM.render(
<Hello name={"Nick"} />,
document.getElementById('root')
)
Then in our component definition, we have a reference to that data via as a property on the props
object...
class Hello extends Component {
render () {
return (
<h1>Hello {this.props.name}</h1>
)
}
}
In the above example, we replaced "world" with {this.props.name}
.
Properties! Every component has .props
- Properties are immutable. That is, they cannot be changed while your program is running.
- We define properties in development and pass them in as attributes to the JSX element in our
.render
method.
First we can pass multiple properties to our component when its rendered in src/index.js
..
import React from 'react';
import ReactDOM from 'react-dom'
import Hello from './App.js'
ReactDOM.render(
<Hello name={"Nick"} age={24} />,
document.getElementById('root')
)
Then in our component definition we have access to both values...
class Hello extends Component {
render () {
return (
<div>
<h1>Hello {this.props.name}</h1>
<p>You are {this.props.age} years old</p>
</div>
)
}
}
NOTE: The return statement in
render
can only return one DOM element. You can, however, place multiple elements within a parent DOM element, like we do in the previous example with<div>
.
Let's have some practice creating a React component from scratch. How about a blog post?
- Create a
post
object literal insrc/index.js
that has the below properties.title
author
body
comments
(array of strings)
- Render these properties using a Post component.
- The HTML (or more accurately, JSX) composition of your Post is up to you.
It would be a pain to have to explicitly define every comment inside of <Post />
, especially if each comment itself had multiple properties.
- This problem is a tell tale sign that our separation of concerns is being stretched, and it's time to break things into a new component.
We can nest Comment components within a Post component.
- We create these comments the same way we did with posts:
extends Component
and.render
- Then we can reference a comment using
<Comment />
inside of Post's render method.
Let's create a new file for our Comment component, src/Comment.js
...
import React, {Component} from 'react'
class Comment extends Component {
render () {
return (
<div>
<p>{this.props.body}</p>
</div>
)
}
}
export default Comment
Then in src/App.js
, we need to load in our Comment
component and render it inside of our Post
component...
import React, { Component } from 'react';
// Load in Comment component
import Comment from './Comment.js'
class Post extends Component {
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>By {this.props.author}</p>
<div>
<p>{this.props.body}</p>
</div>
<h3>Comments:</h3>
// Render Comment component, passing in data
<Comment body={this.props.comments[0]} />
</div>
);
}
}
export default Post;
Note: We could put all of our code in one file, but it's considered a good practice to break components out into different files to help practice separation of concerns. The only downside is we have to be extra conscious of remembering to export / import each component to where it's rendered.
Amend your Post
's render method to include reference to a variable, comments
, that is equal to the return value of generating multiple <Comment />
elements. Make sure to pass in the comment
body as an argument to each Comment
component. Then render the comments
somewhere inside the UI for a Post
.
NOTE: You can use
.map
inPost
'srender
method to avoid having to hard-code all yourComment
's. Read more about it here and here.HINT I: You should only have to return one
<Comment />
inside of.map
.HINT II: Each comment needs a key attribute to keep track of data passed as props. The key should be a unique identifier for each child component
HINT III: Remember that whenever you write vanilla Javascript inside of JSX, you need to surround it with single brackets (
{}
).
So we know about React properties, and how they relate to our component's data.
- The thing is,
props
represent data that will be the same every time our component is rendered. What about data in our application that may change depending on user action? - That's where
state
comes in..
Values stored in a component's state are mutable attributes.
- Like properties, we can access state values using
this.state.val
- Setting up and modifying state is not as straightforward as properties. It involves explicitly declaring the mutation, and then defining methods to define how to update our state...
Lets implement state in our earlier Hello
example by incorporating a counter into our greeting.
class Hello extends Component {
// when our component is initialized,
// our constructor function is called
constructor () {
// make call to parent class' (Component) constructor
super()
// define an initial state
this.state = {
counter: 0 // initialize this.state.counter to be 0
}
}
render () {
return (
<div>
<h1>Hello {this.props.name}</h1>
<p>You are {this.props.age} years old</p>
<p>The initial count is {this.state.counter}
</p>
</div>
)
}
}
Ok, we set an initial state. But how do we go about changing it?
- We need to set up some sort of trigger event to change our counter.
Take a look at how this event is implemented. We use an attribute called onClick
to define what happens when we click this particular button. As its value, we're passing in an anonymous function that invokes handleClick, a function defined on this component.
class Hello extends Component {
constructor () {
super()
this.state = {
counter: 0
}
}
handleClick (e) {
// setState is inherited from the Component class
this.setState({
counter: this.state.counter + 1
})
}
render () {
// can only return one top-level element
return (
<div>
<h1>Hello {this.props.name}</h1>
<p>You are {this.props.age} years old</p>
<p>The initial count is {this.state.counter}
</p>
<button onClick={(e) => this.handleClick(e)}>click me!</button>
</div>
)
}
}
Whenever we run .setState
, our component mimics a diff against the current DOM, and compares the Virtual DOM node with the updated state to the current DOM.
- Only replaces the current DOM with parts that have changed.
Let's implement state
in our Blog by making body
a mutable value.
- Initialize a state using a
constructor()
method for ourPost
to set a initial state. It should create a state value calledbody
. Set it to thebody
of your hard-codedpost
. - Modify
Post
'srender
method so thatbody
comes fromstate
, notprops
. - Create a
handleClick
method insidePost
that updatesbody
based on a user input.
- You should use
setState
somewhere in this method. - How can you get a user input? Keep it simple and start with
prompt
.
- Add a button to
Post
'srender
method that triggershandleClick
.
Use a form to take in user input.
- The post body should be updated using a method that is triggered by
onSubmit
. - One option is to keep track of what the new input is going to be by triggering a method using
onChange
on the<input>
- Another option is to pass an event object to the
onSubmit
method and traverse the DOM frome.target
to find the<input>
value.
NOTE: You're starting to mock Angular's two-way data binding!