Intro

Start Here! (Fork)

https://stackblitz.com/edit/fcc-seattle-friendz?file=App.js (FORK)

What are we going to build?

We are building a contacts app, “Friendz”, which will create many Friend components that each contain contact information.

Free%20Code%20Camp%20February%202020%20Learn%20Components%20Thro%20eae4194c25dd49c6acc3979229763108/screencapture-localhost-3000-2020-02-21-13_23_52.png

Sponsors

SauceLab

Sauce Labs provides the world’s largest cloud-based Selenium Grid for the continuous testing of web and mobile applications. Founded by Jason Huggins, the original creator of Selenium, Sauce Labs helps companies accelerate software development cycles, improve application quality, and deploy with confidence across hundreds of browser / OS platforms, including Windows, Linux, iOS, Android & Mac OS X.

The Tech Academy

The Tech Academy is a licensed career school that teaches students Computer Programming and Web Development. The Tech Academy offers 4 different bootcamp tracks that include: Frontend Web Development, Python, C# & .NET Framework, and the Software Development Bootcamp, which is a comprehensive bootcamp covering all other bootcamp tracks.

What sets The Tech Academy apart from other coding bootcamps is the comprehensive curriculum, open enrollment, flexible scheduling options, online or in-person training options, self-paced study and exceptional job placement training. At the end of our program, graduates are well-rounded, full-stack junior developers!

Tweet our sponsors to let them know that you appreciate them

@saucelabs

@thetechacad

The App

Start here: https://stackblitz.com/edit/fcc-seattle-friendz?file=App.js

We will be using stackblitz, which simulates a code editor, VSCode! For some of you, this may be your first time working within a code editor. Take a minute to look at the hierarchy of folders and familiarize yourself before we start.

Fork the pen. You will need to create a StackBlitz account if you don’t have one already. Otherwise, you will not be able to save your work.

Step 1- What is React?

Before we start working through our exercise and getting hands-on experience, let’s take a step back and think of what React is.

“A javascript library for building user interfaces”

This is the official answer from React’s website as to what React thinks React is.

“React has been designed from the start for gradual adoption, and you can use as little or as much React as you need. Whether you want to get a taste of React, add some interactivity to a simple HTML page, or start a complex React-powered app…”

React is an open-source library maintained by Facebook, is incredibly popular right now, and allows you to build front-end to your applications through components. These components use internal data, state and props, to streamline creating dynamic UI. The best part about using these components is that they will automatically re-render when their state or props are updated, meaning you can have a component re-rendering WITHOUT re-loading an entire page of content that hadn’t changed.

If this is your first time working with a front-end library, but you are familiar with HTML and Javascript, the syntax for React will look like it is using both HTML and javascript together. This is because React is often written using “JSX”, meaning you will be combining both HTML elements as well as javascript expressions all within the same file. Look at our app file to see how a React component looks:

//=======
// App.js
//=======
 
import React, { Component } from 'react';
 
import './style.css';
import Profile from './components/Profile';
 
class App extends Component {
  render() {
    return (
      <div className='app-container'>
        <h3>Welcome FCC Seattle </h3>
        <Profile />
      </div>
    );
  }
}
 
export default App;

What are we looking at? From top to bottom:

  • import additional modules that will be used with this component (We import React so we can use it!)
  • A class is being created, called App, and that class does one thing: render.
  • What is returned from the render is first an h3 element displaying “Welcome FCC Seattle”
  • Next is a component, Profile, which has divs enclosing it.
  • Lastly, the App class is exported so it can be used throughout the application.

Step 2- What is a React component?


So now we’ve seen an example of a “component”, but what is it?

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.

A React component is kinda like a custom HTML element. You just reference it in your code where you want it to appear, and React knows to render out another copy of that component to your specification. They even have attributes you can set to influence the output just like native HTML elements. Those attributes are referred to in React as “props.”

Step 3- What are props and state?


State is an object made up of properties and values that the component keeps track of. To put it in more relatable terms, state is the current…state…of that particular component!

Here’s an example: You’re a long-distance runner. You’ve built a simple lap counter web app for yourself to use on your phone to keep track of your laps as you run. You click a button to increment the number of laps. The component displaying the current number of laps might store that value in state so that, as it is incremented, the display is automatically updated.

As mentioned before, the power of storing state rather than simply a variable is that a component will re-render when either state or props change. This means that as soon as you add a Friend to your state for “friendsList”, for example, what you see on screen will immediately update if that state is being referenced in your JSX.

State should not be changed directly. Changing state directly may break React’s automatic re-rendering. This means you shouldn’t, for instance, do something like increment a “count” state directly like this.state.count += 1.

Instead, what should you do? You should set state to a new value, overwriting the previous value. React has an easy method to do this: setState().

state = {
  count: 0,
};
 
//calling this would work, but is BAD
this.state.count += 1;
//
 
//This IS correct!
this.setState({
  count: this.state.count + 1,
});

Props, on the other hand, are arguments passed into a component . This allows a component to be customized in what is rendering, or how, based on the data passed down.

In the example we will build, we will pass down state from our FriendContainer component to our Friend child components as props. Don’t worry if that sounds a little confusing so far — we’ll make sense of it soon with some examples!

Below, we’ll be looking at our App component. We will provide a Prop, which will be “name”, to the Profile component. When the Profile component sees that a “name” prop is provided, it will display “Welcome (name provided)“.

//=======
// App.js
//=======
 
import React, { Component } from 'react';
 
import './style.scss';
import Profile from './components/Profile';
 
class App extends Component {
  render() {
    return (
      <div className='app-container'>
        <Profile name='Michael Jordan' />
      </div>
    );
  }
}
 
export default App;

Welcome Michael Jordan

How did it know to display the name? In our code for our Profile component below, see that we included a ternary statement (a type of “if, else” statement) which looked to see if the component was receiving a “name” prop.

This is an example of the JSX evaluating a javascript expression. What is in the curly braces {} is being run as Javascript.

If our statement could say aloud what it was doing, it would say:

“if this.props.name is provided, render here an h3 of “Welcome” followed by what was received as a name prop”

This operation only runs when the two sides of the evaluation 1) this.props.name and 2) <h3> Welcome {this.props.name} </h3> are both true / truthy. Really, we only are trying to figure out if the first part of the operation is true, otherwise the entire operation does not run.

It works because in JavaScript, true && expression always evaluates to expression, and false && expression always evaluates to false. Therefore, if the condition is true, the element right after && will appear in the output. If it is false, React will ignore and skip it.

//=======
// Profile.js
//=======
 
import React, { Component } from "react";
 
export default class Profile extends Component {
	render() {
		return (
			<div className='profile'>
				<p className='subtle-text'>
					profile container
				</p>
				{[this.props.name](http://this.props.name/) && <h3>Welcome {[this.props.name](http://this.props.name/)}</h3>}
			</div>
		);
	};
};
{
  this.props.name && <h3> Welcome {this.props.name} </h3>;
}
 
// Is doing the same thing as...
 
{
  if (this.props.name) {
    <h3> Welcome {this.props.name} </h3>;
  }
}

Step 4- FriendContainer


In order to create the app that we want, we will want to have one component that will house all of the individual Friend components as we create them with a form. This means we will have this new component manage the state for the form, as well as a state for the list of friends we will be creating.

We’ll put together the skeleton of our FriendContainer, and you’ll see it is created the same way as our other components. Then, we’ll import the Friend component and render two Friend components, each with a “name” prop to distinguish them.

//=======
// FriendContainer.js
//=======
 
import React, { Component } from 'react';
 
import Friend from './Friend';
 
class FriendContainer extends Component {
  render() {
    return (
      <div className='friend-container'>
        <p className='subtle-text'>Friend Container</p>
        <div className='friends-list'>
          <Friend name='number 1' />
          <Friend name='number 2' />
        </div>
      </div>
    );
  }
}
 
export default FriendContainer;

Now let’s update App so that we are importing FriendContainer at the top of the file, and rendering FriendContainer within the return:

//=======
// App.js
//=======
 
import React, { Component } from 'react';
 
import './style.css';
import Profile from './components/Profile';
import FriendContainer from './components/FriendContainer';
 
class App extends Component {
  render() {
    return (
      <div className='app-container'>
        <Profile />
        <FriendContainer />
      </div>
    );
  }
}
 
export default App;

We should initialize state on our FriendContainer component for the form we will be adding. We will need state for “name”, “location”, “color”, and “isFavorite”, which will all be different inputs in our form. We had talked earlier about not changing the state directly, which is exactly what we are doing here. The difference is here we are setting a “default” or initial state before the component is constructed, not changing state through a method after the component is created.

We’ll also prepare a “friendsList” state, which we will use to store each “friend”, but for now that will start as an empty array.

Again, why do we want this as state and not simply a global variable?

By leveraging state, the component is going to re-render whenever we update the state, so we will see the updates right away!

//=======
// FriendContainer.js
//=======
 
import React, { Component } from 'react';
 
import Friend from './Friend';
 
class FriendContainer extends Component {
    state = {
      name:'',
      color:'lightblue',
      isFavorite: false,
      location:'',
      friendsList: []
    }
}
 
...

Create form to create multiple Friend Components


Now that state is being initialized, let’s build out the rest of our component with a render and return, and we’ll start building out the form we will be using.

//=======
// FriendContainer.js
//=======
 
import React, { Component } from 'react';
 
import Friend from './Friend';
 
class FriendContainer extends Component {
  state = {
    name: '',
    color: 'lightblue',
    isFavorite: false,
    location: '',
    friendsList: [],
  };
 
  render() {
    return (
      <div className='friend-container'>
        <p className='subtle-text'>Friend Container</p>
        <div className='friend-container-form'>
          <form onSubmit={this.handleSubmit}>
            <label>
              Friend Name
              <input
                type='text'
                name='name'
                value={this.state.name}
                onChange={this.handleChange}
              />
            </label>
            <label>
              Color
              <select
                name='color'
                value={this.state.color}
                onChange={this.handleChange}>
                <option value='lightblue'>Light Blue</option>
                <option value='lightgreen'>Light Green</option>
                <option value='salmon'>Salmon</option>
                <option value='lavender'>Lavender</option>
                <option value='lightpink'>Light Pink</option>
              </select>
            </label>
            <label>
              Favorite
              <input
                name='isFavorite'
                type='checkbox'
                checked={this.state.isFavorite}
                onChange={this.handleChange}
              />
            </label>
            <label>
              Location
              <input
                type='text'
                name='location'
                value={this.state.location}
                onChange={this.handleChange}
              />
            </label>
            <input className='submit' type='submit' />
          </form>
        </div>
        <div className='friends-list'>
          <Friend name='number 1' />
          <Friend name='number 2' />
        </div>
      </div>
    );
  }
}
 
export default FriendContainer;

As we put together our form, notice that for each input we are including an “onChange” property. We will need to create a function that will set state whenever that input changes in any way. While we could make a separate onChange function for each input, it would be even better to make one function that handles any type of input.

See the snippet below — this code will rest within the FriendContainer component before the render() is called.

You can see this snippet alongside the rest of the code in FriendContainer a little further down

//=======
// FriendContainer.js
//=======
 
//snippet of handleChange function
 
...
 
handleChange = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
 
    this.setState({
      [name]: value
    })
  }
 
...

This handleChange function will assign a value based on whether it was a checkbox (where the only “value” would be “checked”, which results in true or false), or a text value. We then use this.setState() to rewrite our state to what the current value on the input is.

Great. That should start handling setting a state for each of our inputs. BUT…we need to set an onSubmit property on our form, too, for when we are ready to complete creating a “friend”.

So here’s what’s happening:

You can see this snippet within the rest of the code in FriendContainer a little further down

//=======
// FriendContainer.js
//=======
 
//snippet of handleSubmit function
 
...
 
handleSubmit = (event) => {
  event.preventDefault();
 
  let friendObj = {
    name: this.state.name,
    color: this.state.color,
    isFavorite: this.state.isFavorite,
    location: this.state.location
  }
 
  this.setState({
    friendsList: [...this.state.friendsList, friendObj]
  }, () => console.log('this is your friendsList state', this.state.friendsList))
}
 
...
  • prevent the default action of the submit — which means don’t refresh the entire page
  • create an object, friendObj, which will set properties name, color, isFavorite, and location to each corresponding state.
  • State shouldn’t be changed directly! We can’t simply push our friendObj onto the friendsList state…
    • So, we set the friendsList state to the previous friendsList state (the ... makes this possible, a spread operator) and append at the end of that list the friendObj.
  • Lastly, just so we can see that things are working, we set a callback on setState to console.log our friendsList state!
    • setState is an asynchronous function, so we use a callback function here to make sure we don’t see a console.log of the state until the setState has finished.

  • Ok, that should handle creating a friend! At this point, our friendsList state should have updated to include the friend we created with our form. Let’s take a look at the whole FriendContainer component (TOGGLE):

    import React, { Component } from 'react';
     
    import Friend from './Friend';
     
    class FriendContainer extends Component {
      state = {
        name: '',
        color: 'lightblue',
        isFavorite: false,
        location: '',
        friendsList: [],
      };
     
      handleChange = (event) => {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
     
        this.setState({
          [name]: value,
        });
      };
     
      handleSubmit = (event) => {
        event.preventDefault();
     
        let friendObj = {
          name: this.state.name,
          color: this.state.color,
          isFavorite: this.state.isFavorite,
          location: this.state.location,
        };
     
        this.setState(
          {
            friendsList: [...this.state.friendsList, friendObj],
          },
          () =>
            console.log('this is your friendsList state', this.state.friendsList)
        );
      };
     
      render() {
        return (
          <div className='friend-container'>
            <p className='subtle-text'>Friend Container</p>
            <div className='friend-container-form'>
              <form onSubmit={this.handleSubmit}>
                <label>
                  Friend Name
                  <input
                    type='text'
                    name='name'
                    value={this.state.name}
                    onChange={this.handleChange}
                  />
                </label>
                <label>
                  Color
                  <select
                    name='color'
                    value={this.state.color}
                    onChange={this.handleChange}>
                    <option value='lightblue'>Light Blue</option>
                    <option value='lightgreen'>Light Green</option>
                    <option value='salmon'>Salmon</option>
                    <option value='lavender'>Lavender</option>
                    <option value='lightpink'>Light Pink</option>
                  </select>
                </label>
                <label>
                  Favorite
                  <input
                    name='isFavorite'
                    type='checkbox'
                    checked={this.state.isFavorite}
                    onChange={this.handleChange}
                  />
                </label>
                <label>
                  Location
                  <input
                    type='text'
                    name='location'
                    value={this.state.location}
                    onChange={this.handleChange}
                  />
                </label>
                <input className='submit' type='submit' />
              </form>
            </div>
            <div className='friends-list'>
              <Friend name='number 1' />
              <Friend name='number 2' />
            </div>
          </div>
        );
      }
    }
     
    export default FriendContainer;

Step 5- Render several Friend Components

So we have the friendsList state updating properly now — let’s create some components for each of those friends!

We’ll create a renderFriends function to look at the friendsList state, and for each of those objects, return a Friend component.

Since we will be returning a list of Friend components eventually, we will update the code within our “friends-list” div at the bottom of our FriendContainer file to add a ul within.

We also don’t need the two Friend components we had used earlier to demonstrate that everything was working, so we’ll take those out.

Then, we’ll include in brackets a call to the renderFriends function we are about to make:

You can see this snippet within the rest of the code in FriendContainer a little further down

//=======
// FriendContainer.js
//=======
 
//snippet of friends-list div
 
...
 
<div className='friends-list'>
	<ul>
		{this.renderFriends()}
	</ul>
</div>
 
...

Now let’s create that renderFriends function:

You can see this snippet within the rest of the code in FriendContainer a little further down

//=======
// FriendContainer.js
//=======
 
//snippet of renderFriends function
 
...
 
renderFriends = () => {
  return this.state.friendsList.map((friend, index) => {
    return (
      <li key={index}>
        <Friend {...friend} />
      </li>
    )
	})
}
 
...

Here’s how we did this:

  • return a map on the friendsList state, with the first argument, “friend”, returning all of the properties of the object, and “index” representing the index of each object
  • For each element mapped, return an li with a “key” property set to the index, and then a Friend component receiving all of the properties of that object as props (again, using a spread operator to receive all properties)

It is important to note that all React Children must contain a unique “key” so that they can be re-rendered more easily. We satisfy this in our friends list by providing a key to each li

NOTE: Using an index as a unique key is not a scalable answer or best practice, but it will work for our app.

This means that each Friend component will receive, as props, all properties from our friend objects. At this point, the Friend component being created will have access to name, location, color, and isFavorite through this.props (this.props.name, this.props.location, etc)


  • Let’s take a look at the updated code for our FriendContainer component (TOGGLE):

    //=======
    // FriendContainer.js
    //=======
     
    import React, { Component } from 'react';
     
    import Friend from './Friend';
     
    class FriendContainer extends Component {
      state = {
        name: '',
        color: 'lightblue',
        isFavorite: false,
        location: '',
        friendsList: [],
      };
     
      handleChange = (event) => {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
     
        this.setState({
          [name]: value,
        });
      };
     
      handleSubmit = (event) => {
        event.preventDefault();
     
        let friendObj = {
          name: this.state.name,
          color: this.state.color,
          isFavorite: this.state.isFavorite,
          location: this.state.location,
          id: nextId,
        };
     
        this.setState(
          {
            friendsList: [...this.state.friendsList, friendObj],
          },
          () =>
            console.log('this is your friendsList state', this.state.friendsList)
        );
      };
     
      renderFriends = () => {
        return this.state.friendsList.map((friend, index) => {
          return (
            <li key={index}>
              <Friend {...friend} />
            </li>
          );
        });
      };
     
      render() {
        return (
          <div className='friend-container'>
            <p className='subtle-text'>Friend Container</p>
            <div className='friend-container-form'>
              <form onSubmit={this.handleSubmit}>
                <label>
                  Friend Name
                  <input
                    type='text'
                    name='name'
                    value={this.state.name}
                    onChange={this.handleChange}
                  />
                </label>
                <label>
                  Color
                  <select
                    name='color'
                    value={this.state.color}
                    onChange={this.handleChange}>
                    <option value='lightblue'>Light Blue</option>
                    <option value='lightgreen'>Light Green</option>
                    <option value='salmon'>Salmon</option>
                    <option value='lavender'>Lavender</option>
                    <option value='lightpink'>Light Pink</option>
                  </select>
                </label>
                <label>
                  Favorite
                  <input
                    name='isFavorite'
                    type='checkbox'
                    checked={this.state.isFavorite}
                    onChange={this.handleChange}
                  />
                </label>
                <label>
                  Location
                  <input
                    type='text'
                    name='location'
                    value={this.state.location}
                    onChange={this.handleChange}
                  />
                </label>
                <input className='submit' type='submit' />
              </form>
            </div>
            <div className='friends-list'>
              <ul>{this.renderFriends()}</ul>
            </div>
          </div>
        );
      }
    }
     
    export default FriendContainer;

Step 6- Update the Friend Component

Update what is rendered per each Friend Component

It’s great that we are seeing a Friend component render for each “friend” in our friendsList state. However, the Friend component is not doing anything with the data besides displaying a “name” prop!

Let’s make changes to the Friend component so that it knows how to render a color (as a background color), a location, and identify if this is a “favorite” friend or not.

//=======
// Friend.js
//=======
 
import React, { Component } from 'react';
 
export default class Friend extends Component {
  render() {
    return (
      <div
        className='friend-component'
        style={{ background: this.props.color }}>
        <div className='friend-header'>
          {this.props.isFavorite && <h3> 🌟 </h3>}
          <p className='subtle-text'>Friend Component</p>
        </div>
        <div className='friend-contact'>
          <h4>{this.props.name} </h4>
          {this.props.location && <h4>Location: {this.props.location} </h4>}
        </div>
        <div className='skill-list' />
      </div>
    );
  }
}

What did we add:

  • Add a “style” prop to the div surrounding the app. We set the background to the color passed down through props.
  • We added a javascript expression within the ‘friend-header’ div, which would render a star when this.props.isFavorite was true
  • Lastly, we added a javascript expression within the ‘friend-contact’ div, which would render an h4 with the location given through props when this.props.location was provided.

At this point, we should see that each Friend component will display a background color given through props, a location, and a star if that friend is a favorite!

Make a few friends, and see how they are different — yet within the component’s code, they look the same! What really makes the components individualized are the props that make each Friend unique. This is the power of props, and how you can reuse components to accomplish different things simply by sending or excluding props.

At this point, we’ve gotten through the lesson and can now create several Friend components within our FriendContainer through our form. We should see that each Friend has unique values for name, location, color, etc, and that our FriendContainer is managing that friends list state.

You may have also noticed that there is a div at the bottom of our Friend file, with a className of ‘skills-list’. If you are feeling adventurous, look through the extra credit below, and see if you can add a skills list for each Friend!

  • If you’d like some hints to get your started, toggle here!

    You may want to add to your form on FriendContainer, add a skillsList state on the FriendContainer, and then render that list somehow within your Friend component. Look to how we rendered a list of friends with the renderFriends function within FriendContainer if you need a reference!

Next Steps:

If you need help completing any of this, let us know and we will help you figure it out!

  • Clear contents of inputs on form after submit
  • Add a “skills list” to each Friend (ex: javascript, java, react, etc)
  • Use localStorage to save your friendsList
  • Create another component for your app
  • Add to the Profile component
  • Decorate / Style your Friend component better than I did!

Completed Project

If you have questions about how the completed app turned out, let us know!

https://stackblitz.com/edit/fcc-seattle-friendz-complete

Additional Resources

https://reactjs.org/docs/getting-started.html

https://www.taniarascia.com/getting-started-with-react/