Jump Start React (http://bit.ly/jumpstart-react)
In this workshop, we are going to learn how to build apps with React while ignoring all the overwhelming distractions on the web. No build tools for now. We are all going to code together, right in the browser using JS Bin. Of course, you need to build tools in real life apps so I am going to show you how to use them at the end of this workshop.
- Beginners - will learn how to get started with React.
- Experienced - will get a refresher of the fundamentals of React and how some features work under the hood. You will also learn how best to teach beginners React
-
Open JS Bin and paste the following in the HTML tab
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet"> <title>JS Bin</title> </head> <body> <!-- Coming soon... --> </body> </html>
Note For sake, we are adding a different font called
Lato
. -
Open the CSS tab and add the following to it:
body { font-family: 'Lato', sans-serif; }
Note This adds the font to the body tag of your project.
-
Update the HTML tab by adding the following inside the
<body>
tag:<div id="root"></div> <script> const rootElement = document.getElementById('root') const element = document.createElement('h2') element.textContent = 'Hello World from React' element.className = 'container' rootElement.appendChild(element) </script>
What is going on?
- First, we create a
div
with an idroot
- Next, we add a
script
tag to contain our JavaScript code. In the JS code, we grab the reference to that div element and pass the reference to arootElement
variable. - We also create a new
h2
DOM element usingcreateElement
and pass the element to anelement
variable. - We then set text to the new element and a class of container
- Finally, we append the
h2
element we to our rootdiv
element.
- First, we create a
-
You can move the JavaScript code to the JavaScript tab
-
Next, let’s rebuild this same greeting app with React. Import the following libraries (React and React DOM) below the
root
element in the HTML:<script src="https://unpkg.com/react@16.0.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.0.0/umd/react-dom.development.js"></script>
-
Update the JavaScript code to the following:
const rootElement = document.getElementById('root'); const element = React.createElement( 'h2', // element.createElement('h2) {className: 'container'}, // element.className 'Hello World from React' // element,textContent ); ReactDOM.render(element, rootElement);
What’s going on?
- We are still grabbing the
root
element. - We then use the React’s
createElement
method to create a new element. It takes 3 arguments:- The element (eg.
h2
). - Attributes as properties in an object (eg:
className
). - The text content of the element.
- The element (eg.
- Then the render method in
ReactDOM
is used to render the element insideroot
.
Note This shows we can do the same thing we do with the DOM API using React but with more features and flexibilities which we will see in the rest of this workshop.
- We are still grabbing the
-
Log the value of
element
to the console, let’s inspect it and see what looks like. Paste this below theelement
variable declaration.console.log(element);
-
Now open the developer console and expand the object logged. You should see that the
props
property contains theclassName
as well as the text content aschildren
:Note For the content of
h2
to be stored in achildren
property inprops
alongside, that means we can havechildren
in the object you saw in the second argument ofReact.createElement
. -
Update the JS tab as follows:
const rootElement = document.getElementById('root'); const element = React.createElement( 'h2', { className: 'container', children: 'Hello World from React' } ); ReactDOM.render(element, rootElement);
You should see it produces the same output. Now convert the
children
property’s value to an array of strings:const element = React.createElement( 'h2', { className: 'container', children: [ 'Hello World from React', '. Thank you!' ] } );
React would append each of the items in the array to the DOM.
DEMO: https://jsbin.com/pebojug/15/edit?html,js,output
Note A React element can also be a one off the items in
children
array. This way you can start building nested DOM trees.
Though it’s 100% possible to write a React app using createElement
, you can imagine how difficult it would be when you have a deep DOM tree. JSX allows you to write HTML-like content right inside your JavaScript code.
-
For JSX to work, you need to enable Babel. Babel is a transpiler which basically converts code written in on format or representation to another. Refer to the image below to enable Babel:
If you are working with another editor, you can just include this in your HTML:
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
-
Update the JS code as follows:
const rootElement = document.getElementById('root'); const props = { className: 'container', children: [ 'Hello World from React', '. Thank you!' ] } const element = <h2 {...props} /> ReactDOM.render(element, rootElement);
What’s happening?
- We are writing a JSX and rendering this JSX.
- The
props
object is also passed to the JSX using spread operators.
-
You can nest elements just the way you write HTML:
const element = <div className="wrapper"> <h2 {...props} /> </div>
React is a component-based library, therefore, it encourages the component architectural pattern. Components allows you to build small reusable UI elements and UI widgets then compose them to make a full fleshed app. Components are not exclusive to React. It’s a trending pattern for building engineering projects.
-
Assuming you had the following JSX that has a repeating pattern:
<div> <h2 className="greeting">Hello World from React</h2> <h2 className="greeting">Goodbye from React</h2> </div>
We are repeating the
h2
tag along with all of its properties. Why don’t we create agreating
component that we can reuse? -
Create a function to to return this elements as JSX:
const rootElement = document.getElementById('root'); const greeting = (props) => <h2 className="greeting">{props.children}</h2> const element = <div className="wrapper"> {greeting({children: 'Hello World from React'})} {greeting({children: 'Goodbye from React'})} </div> ReactDOM.render(element, rootElement);
DEMO: https://jsbin.com/dugema/2/edit?html,js,output
As you can see, a component can be a simple function that returns a chunk of HTML elements as JSX.
-
A more intuitive way to write this is to update the
greeting
variable so that it’s in title case. Then you can use it like a normal tag in the JSX. You can’t use the lower case version because JSX would mistake it as an inbuilt HTML tag. Update the code to look like the following:const rootElement = document.getElementById('root'); const Greeting = (props) => <h2 className="greeting">{props.children}</h2> const element = <div className="wrapper"> <Greeting>Hello World from React</Greeting> <Greeting>Goodbye from React</Greeting> </div> ReactDOM.render(element, rootElement);
To compose components, you need a way to move shared data from one component to another. We have fairly see properties which is only perfect for data that a component must not mutate. Props can only be rendered without changing its value.
States on the other hand gives you the flexibility to store data that can change and probably render this data directly or pass them down to child components as props. States control the behavior of a component.
-
To use state, we need to make use of a class component. Replace the content of your JS with the following:
const rootElement = document.getElementById('root'); const Usernames = (props) => ( <ul> <li>Codebeast</li> <li>Eugene</li> <li>Jacky</li> </ul> ) class App extends React.Component { render() { return (<div> <Usernames /> </div>) } } const element = <App /> ReactDOM.render(element, rootElement);
-
Take a close look at the two components and figure out what data might change in the future.
-
The list of usernames. Update the code to store this list in a state:
const rootElement = document.getElementById('root'); const Usernames = (props) => ( <ul> {props.users.map(user => <li>{user.username}</li>)} </ul> ) class App extends React.Component { constructor(props) { super(props) this.state = { users: [ {username: 'Codebeast'}, {username: 'Eugene'}, {username: 'Jacky'} ] } } render() { return (<div> <Usernames users={this.state.users} /> </div>) } } const element = <App /> ReactDOM.render(element, rootElement);
What’s going on?
- Each class makes the
props
andstate
available viathis.props
andthis.state
. As forprops
, you need to let tell the base component from React about them by callingsuper(props)
in the constructor. - The state is created as an object with a
users
property that stores an array of usernames - We render the
Usernames
child component while passing theusers
state to it via theusers
props.
- Each class makes the
The most common (if not only) way of collecting inputs and actions from the user of our apps is through events. Let’s see how we can collect actions in React apps.
-
Update the render method in the previous task to look like the following:
render() { return (<div> <Usernames users={this.state.users} /> <button onClick={this.addRandomUser.bind(this)}>Add Random User</button> </div>) }
We now have a button that we attached an
onClick
event to. This event takes a function to call when the button is clicked. In our case, we care calling an instance method namedaddRandomUser
. -
Add this method to your class:
addRandomUser() { const newUsers = ['Ganga', 'Joy', 'Karis', 'William']; const randomNumber = Math.floor(Math.random() * newUsers.length) const randomUser = newUsers[randomNumber]; this.setState({users: [...this.state.users, {username: randomUser}]}) }
What’s going on?
- We have list of possible new users.
- We create a random number by 1. Generating a random number between 0 and 1 2. Multiplying it with the length of the array to create a boundary between 0 and the length of the array (4) 3. Then we floor the number
- We use this random number to get a random user from the array
- Finally we set the new state of the
users
array by adding the new user to it.
Having a state with static data doesn’t showcase real life situations. Let’s see how we can make a request to a server hosted somewhere else for some data and then render the data in our page.
-
Import the axios library to your HTML:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
-
Add a the following method to the class:
componentDidMount() { axios.get('https://api.github.com/users/christiannwamba/followers') .then(res => { const usernames = res.data.map(user => ({username: user.login})); this.setState({users: [...this.state.users, ...usernames]}); }) }
What's going on?
- We declare a method called
componentDidMount
. This method is one of React’s inbuilt methods that are known as lifecycle methods. These methods allow you to hook into a React’s component lifecycle from creation to destruction. In our case we need the to make a request once the component s ready (mounted). No need to call this component, React will after this component is mounted - We then make a request to the Github server to get a user’s followers. The request is made using Ajax.
- The payload returned has a lot of data but we just need to get the usernames.
- We update the state. When ever state is updated, the affected components are re-rendered to update the UI.
- We declare a method called
Building an app is a process. A process much more complex than writing the code itself. Bundling, performance, offline capabilities, deploying, local development, etc are tasks you might need to consider when building a real app.
The Create React App (CRA) CLI tool simplifies this process.
-
Install Node.js
-
Install the CLI tool
npm install -g create-react-app
-
Create a new app:
create-react-app jump-start
-
Install axios via npm:
npm install axios --save
-
Open the file in a code editor, head to the
src
folder, and add a new file namedUsernames.js
with the following content:import React from 'react'; export default props => ( <ul>{props.users.map(user => <li>{user.username}</li>)}</ul> );
-
Update
App.js
to look like the following:import React from 'react'; import axios from 'axios'; import Usernames from './Usernames'; class App extends React.Component { constructor(props) { super(props) this.state = { users: [ {username: 'Codebeast'}, {username: 'Eugene'}, {username: 'Jacky'} ] } } componentDidMount() { axios.get('https://api.github.com/users/christiannwamba/followers') .then(res => { const usernames = res.data.map(user => ({username: user.login})); console.log(usernames) this.setState({users: [...this.state.users, ...usernames]}); }) } addRandomUser() { const newUsers = ['Ganga', 'Joy', 'Karis', 'William']; const randomNumber = Math.floor(Math.random() * newUsers.length) const randomUser = newUsers[randomNumber]; this.setState({users: [...this.state.users, {username: randomUser}]}) } render() { return (<div> <Usernames users={this.state.users} /> <button onClick={this.addRandomUser.bind(this)}>Add Random User</button> </div>) } } export default App;
The major difference is:
-
We have split the two components into 2 files.
-
Then import the child component
Usernames
to the parent componentApp
for render. -
Also, instead of using script tags, we are importing React and axios using
import
. -
Run
npm start
to see the app in your browser.