Learn the basics of React and see how its implementation compares to Angular.
Get started by cloning this repo.
We'll use create-react-app
to make our app.
-
npm install -g create-react-app
-
Then run the command
create-react-app friends
inside of your cloned repo. -
This will make a react app in the directory friends.
cd friends
to enter that directory. -
Run
npm start
, and you should see your basic react app spring up in the browser.
Let's take a look at our basic react app
- node_modules // create-react-app installed a bunch of dependencies for us
- public
\
- index.html // This is the html file that our react app will use
- src
\
- App.css
- App.js // This is where our react code begins
- App.test.js // This is a test file we can ignore
- Index.js // This is where our react app is initially renderd
- Index.css
create-react-app
has given us the basic "boilerplate" of our app. Now we can start working in this directory.
Checkpoint: You should be able to see your "boilerplate" react app in your browser.**
Provided in components.png
is an image breaking down the individual components of our application.
- Red - The wrapper component of our entire application
- Green - This component contains the friends list and search fields
- Yellow - Each individual friend will also be a component
First let's remove some stuff. Whenever you use create-react-app
, you'll want to remove the boilerplate
code they add that you don't need.
- Delete the following files:
src/App.test.js
src/logo.svg
- in
App.js
, remove the lineimport logo from './logo.svg';
Now that we have removed unnecessary code, we can start building our first component.
Open App.js
.
- Inside the render method, replace all the existing JSX with this code:
<div>
<h1>The <strong>facebook</strong> Friend Machine</h1>
<div className="friends">
</div>
</div>
Checkpoint: Your browser should now be displaying a styled header of "The facebook Friend Machine". The code should look something like this:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
);
// App.js
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (
<div>
<h1>The <strong>facebook</strong> Friend Machine</h1>
<div className="friends">
</div>
</div>
);
}
}
export default App;
With our header displaying and our app component ready for some child components, we can start building the next component: FriendsList.js
- Let's keep all our next components inside of a directory in
src
calledcomponents
. - Make a file called
FriendsList.js
inside ofcomponents
. - Set up
FriendsList.js
the same asApp
was; an exported class inheriting fromReact.Component
with arender
method that returns the following JSX:
<div>
<form className="form-inline searchForm" role="form">
<div className="form-group">
<input className="form-control" placeholder="Search For Friends" />
<select className="input-medium">
<option>Name</option>
<option>#Friends</option>
</select>
<select className="input-medium">
<option>Descending</option>
<option>Ascending</option>
</select>
</div>
</form>
<ul>
</ul>
</div>
- This component will need internal state, which means it needs a
constructor
. - The component's state should have three properties:
searchText
- Initially an empty string, used to search for friends.orderBy
- Initially the string"name"
, used to set what we sort our friends byorder
- Initially the string"ascending"
, used to set the sorting order.
- Look at the HTML code in your
FriendsList
component. Each piece of the state corresponds to a specific input or select. Use thevalue
attribute to bind the value from that piece of state to that part of the HTML, like so:<input value={this.state.foo} />
- Each option inside of a select needs to be bound to a value. Looking at the values in
friends.js
, bind a property from each friend to a specific option. For example:This will create a drop down selector with a single option of Name, bound to the name value.<select> <option value="name">Name</option> </select>
Your FriendsList
class should now look something like this:
import React from "react";
class FriendsList extends React.Component {
constructor( props ) {
super( props );
this.state = {
searchText: ""
, orderBy: "name"
, order: "ascending"
};
}
render() {
return (
<div>
<form
className="form-inline searchForm"
role="form"
>
<div className="form-group">
<input
className="form-control"
placeholder="Search Anything For Friends"
value={ this.state.searchText }
/>
<select
className="input-medium"
value={ this.state.orderBy }
>
<option value="name">Name</option>
<option value="friend_count">#Friends</option>
</select>
<select
className="input-medium"
value={ this.state.order }
>
<option value="descending">Descending</option>
<option value="ascending">Ascending</option>
</select>
</div>
</form>
<ul>
</ul>
</div>
);
}
}
export default FriendsList;
To test this component we'll need to import it into App.js
and add the component inside of the div with the class of "friends".
None of our values will be editable. This is because they are bound to values on this components state, which can only be changed with this.setState
Let's fix this so we can change theses values.
- Begin by creating a new method on the
FriendsList
class namedhandleChange
.- This method should take in two parameters,
field
andevent
. - When called, the method should use React's
setState
method to change the value of the correct property on state toevent.target.value
. - If the field is
"searchText"
, then setState should change"searchText"
toevent.target.value
.
- This method should take in two parameters,
- Next we need to add
onChange
properties to our select and input elements, passing in ourhandleChange
method.- Don't forget to use
bind
to preserve the context ofthis
! - Remember that you can use
bind
to preset an argument for a function. Use that to set the value offield
for eachonChange
event handler. - Make sure
onChange
'sfield
parameter matches the field on state that you want to change.
- Don't forget to use
Checkpoint: You should now be able to make changes to your search field and select boxes and have that value placed on FriendsList
's state
. Your code should look something like this:
import React from "react";
class FriendsList extends React.Component {
constructor( props ) {
super( props );
this.state = {
searchText: ""
, orderBy: "name"
, order: "ascending"
};
}
handleChange( field, event ) {
this.setState( { [ field ]: event.target.value } );
}
render() {
return (
<div>
<form
className="form-inline searchForm"
role="form"
>
<div className="form-group">
<input
className="form-control"
onChange={ this.handleChange.bind( this, "searchText" ) }
placeholder="Search For Friends"
value={ this.state.searchText }
/>
<select
className="input-medium"
onChange={ this.handleChange.bind( this, "orderBy" ) }
value={ this.state.orderBy }
>
<option value="name">Name</option>
<option value="friend_count">#Friends</option>
</select>
<select
className="input-medium"
onChange={ this.handleChange.bind( this, "order" ) }
value={ this.state.order }
>
<option value={ "descending" }>Descending</option>
<option value={ "ascending" }>Ascending</option>
</select>
</div>
</form>
<ul>
</ul>
</div>
);
}
}
export default FriendsList;
Create a new file named Friend.js
and follow the usual steps for creating a class with a render method that returns the following JSX:
<li className='friend'>
<img className="profile-pic" src='http://placebear.com/50/50.jpg' />
<h3>Cali Fornia</h3>
<div className="location">
Location: New Port Beach, California, United States
</div>
<div className="status">
Status: I hate the snow. I wish I was on the beach right now!!! <span className="hashtag">#ihateprovo</span>
</div>
<div className="num-friends">
Friends: 1,367
</div>
</li>
- Import
Friend
intoFriendsList
and place it inside theul
tag at the bottom.- You should now see a single friend listed, but we want to display our whole list!
- First we need our data.
- Move
friends.js
into this directory. - import
friends.js
intoFriendList.js
and save it to a variable namedfriends
.
- Move
- At the top of the render method
map
over the array of friends to create an array ofFriend
components, passing infriend.name
,friend.pic_square
,friend.status
,friend.friend_count
, andfriend.current_location
as props.- Don't forget that every repeated item in React needs a unique
key
.friend.$$hashKey
would work well for this. - Be careful of null values!
- Don't forget that every repeated item in React needs a unique
- Adjust
Friend.js
to usethis.props
instead of the static data we included in our original JSX.
Checkpoint: You should now be displaying a large list of friends. Your code should look something like this:
// FriendsList.js
import React from "react";
import friends from "../../friends";
import Friend from "./Friend";
class FriendsList extends React.Component {
// ...
render() {
const friendsList = friends.map( friend => (
<Friend
currentLocation={ friend.current_location || {} }
friendCount={ friend.friend_count }
key={ friend.$$hashKey }
name={ friend.name }
pictureUrl={ friend.pic_square }
status={ friend.status ? friend.status.message : "" }
/>
) );
return (
// ...
<ul>
{ friendsList }
</ul>
);
}
// ...
// Friend.js
import React from "react";
class Friend extends React.Component {
render() {
return (
<li className='friend'>
<img className="profile-pic" src={ this.props.pictureUrl } />
<h3>{ this.props.name }</h3>
<div className="location">
Location: { this.props.currentLocation.city }, { this.props.currentLocation.state }, { this.props.currentLocation.country }
</div>
<div className="status">
{ this.props.status }
</div>
<div className="num-friends">
{ this.props.friendCount }
</div>
</li>
);
}
}
export default Friend;
As the final touch, we need to add sorting and filtering. For this we will use plain JavaScript.
- Using the values we have stored on our FriendList component's state and built in array methods sort, filter, and reverse the array of Friend components as expected.
- Warning: JavaScript's built in
.sort
does not reliably sort in Chrome. Either test in another browser or find a different sorting algorithm. - Your code should look something like this:
- Warning: JavaScript's built in
// FriendsList.js
// ...
const friendsList = friends
.filter( friend => friend.name.toLowerCase().indexOf( this.state.searchText.toLowerCase() ) !== -1 )
.sort( ( a, b ) => a[ this.state.orderBy ] > b[ this.state.orderBy ] )
.map( friend => (
<Friend
currentLocation={ friend.current_location || {} }
friendCount={ friend.friend_count }
key={ friend.$$hashKey }
name={ friend.name }
pictureUrl={ friend.pic_square }
status={ friend.status ? friend.status.message : "" }
/>
) );
const displayFriends = this.state.order === "ascending" ? friendsList : friendsList.slice().reverse();
// ...
- Currently we are only searching by name. Create a select that allows users to choose what to search by.
- Update the UI so that Friend components without location data do not display two empty commas.
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2016. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.