Nathan Sebhastian

@nathansebhastian

Hands-on projects to learn the basics of React

Have you went through the official React’s Tic Tac Toe tutorial? If you have, you might notice this bolded text in the introduction

You might be tempted to skip it because you’re not building games — but give it a chance.

I’m guessing even React team knows that it’s not that interesting to build a Tic Tac Toe game, since they put that line in. While the project indeed gives you an understanding of React, you’ll need a strong will to process the tutorial.

Now don’t get me wrong, I’m grateful for this introduction project, but I just don’t like it. If you’re thinking the same thing, I’m going to write about alternative projects that you can build to learn React in this tutorial.

So what front-end projects you can pick up to learn about React? As I went through tutorials and blog posts, I noticed a good introduction tutorial for React must do these things:

  • Teach the fundamentals of React like components, state and props
  • Handling dynamic data, changes in state and props
  • Shows the use of lifecyle method

Well, almost everything from the main concepts tab, really.

Core React knowledge right in this tab!

By the end of this tutorial, you will have a better understanding of the uses of React concepts like components, state and lifecycle methods and how they are used in common web application UI.

Table of Contents

Note: For the CSS part of this tutorial, we’ll use Bootstrap to make it look nicer without writing our own CSS. You can safely ignore the className parts of the sample code, since they are from Bootstrap

Front-end fun with list of cards

Let’s start with utilizing JSX, components and props — the basic ingredients of a React UI. Here is our final product:

A list of cards with React

So let’s build it. All we have to do is to create the <Card/> Component which returns JSX elements:

function Card(props) {
return (
<div className="card">
<img className="card-img-top"
src="https://via.placeholder.com/600x250.png"
alt="cap image" />
<div className="card-body">
<h5 className="card-title">Title Placeholder</h5>
<p className="card-text">Description Placeholder</p>
<a href="#" className="btn btn-primary">Learn more</a>
</div>
</div>
);
}

And then create a parent component that renders <Card/> three times. We can name it <CardList/>

function CardList() {
return (
<div className="row">
<div className="col-sm-4">
<Card />
</div>
<div className="col-sm-4">
<Card />
</div>
<div className="col-sm-4">
<Card />
</div>
</div>
);
}

Don’t forget to add the ReactDOM.render call at the bottom of the code. This is the code responsible for bringing our React application into our HTML element.

ReactDOM.render(<CardList />, document.getElementById('root'));

Next, we need to include our own data into these cards, so pass some props into it.

function CardList() {
return (
<div className="row">
<div className="col-sm-4">
<Card featureImage="https://sebhastian.com/static/eb0e936c0ef42ded5c6b8140ece37d3e/fcc29/feature-image.png"
title="How To Make Interactive ReactJS Form"
description="Let's write some interactive form with React"
link="
https://sebhastian.com/interactive-react-form"
/>
</div>
<div className="col-sm-4">
<Card
// your data
/>
</div>
<div className="col-sm-4">
<Card
// your data
/>
</div>
</div>
);
}

And then use these props in our <Card/> component:

function Card(props) {
return (
<div className="card">
<img className="card-img-top" src={props.featureImage} alt="cap image" />
<div className="card-body">
<h5 className="card-title">{props.title}</h5>
<p className="card-text">{props.description}</p>
<a href={props.link} className="btn btn-primary">Learn more</a>
</div>
</div>
);
}

Now this <Card/> component used JavaScript in its JSX, pretty similar to templating engines, isn't it?

You might be wondering, “why do we use function instead of class for declaring component?”

Because we don’t keep state or use lifecycle methods. React components are declared as a class in order to use those two things (although now we can do it with React hooks as well, but we'll leave hooks out for now.)

As we can see in the example, React’s UI are made of three basic ingredients: components, JSX and props.

  • Component is a single piece of UI made out of methods and JSX.
  • JSX is HTML supercharged with JS, enabling us to describe UI using JavaScript syntaxes.
  • Props are arbitrary inputs that we pass down into a component.

There really is not much more we can learn from this simple static cards except basic UI patterns, so let’s move on to a more complex task.

Getting Complex With Wizard Form

In this second exercise we will build a wizard form, which is a multi-step form designed to ease the filling process for a long and complex form. By only showing a few inputs on a screen, users will feel encouraged to fill the blank inputs rather than feeling overwhelmed and potentially abandoning the form.

Let’s take a look at how you can build a form like this with React:

React wizard form

The easiest way to create a multi step form is to create a container form component, which contains all the wizard step component inside of it. This diagram will help you understand it clearly.

React wizard form diagram

Although it seems to be more complex than a regular form, a wizard form still use the same React principle. But since we have state in this exercise, we need to include a new principle:

  • State is used for storing dynamic data

Instead of having one form component, we will have one parent component and three child components. In the diagram above, <MasterForm/> component will send data and functions to children components via props, and children components will trigger handleChange() function to set values in the state of <MasterForm/> . We'll need a function to move the form from one step to another as well.

Just like how CardList send props to Card, These children components will receive props from <MasterForm/> for value and onChange props.

  • <Step1/> component will render email address input
  • <Step2/> will render username input
  • <Step3/> will render password input and a submit button

The parent <MasterForm/> will supply both data and function into child components, and child components will pass user inputs back to the parent using its props.

First, we’ll create the form children components. This example will only include one input per form step. The comments will show the use of props.

function Step1(props) {
if (props.currentStep !== 1) {
return null
}
return(
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
className="form-control"
id="email"
name="email"
type="text"
placeholder="Enter email"
value={props.email} // props from parent
onChange={props.handleChange} // props from parent

/>
</div>
)
}

Since the child components look almost similar between one another, I’m just showing one of them above. You can take a look at the demo for the full code. Notice how we used function instead of class, since we don't use state or lifecycle methods.

Then we can put this child component into the master form render() function and pass in the necessary props. To handle events in which user type something into the text, we used the onChange synthetic event, which is a part React core libraries for handling events. More details here.

Let’s make the <MasterForm/> component and initialize its state and methods. A currentStep state will be initialized with the value of 1. This is used for step indicator so our form knows on what step we are currently. We'll use ES6 class since need local state for this component:

class MasterForm extends React.Component {
constructor(props) {
super(props)
this.state = {
currentStep: 1,
email: '',
username: '',
password: '',
}
}
// creating functions with ES6 arrow function syntax
handleChange = event => {
const {name, value} = event.target
this.setState({
[name]: value
})
}

handleSubmit = event => {
event.preventDefault()
const { email, username, password } = this.state
alert(`Your registration detail: \n
Email: ${email} \n
Username: ${username} \n
Password: ${password}`)
}

// render method here...
}

Next, we add the steps in the render method of <MasterForm/> . It will send handleChange() function and required state values as props, note the highlighted code blocks:

render() {    
return (
<React.Fragment>
<h1>A Wizard Form!</h1>
<p>Step {this.state.currentStep} </p>

<form onSubmit={this.handleSubmit}>
{/*
render the form steps and pass required props in
*/}
<Step1 
currentStep={this.state.currentStep}
handleChange={this.handleChange}
email={this.state.email}
/>
<Step2
currentStep={this.state.currentStep}
handleChange={this.handleChange}
username={this.state.username}
/>
<Step3
currentStep={this.state.currentStep}
handleChange={this.handleChange}
password={this.state.password}
/>
</form>
</React.Fragment>
)
}

Because render() must return a single element, the <React.Fragment> component lets you return multiple elements in a render() method without creating an additional DOM element. More details here.

Then we add the next or previous step function, which will check if the current step has a previous or next step. If it is, it will push currentStep up or down:

class MasterForm extends React.Component {
/*
* Test current step with ternary
* _next and _previous functions will be called on button click
*/
_next = () => {
let currentStep = this.state.currentStep
currentStep = currentStep >= 2? 3: currentStep + 1
this.setState({
currentStep: currentStep
})
}

_prev = () => {
let currentStep = this.state.currentStep
currentStep = currentStep <= 1? 1: currentStep - 1
this.setState({
currentStep: currentStep
})
}
// ... the rest of the code

We’ll make functions that will check whether the current step is 1 or 3. This is because we have 3 step wizard form. You can change them if you have more. The buttons will disappear if there is no next or previous step from the current step. These buttons will call on our _next and _previous methods.

/*
* the functions for our button
*/
previousButton(){
let currentStep = this.state.currentStep;
if(currentStep !==1){
return (
<button
className="btn btn-secondary"
type="button" onClick={this._prev}>
Previous
</button>
)
}
return null;
}
nextButton(){
let currentStep = this.state.currentStep;
if(currentStep <3){
return (
<button
className="btn btn-primary float-right"
type="button" onClick={this._next}>
Next
</button>
)
}
return null;
}

All that’s left is to render our next and previous buttons

/*
* add buttons to our form in render
*/
render(){
return(
<form onSubmit={this.handleSubmit}>
{/*
... other codes
*/}

{this.previousButton()}
{this.nextButton()}

</form>
)
}

If you’re wondering why we used () on the call to buttons above, that's because we need to actually execute the button functions. The _next and _previous functions are only executed on button click, hence they musn't have () on the call.

Phew! There’s a lot of interactions between component and state here, but I hope by now you get to understand the use of state in React application. To summarize, state is just arbitrary data we defined in a component, and it becomes a part of that component forever. We can pass it down to another component, we can update it, and we can do conditional stuff based on what state our component currently is in.

In this sample form, we have used state for keeping track of user’s inputs and the current step of the wizard form. Since React is a one-way data flow from parent to child component, always remember that only the owner of the state can mutate or update it.

To use state we can use ES6 Class or React Hooks (will be explained in another tutorial.)

Still up for another exercise? Let’s do it then!

P.S: If you are working on react form, you might wanna check out this in depth tutorial by Arinich

GitHub Search App

Now for our third exercise, let’s actually use some ES6 features to get data from GitHub API and display its results. This exercise will include all the things we learned from previous projects and new ones: lifecycle methods and rendering lists.

Simple GitHub search app

Note: There are extra css that I wrote for this app. Make sure to visit the CSS tab of the codepen above and paste it if you’re not forking the pen

First, let’s explore about Github API that we’re going to use. Since we’re only searching by username, we need this API url:

https://api.github.com/search/users?q={--search-string--}

Let’s get ready to build the app by writing the big header component first. It’s just static Bootstrap Jumbotron actually:

const Header = () => {
return (
<div className="jumbotron">
<h1>Github Search App</h1>
<h2>Search users in GitHub using this simple React application.</h2>
<p>Click on the card to see more detail about individual user. The search default is nsebhastian (me!)</p>
</div>
);
};

Now let’s think about making the input form. We’re going to need:

  1. A search form
  2. Calling Github API when search form is submitted
  3. Display search result in a list of cards

We’ll start with declaring the API constant

const API = 'https://api.github.com/';

Then let’s initialize the “top” component with two state values: the searchText and data.

class App extends React.Component {
constructor(props){
super(props);
this.state = {
searchText: 'nsebhastian',
data: '',
}
}

fetchSearch = username => {
let url = `${API}search/users?q=${username}`;
fetch(url)
.then((res) => res.json() )
.then((data) => {
this.setState({
data: data
});

})
.catch((error) => console.log('Oops! . There Is A Problem' + error) )
}
componentDidMount() {
this.fetchSearch(this.state.searchText);
}

fetchSearch function will fetch data from the API url, transform it into JSON object, then update our data state with the freshly fetched data. It will be called in a component lifecycle method componentDidMount. If you're not familiar with lifecycle methods, they are basically methods run on a particular times in the process of constructing and rendering components. There are other methods beside componentDidMount, including constructor method. Not all lifecycle methods are used frequently, some of them will be used more often than the rest.

Let’s continue with our app by writing the render method of App component:

render() {
return (
<div>
<MyHeader />
<SearchForm
fetchSearch={this.fetchSearch}
/>
<Profiles
data={this.state.data}
/>

</div>
);
}

You might’ve guessed it by seeing the code that we need to create two more components, namely <SearchForm/> and <Profiles/>.

Let’s start with the <SearchForm/>. We have written form in React before, so this won't be hard. We just need a single text input and a submit button. Also, let me show you another way of getting input value without using state:

class SearchForm extends React.Component {
render() {
return (
<div className="search-bar">
<form
className="input-group"
onSubmit={this.handleForm}>
<input
type="search"
ref="username"
placeholder="Type Username here"
className="form-control"/>
<span className="input-group-btn">
<button type="submit"
className="btn btn-warning">Submit</button>
</span>
</form>
</div>
)
}
handleForm = event => {
event.preventDefault();
let username = this.refs.username.value
this.props.fetchSearch(username);
}
}

As you can see, we are getting username value by using ref. This way we don't need to initialize state at all. We have to use ES6 class for declaring the component, since we need to write the handleForm function.

Now it’s time to write the final component <Profiles/>. I will use this opportunity to show you component declaration — arrow function style.

Profiles = props => {
if(props.data){
let data = props.data;
if (data.message === 'Not Found')
return (
<div className="notfound">
<h2>Oops !!!</h2>
<p>The Component Couldn't Find The You Were Looking For . Try Again </p>
</div>
);
else{
// map the users into JSX elements
let userList = data.items.map((name) => {
return (
<a key={name.id} href={name.html_url} target="blank">
<div className="bs-callout bs-callout-info">
<img className="user" src={name.avatar_url} alt={`${name.login}`}/>
<h4>Username : {name.login}</h4>
<p> Url : {name.html_url}</p>
<p> Score : {name.score} </p>
</div>
</a>
);
})
// then render it
return (
<div>{userList}</div>
);
}

}
else {
return <div>Fetching data . . .</div>
}
}

If you’re following this tutorial from the start, I think you can understand what this <Profiles/> component do. It will accept props named data from its parent, then do something based on that props. We used the map function to iterate and write JSX elements from the data array. Then it's simply returned for rendering.

Notice how a key props is passed into the <a> element so that React can identify individual element in the list. More detail here.

Now you can search and click on the result to be taken to the GitHub user profile. Great job coming this far! We can actually improve the app by using React Router and create user page for a detailed view of individual user, but let’s call it a day for now and go for React Router refactoring when we actually learn about React Router.

Conclusion

We’ve build three exercise projects to learn about React fundamentals, starting from a simple, static cards list, to a more complex React application that fetch data from GitHub API and display it. We also learned about dynamic data management using state.

The take away from these tutorials are simple and reuseable React patterns that you’ll see in almost any React app:

  • Component is a single piece of UI made out of methods and JSX.
  • JSX is HTML supercharged with JS, enabling us to describe UI using JavaScript syntaxes
  • Props are arbitrary inputs that we pass down into a component.
  • State is used for storing dynamic data. We can use it for rendering UI and storing fetched data
  • Lifecycle methods are used for methods that need to be called on component render. The easiest example is for calling API and fetching data

So how was it? Isn’t it more fun learning React by actually building pieces of components you’re more likely to use in your projects? Is it too hard for you? Please give me feedback so I can improve my writing skill.

I’ll be covering more about React soon — like using Hooks or React router — so if you’re interested in that, be sure to stay tuned by following me or subscribe to my newsletter. I promise not to spam your inbox!

Thanks for reading 🙌

Originally published at sebhastian.com

More by Nathan Sebhastian

Topics of interest

More Related Stories