{{announcement.body}}
{{announcement.title}}

How to Build React Components You Can Reuse

DZone 's Guide to

How to Build React Components You Can Reuse

Take a look at this tutorial that demonstrates how to create a fully functional React component, pass and emit values, and connect it to Firebase.

· Java Zone ·
Free Resource

React lets you create a component for rating that can be used over and over, anyplace a rating component is needed. It’s even possible to add it to your company’s internal package management system, so that the component is easily consumed by any React application that needs it. Today, we’re going to be creating a beer rating app—Brewstr—using reusable react components.

You will need:

Scaffold Your React Application

To get this thing kicked off, scaffold a basic React application by running the following command in the terminal in a folder where you’d like to store your code:

Shell
 




xxxxxxxxxx
1


1
create-react-app brewstr


Once this program has created your React application’s skeleton, change into your code directory and open it in your favorite code editor. I’ll be using Visual Studio Code, so I’ll run the following command:

Shell
 




xxxxxxxxxx
1


1
code .


Add Okta Authentication to Your React Application

Now that you have a basic React app, you’ll need to set up Okta to allow users to authenticate into your application. If you don’t already have one, create a free-forever Okta developer account. Once you’ve logged in, take note of your Okta org URL on the top right of the dashboard. Then choose Applications from the main menu and click Add Application.

In the wizard, choose Single-Page App from the available platforms and click Next. In the Application Settings page you’ll want to name the app “Brewstr” and change the BaseURIs, and Login redirect URIs values to use port 3000, and click Done. Take note of the Client ID for your newly created app.

General settings

Back in the React application, add two packages to your application:

Shell
 




xxxxxxxxxx
1


1
yarn add @okta/okta-react@^1.2.3 react-router-dom@^5.1.2


Create a .env.local file at the root of the project and add the following to it:

Java
 






Then, in the index.js file, add the following snippet right below the import statements:

Java
 






Add some imports to your index.js file:

Java
 




xxxxxxxxxx
1


1
import { BrowserRouter } from 'react-router-dom';
2
import { Security } from '@okta/okta-react';


Then replace the entire ReactDOM.render statement with the following:

Java
 




xxxxxxxxxx
1


1
ReactDOM.render(
2
  <BrowserRouter>
3
    <Security {...oktaConfig}>
4
      <App />
5
    </Security>
6
  </BrowserRouter>,
7
  document.getElementById('root')
8
);


This sets up Okta to handle user authentication. It also makes it easy to get information about the logged in user by wrapping the App component in the Security component. Now you just have to use it.

To start simple, just change the App.js file to look like this:

Java
 




xxxxxxxxxx
1
23


1
import React from 'react';
2
import { Link, Route } from 'react-router-dom';
3
import { SecureRoute, ImplicitCallback } from '@okta/okta-react';
4
 
          
5
import './App.css';
6
 
          
7
function App() {
8
  return (
9
    <div className="App">
10
      <nav>
11
        <Link to="/">Home</Link>
12
        <Link to="/rating">Rate</Link>
13
      </nav>
14
      <main>
15
        <Route exact path="/" component={()=> 'Home Page'} />
16
        <SecureRoute exact path="/rating" component={()=>'Rating Page'} />
17
        <Route path="/implicit/callback" component={ImplicitCallback} />
18
      </main>
19
    </div>
20
  );
21
}
22
 
          
23
export default App;


Make sure you’ve logged out of your Okta account, and then run the application with:

Shell
 






When you click on the Rate menu item, it should redirect you to log in with Okta. You can use the credentials you use to log in to your Okta dashboard. If you don’t get prompted to log in, it may be because the application still has you logged in. Go to your Okta dashboard and log out, then clear your tokens in the dev tools under Application, LocalStorage, http://localhost:3000. Then try to go to the Rate page again and make sure it prompts you to log in.

NOTE: If you’re still having problems getting the login to work, go back and double-check all your values and code from the previous steps.

Set Up a Firebase Real-Time Datastore

You’ll be storing your app’s beer ratings in a Firebase Real-Time Data Store, so you’ll need an account there. Once you’ve signed up and logged in, click the Add Project tile and choose the name “Brewstr,” then click Continue. On the next screen, turn off analytics (since it’s a test project), then click Create Project. This may take a few moments to get created. Once you see, “Your new project is ready,” click Continue.

You will then be taken to a screen that has three white icons for “Getting Started.” Choose the icon for web apps that should have </> as the text. You’ll need to register the project you just created with Firebase, so put in the name of your app again, “Brewstr," and click Continue. You’ll see a chunk of code there, just copy it to an empty text file for now.

Getting started

Now click Database in the left-hand menu and scroll down to Or choose Realtime Database. In that block, click on Create database and in the dialog that pops up choose Start in test mode then click Enable. The permissions here are completely open for reads and writes. Obviously, this is not the way you’ll want to set it up for production, but in the case of testing, this is the easiest set up.

Back in the editor, add some new keys to the .env.local file:

Java
 




xxxxxxxxxx
1


1
REACT_APP_FIREBASE_APIKEY={yourApiKey}
2
REACT_APP_FIREBASE_AUTH_DOMAIN={yourAuthDomain}
3
REACT_APP_FIREBASE_DB_URL={yourDatabaseUrl}
4
REACT_APP_FIREBASE_PROJECT_ID={yourProjectId}
5
REACT_APP_FIREBASE_STORAGE_BUCKET={yourStorageBucket}
6
REACT_APP_FIREBASE_MESSAGE_SENDER_ID={yourMessagSenderId}
7
REACT_APP_FIREBASE_APP_ID={yourAppId}


Add the firebase package to your app:

Shell

Now you can create a JavaScript file to set up your database connection. Create a file called firebase.js in your src directory.

Java

This will set up the connection to the data store and allow you to use it wherever you need.

NOTE: You may need to stop the application from running (with a CTRL+c) and restart it so it can pick up the environment variables you put in .env.local.

Create a Star Rating Component

The beauty of React is being able to reuse common components and compose pages from those components. Since you’re creating a beer rating site, you’ll create a star rating component that will handle collecting users’ ratings.

Start by creating a folder called components in the src directory and add a rater folder inside that. Then add two files: star-rating.jsx and star-rating.css. In the star-rating.jsx file add the following contents:

Java
 




xxxxxxxxxx
1
67


1
import React, { Component } from 'react';
2
 
          
3
import './star-rating.css';
4
 
          
5
class StarRating extends Component {
6
  constructor(props) {
7
    super(props);
8
    this.state = {
9
      currentRating: this.props.currentRating
10
    };
11
  }
12
 
          
13
  componentDidMount() {
14
    this.setRating();
15
  }
16
 
          
17
  hoverHandler = ev => {
18
    const stars = ev.target.parentElement.getElementsByClassName('star');
19
    const hoverValue = ev.target.dataset.value;
20
    Array.from(stars).forEach(star => {
21
      star.style.color = hoverValue >= star.dataset.value ? 'yellow' : 'gray';
22
    });
23
  };
24
 
          
25
  setRating = ev => {
26
    const stars = this.refs.rating.getElementsByClassName('star');
27
    Array.from(stars).forEach(star => {
28
      star.style.color =
29
        this.state.currentRating >= star.dataset.value ? 'yellow' : 'gray';
30
    });
31
  };
32
 
          
33
  starClickHandler = ev => {
34
    let rating = ev.target.dataset.value;
35
    this.setState({ currentRating: rating }); // set state so the rating stays highlighted
36
    if(this.props.onClick){
37
      this.props.onClick(rating); // emit the event up to the parent
38
    }
39
  };
40
 
          
41
  render() {
42
    return (
43
      <div
44
        className="rating"
45
        ref="rating"
46
        data-rating={this.state.currentRating}
47
        onMouseOut={this.setRating}
48
      >
49
        {[...Array(+this.props.numberOfStars).keys()].map(n => {
50
          return (
51
            <span
52
              className="star"
53
              key={n+1}
54
              data-value={n+1}
55
              onMouseOver={this.hoverHandler}
56
              onClick={this.starClickHandler}
57
            >
58
              &#9733;
59
            </span>
60
          );
61
        })}
62
      </div>
63
    );
64
  }
65
}
66
 
          
67
export default StarRating;


The import statements should be self-explanatory. The class component starts by setting up the basic state with a prop passed in that allows parent components to set an initial rating. In the componentDidMount() function the this.setRating() function is called so the initial rating is reflected in the number of stars highlighted when the component loads.

The next three functions are handlers for the rating component. The hoverHandler() function gets all the star elements in the components and as the user hovers and highlights all the stars up to (and including) the star being hovered over.

The setRating() function is called from componentDidMount() to highlight the stars with the initial rating. It is also called as an event handler from the component when the user moves their mouse away from the rating component without choosing a rating. This will reset the highlighting back to the current rating.

The starClickHandler() is used by the component when a user clicks a rating. It sets the state’s currentRating value so that the highlighting is locked in when the user moves their mouse away from the component. It also emits an event up to the parent’s onClick handler that was passed to the component and passes up the rating that the user clicked on.

The render() method displays a container to hold the stars and then it displays the requested number of stars, using a property passed in from the parent component. it loops through an array with that number of elements and gives each star a value of the current index + 1 to account for the zero-based nature of arrays. The relevant portion is:

Java
 




xxxxxxxxxx
1
13


1
{[...Array(+this.props.numberOfStars).keys()].map(n => {
2
  return (
3
    <span
4
      className="star"
5
      key={n+1}
6
      data-value={n+1}
7
      onMouseOver={this.hoverHandler}
8
      onClick={this.starClickHandler}
9
    >
10
      &#9733;
11
    </span>
12
  );
13
})}


This just uses the spread operator ... on a new array with a size based on the number of stars requested. Each star gets wired to the hoverHandler() and starClickHandler() event handlers. The &#9733; is just the Unicode value of a star. I also added a little style to my stars in the star-rating.css file:

Java
 




xxxxxxxxxx
1


1
.star {
2
  color: gray;
3
}


This just sets the initial color of the stars to gray. You don’t have to do this, but I think it makes it look a lot nicer and it can help if you’re putting the star rater component on a weird colored background.

Consume the Rating Component

Now that you’ve got a rating component, you’ll want to put it on a page. Create a folder in src called pages and inside that add a new rating folder with a rating-page.jsx and rating-page.css file.

The contents of the rating-page.jsx should be:

Java
 




xxxxxxxxxx
1
83


1
import React, { Component } from 'react';
2
import { withAuth } from '@okta/okta-react';
3
 
          
4
import { BrewstrRef } from '../../firebase';
5
import StarRating from '../../components/rater/star-rating';
6
import './rating-page.css';
7
 
          
8
class RatingPage extends Component {
9
 
          
10
  constructor(props) {
11
    super(props);
12
    this.state = {
13
      name: '',
14
      description: '',
15
      rating: 0,
16
      user: ''
17
    };
18
  }
19
 
          
20
  async componentDidMount(){
21
    const user = await this.props.auth.getUser();
22
    this.setState({user:user.email});
23
  }
24
 
          
25
 
          
26
  handleChange = ev => {
27
    this.setState({
28
      [ev.target.name]: ev.target.value
29
    });
30
  };
31
 
          
32
  setRating = rating => {
33
    this.setState({ rating: rating });
34
  };
35
 
          
36
  saveRating = () => {
37
    BrewstrRef.push()
38
      .set(this.state)
39
      .then(() => {
40
        this.props.history.push('/ratinglist');
41
      });
42
  };
43
 
          
44
  render() {
45
    return (
46
      <div className="rating-form">
47
        <div className="heading">Rate A Beer</div>
48
        <div className="form-input">
49
          <label htmlFor="name">Beer:</label>
50
          <input
51
            type="text"
52
            name="name"
53
            id="name"
54
            onChange={this.handleChange}
55
          />
56
        </div>
57
        <div className="form-input">
58
          <label htmlFor="description">Description:</label>
59
          <textarea
60
            name="description"
61
            id="description"
62
            onChange={this.handleChange}
63
          />
64
        </div>
65
        <div className="form-input rating">
66
          <label htmlFor="rating">Rating:</label>
67
          <StarRating
68
            numberOfStars="5"
69
            currentRating="0"
70
            onClick={this.setRating}
71
          />
72
        </div>
73
        <div className="actions">
74
          <button type="submit" onClick={this.saveRating}>
75
            Submit Rating
76
          </button>
77
        </div>
78
      </div>
79
    );
80
  }
81
}
82
 
          
83
export default withAuth(RatingPage);


The import statements bring in the withAuth higher-order component from the @okta/okta-react package. This allows you to get the currently logged in user when saving ratings for that user. This also brings in the Firebase set up and the StarRating component.

At the bottom of the file, you wrap the RatingPage component with the withAuth higher-order component. This allows you to get the currently logged in user in the componentDidMount() function and add the user’s email address to the state. This will be saved with their ratings so that when they go to the RatingList page, they will only see their ratings.

The handleChange() function handles the changing of the text values for the beer name and description in the component’s form. The setRating() handler is what is passed to the rating component so that when a user clicks on a rating, the value is propagated back to the parent and, in this case, is added to the state.

The saveRating() function gets the reference to the Firebase store and pushes a new rating into the collection then the application is routed to the RatingList page.

The render() method is pretty standard except where you add the StarRating component. You set the numberOfStars to five for this rating system, then set the currentRating to zero. You could set it to two or three if you think that looks better. Finally, the reference to the click handler is passed to the StarRating component, so that when a user chooses a rating, the value is bubbled back up to the click handler on this page component.

The stylesheet for this page is unremarkable. It just contains some styles to make it more readable.

Java

Again, you’re bringing in the Okta and Firebase imports. This componentDidMount() is getting the currently logged in user and passing it to Firebase to get a list of the ratings that this user has entered. All queries to Firebase return a “snapshot” represented here by the variable snap and it is pushed onto an array and then the state is set with that array. If you push each “record” onto the array in the state object, the component will redraw each time one is pushed. That’s the reason you push onto another array and then only update the state once. The render() function merely lists the ratings in a table.

Add Routing to the Actual Components

If you remember, all the routing is going to “fake” components that just spit out text right now. You’ll need to go back to the App.js file and make sure the routes are hooked to the components you just created, so that the final file contents are:

Java
 




xxxxxxxxxx
1
27


1
import React from 'react';
2
import { Link, Route } from 'react-router-dom';
3
import { SecureRoute, ImplicitCallback } from '@okta/okta-react';
4
 
          
5
import RatingPage from './pages/rating/rating-page';
6
import RatingsListPage from './pages/rating-list/rating-list';
7
import './App.css';
8
 
          
9
function App() {
10
  return (
11
    <div className="App">
12
      <nav>
13
        <Link to="/">Home</Link>
14
        <Link to="/rating">Rate</Link>
15
        <Link to="/ratinglist">My Ratings</Link>
16
      </nav>
17
      <main>
18
        <Route exact path="/" component={()=> 'Home Page')} />
19
        <SecureRoute exact path="/rating" component={RatingPage} />
20
        <SecureRoute exact path="/ratinglist" component={RatingsListPage} />
21
        <Route path="/implicit/callback" component={ImplicitCallback} />
22
      </main>
23
    </div>
24
  );
25
}
26
 
          
27
export default App;


Here, you just added the imports for the component pages you just created, then updated or added routes to those components.

I also added some styling to my menu in App.css in the src folder:

Java
 




xxxxxxxxxx
1
15


1
nav {
2
  background-color: #333;
3
  font-size: 1.5rem;
4
}
5
 
          
6
nav a {
7
  display: inline-block;
8
  color: white;
9
  padding: 1rem;
10
  text-decoration: none;
11
}
12
 
          
13
nav a:hover {
14
  background-color: black;
15
}


Rate Some Beers

Now when you run your application, you will be able to go to the Rate menu item and add a beer and a rating. If you’re not logged in, it will prompt you to do so. When you’ve entered a beer and rating, you’ll be routed to the listing page with a listing of all the beers you’ve rated.

Entering beer and rating

So what does this all mean? What have you learned? You learned how to create a React component with all the functionality encapsulated within it. You also learned how to pass values to the component and emit values back up to the consuming component. You also learned how to connect to a Firebase Realtime data store and read and write from it. That’s pretty good for a couple of hours of work!

Do Even More with React, Firebase, and Okta for Secure Authentication

Check out more tutorials on these subjects:

If you have any questions, please don’t hesitate to leave a comment below, or ask us on our Okta Developer Forums. Don’t forget to follow us on Twitter @OktaDev, on Facebook and on YouTube!

Topics:
applications developer, authentication, firebase, java, react, react components

Published at DZone with permission of Lee Brandt , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}