Categories
internet tech

Build a Star Rating Component for React

Who does not love beer? When you drink an ideal beer you need to inform somebody. You no doubt need so that you could remind your self of the good beers you might have had. Enter Brewstr, a lager ranking application that lets you input a lager you might be ingesting and provides it a ranking. This means, you recognize what to get subsequent time since there is no means you’ll be able to commit it to memory later.

Table of Contents

React provides the facility to create an element for ranking that can be utilized and re-used any place a ranking element is wanted. You may just even upload it in your corporate’s inside package deal control gadget in order that the element is well ate up via any React application that wishes it.

To construct this app, you’re going to want:

To get this factor kicked off, scaffold a fundamental React application via operating the next command within the terminal in a folder the place you would love to retailer your code:

create-react-app brewstr

Once this program has created your React application’s skeleton, turn out to be your code listing and open it for your favourite code editor. I’ll be the usage of Visual Studio Code, so I’ll run the next command:

code .

Now that you’ve a fundamental React app, you’ll be able to wish to arrange Okta to permit customers to authenticate into your application. If you do not have already got one, create a free-forever Okta developer account. Once you might have logged in, consider of your Okta org URL at the best proper of the dashboard. Then select Applications from the primary menu and click on Add Application.

In the wizard, select SinglePage App from the to be had platforms and click on Next. In the Application Settings web page it would be best to title the app “Brewstr” and alter the BaseURIs, and Login redirect URIs values to make use of port 3000, and click on Done. Take be aware of the Client ID to your newly created app.

Essential Reading: Learn React from Scratch! (2019 Edition)

Back within the React application, upload two programs in your application:

yarn upload @okta/[email protected]^1.2.3 [email protected]^5.1.2

Create a .env.native record on the root of the mission and upload the next to it:

REACT_APP_OKTA_CLIENT_ID={yourClientIdentification}
REACT_APP_OKTA_ORG_URL=https://{yourOktaOrgUrl}

Then, within the index.js record, upload the next snippet proper underneath the import statements:

const oktaConfig = { issuer: `${procedure.env.REACT_APP_OKTA_ORG_URL}/oauth2/default`, redirect_uri: `${window.location.beginning}/implicit/callback`, client_id: procedure.env.REACT_APP_OKTA_CLIENT_ID,
};

Add some imports in your index.js record:

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

Then exchange all the ReactDOM.render commentary with the next:

ReactDOM.render( <BrowserRouter> <Security {...oktaConfig}> <App /> </Security> </BrowserRouter>, record.getElementById('root')
);

This units up Okta to take care of person authentication. It additionally makes it simple to get details about the logged in person via wrapping the App element within the Security element. Now you simply have to make use of it.

To get started easy, simply exchange the App.js record to appear to be this:

import React from 'react';
import { Link, Route } from 'react-router-dom';
import { ProtectedRoute, ImplicitCallback } from '@okta/okta-react'; import './App.css'; serve as App() { go back ( <div className="App"> <nav> <Link to="/">Home</Link> <Link to="/ranking">Rate</Link> </nav> <primary> <Route actual trail="/" element={()=> 'Home Page'} /> <ProtectedRoute actual trail="/ranking" element={()=>'Rating Page'} /> <Route trail="/implicit/callback" element={ImplicitCallback} /> </primary> </div> );
} export default App;

Make positive you might have logged from your Okta account, after which run the application with:

yarn get started

When you click on at the Rate menu merchandise, it must redirect you to log in with Okta. You can use the credentials you utilize to log in in your Okta dashboard. If you do not get precipitated to log in, it can be since the application nonetheless has you logged in. Go in your Okta dashboard and sign off, then transparent your tokens within the dev equipment underneath Application, LocalStorage, http://localhost:3000. Then attempt to pass to the Rate web page once more and ensure it activates you to log in.

NOTE: If you might be nonetheless having issues getting the login to paintings, return and double-check your entire values and code from the former steps.

You’ll be storing your app’s beer scores in a Firebase Real-Time Data Store, so you’ll be able to want an account there. Once you might have signed up and logged in, click on the Add Project tile and select the title “Brewstr”, then click on Continue. On the following display screen, flip off analytics (since it is a take a look at mission), then click on Create Project. This might take a couple of moments to get created. Once you spot, “Your new mission is able”, click on Continue.

You will then be taken to a display screen that has three white icons for “Getting Started”. Choose the icon for internet apps that are meant to have </> because the textual content. You’ll wish to sign up the mission you simply created with Firebase, so put within the title of your app once more, “Brewstr”, and click on Continue. You’ll see a bit of code there, simply reproduction it to an empty textual content record for now.

Now click on Database within the left-hand menu and scroll right down to Or select Realtime Database. In that block, click on on Create database and within the conversation that pops up select Start in take a look at mode then click on Enable. The permissions listed here are totally open for reads and writes. Obviously, this isn’t the way in which it would be best to set it up for manufacturing, however in relation to trying out, that is the very best arrange.

Back within the editor, upload some new keys to the .env.native record:

REACT_APP_FIREBASE_APIKEY={yourApiKey}
REACT_APP_FIREBASE_AUTH_DOMAIN={yourAuthDomain}
REACT_APP_FIREBASE_DB_URL={yourDatabaseUrl}
REACT_APP_FIREBASE_PROJECT_ID={yourProjectIdentification}
REACT_APP_FIREBASE_STORAGE_BUCKET={yourStorageBucket}
REACT_APP_FIREBASE_MESSAGE_SENDER_ID={yourMessagSenderId}
REACT_APP_FIREBASE_APP_ID={yourAppIdentification}

Add the firebase package deal in your app:

yarn upload [email protected]

Now you’ll create a JavaScript record to arrange your database connection. Create a record known as firebase.js for your src listing.

import * as firebase from 'firebase'; var firebaseConfig = { apiKey: procedure.env.REACT_APP_FIREBASE_API_KEY, authDomain: procedure.env.REACT_APP_FIREBASE_AUTH_DOMAIN, databaseURL: procedure.env.REACT_APP_FIREBASE_DB_URL, projectId: procedure.env.REACT_APP_FIREBASE_PROJECT_ID, storageBucket: procedure.env.REACT_APP_FIREBASE_STORAGE_BUCKET, messagingSenderId: procedure.env.REACT_APP_FIREBASE_MESSAGE_SENDER_ID, appId: procedure.env.REACT_APP_FIREBASE_APP_ID
}; firebase.initializeApp(firebaseConfig);
const databaseRef = firebase.database().ref();
export const BrewstrRef = databaseRef.kid('scores');

This will arrange the relationship to the knowledge retailer and help you use it anyplace you want.

NOTE: You might wish to forestall the application from operating (with a CTRL+c) and restart it so it may well select up the surroundings variables you installed .env.native.

The wonderful thing about React is having the ability to reuse not unusual elements and compose pages from the ones elements. Since you might be developing a lager ranking web page, you’ll be able to create a celeb ranking element that can take care of accumulating customers’ scores.

Start via making a folder known as elements within the src listing and upload a rater folder inside of that. Then upload two recordsdata: star-rating.jsx and star-rating.css. In the star-rating.jsx record upload the next contents:

import React, { Component } from 'react'; import './star-rating.css'; magnificence StarRating extends Component { constructor(props) { tremendous(props); this.state = { presentRating: this.props.presentRating }; } componentDidMount() { this.setRating(); } hoverHandler = ev => { const stars = ev.goal.parentElement.getElementsByClassName('famous person'); const hoverValue = ev.goal.dataset.price; Array.from(stars).forEach(famous person => { famous person.taste.shade = hoverValue >= famous person.dataset.price ? 'yellow' : 'grey'; }); }; setRating = ev => { const stars = this.refs.ranking.getElementsByClassName('famous person'); Array.from(stars).forEach(famous person => { famous person.taste.shade = this.state.presentRating >= famous person.dataset.price ? 'yellow' : 'grey'; }); }; starClickHandler = ev => { let ranking = ev.goal.dataset.price; this.setState({ presentRating: ranking }); if(this.props.onClick){ this.props.onClick(ranking); } }; render() { go back ( <div className="ranking" ref="ranking" data-rating={this.state.presentRating} onMouseOut={this.setRating} > {[...Array(+this.props.numberOfStars).keys()].map(n => { go back ( <span className="famous person" key={n+1} data-value={n+1} onMouseOver={this.hoverHandler} onClick={this.starClickHandler} ></span> ); })} </div> ); }
} export default StarRating;

The import statements must be self-explanatory. The magnificence element begins via putting in place the fundamental state with a prop handed in that permits father or mother elements to set an preliminary ranking. In the componentDidMount() serve as the this.setRating() serve as is known as so the preliminary ranking is mirrored within the selection of stars highlighted when the element a lot.

The subsequent three purposes are handlers for the ranking element. The hoverHandler() serve as will get the entire famous person parts within the elements and because the person hovers and highlights the entire stars as much as (and together with) the famous person being hovered over.

The setRating() serve as is known as from componentDidMount() to spotlight the celebs with the preliminary ranking. It is also known as as an match handler from the element when the person strikes their mouse clear of the ranking element with out opting for a ranking. This will reset the highlighting again to the present ranking.

The starClickHandler() is utilized by the element when a person clicks a ranking. It units the state’s presentRating price in order that the highlighting is locked in when the person strikes their mouse clear of the element. It additionally emits an match as much as the father or mother’s onClick handler that used to be handed to the element and passes up the ranking that the person clicked on.

The render() approach presentations a container to carry the celebs after which it presentations the asked selection of stars, the usage of a belongings handed in from the father or mother element. it loops via an array with that selection of parts and provides each and every famous person a worth of the present index + 1 to account for the zero-based nature of arrays. The related portion is:

{[...Array(+this.props.numberOfStars).keys()].map(n => { go back ( <span className="famous person" key={n+1} data-value={n+1} onMouseOver={this.hoverHandler} onClick={this.starClickHandler} ></span> );
})}

This simply makes use of the unfold operator ... on a brand new array with a length in response to the selection of stars asked. Each famous person will get stressed to the hoverHandler() and starClickHandler() match handlers. The is solely the Unicode price of a celeb. I additionally added just a little taste to my stars within the star-rating.css record:

.famous person { shade: grey;
}

This simply units the preliminary shade of the celebs to grey. You do not need to try this, however I believe it makes it glance so much nicer and it may well assist if you are striking the famous person rater element on a unusual coloured background.

Now that you have got a ranking element, it would be best to put it on a web page. Create a folder in src known as pages and inside of that upload a brand new ranking folder with a rating-page.jsx and rating-page.css record.

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

import React, { Component } from 'react';
import { withAuth } from '@okta/okta-react'; import { BrewstrRef } from '../../firebase';
import StarRating from '../../elements/rater/star-rating';
import './rating-page.css'; magnificence RatingPage extends Component { constructor(props) { tremendous(props); this.state = { title: '', description: '', ranking: 0, person: '' }; } async componentDidMount(){ const person = look forward to this.props.auth.getUser(); this.setState({person:person.electronic mail}); } handleChange = ev => { this.setState({ [ev.goal.title]: ev.goal.price }); }; setRating = ranking => { this.setState({ ranking: ranking }); }; saveRating = () => { BrewstrRef.push() .set(this.state) .then(() => { this.props.historical past.push('/ratinglist'); }); }; render() { go back ( <div className="rating-form"> <div className="heading">Rate A Beer</div> <div className="form-input"> <label htmlFor="title">Beer:</label> <enter kind="textual content" title="title" identification="title" onChange={this.handleChange} /> </div> <div className="form-input"> <label htmlFor="description">Description:</label> <textarea title="description" identification="description" onChange={this.handleChange} /> </div> <div className="form-input ranking"> <label htmlFor="ranking">Rating:</label> <StarRating numberOfStars="5" presentRating="0" onClick={this.setRating} /> </div> <div className="movements"> <button kind="post" onClick={this.saveRating}> Submit Rating </button> </div> </div> ); }
} export default withAuth(RatingPage);

The import statements carry within the withAuth higher-order element from the @okta/okta-react package deal. This permits you to get the recently logged in person when saving scores for that person. This additionally brings within the Firebase arrange and the StarRating element.

At the ground of the record, you wrap the RatingPage element with the withAuth higher-order element. This permits you to get the recently logged in person within the componentDidMount() serve as and upload the person’s electronic mail cope with to the state. This can be stored with their scores in order that once they pass to the RatingListing web page, they are going to simplest see their scores.

The handleChange() serve as handles the converting of the textual content values for the beer title and outline within the element’s variety. The setRating() handler is what’s handed to the ranking element in order that when a person clicks on a ranking, the price is propagated again to the father or mother and, on this case, is added to the state.

The saveRating() serve as will get the connection with the Firebase retailer and pushes a brand new ranking into the gathering then the application is routed to the RatingListing web page.

The render() approach is lovely usual with the exception of the place you upload the StarRating element. You set the numberOfStars to five for this ranking gadget, then set the presentRating to 0. You may just set it to two or three in the event you assume that appears higher. Finally, the connection with the press handler is handed to the StarRating element, in order that when a person chooses a ranking, the price is bubbled again as much as the press handler in this web page element.

The stylesheet for this web page is unremarkable. It simply comprises some types to make it extra readable.

.rating-form { width: 50%; margin: 1rem auto; padding: 1rem;
} .heading { padding: .5rem; background-color: black; shade: white; font-size: 1.5rem; margin-bottom: 1rem;
} .form-input { margin: 1rem;
} .form-input label { font-weight: daring; show: block;
} .form-input enter,
.form-input textarea { font-size: 1.5rem; width: 100%;
} .form-input textarea { top: 5rem;
} .form-input.ranking { text-align: proper;
} .form-input.ranking .ranking { font-size: 2rem;
} .movements { padding: 1rem; text-align: proper;
} .movements button { padding: .5rem;
}

The simplest factor left is so as to add an inventory of the person’s scores. Add a brand new folder underneath pages known as rating-list and upload two recordsdata: rating-list.jsx and rating-list.css. First, the CSS record:

desk { width: 80%; margin: 1rem auto; font-size: 1.25rem; border-collapse: fall down;
} desk thead { background-color: black; shade: #FFF;
} desk, th, td { border: 1px cast black;
} th, td { padding: .5rem;
} td.rating-value { text-align: middle;
}

Then the true web page element:

import React, { Component } from 'react';
import { withAuth } from '@okta/okta-react';
import {BrewstrRef} from '../../firebase';
import './rating-list.css'; magnificence RatingsListingPage extends Component { constructor(props){ tremendous(props); this.state = { scores: [], person:'' }; } async componentDidMount(){ const person = look forward to this.props.auth.getUser(); BrewstrRef.orderByChild('person').equivalentTo(person.electronic mail).on('price', snap => { const reaction = snap.val(); const scores = []; for(let ranking in reaction){ scores.push({identification: ranking, ...reaction[ranking]}); } this.setState({ scores: scores }); }); } render(){ go back ( <desk className="ratings-list"> <thead> <tr> <th>Beer</th> <th>Description</th> <th>Rating</th> </tr> </thead> <tbody> {this.state.scores.map((ranking) => { go back ( <tr className="ranking" key={ranking.identification}> <td>{ranking.title}</td> <td>{ranking.description}</td> <td className="rating-value">{ranking.ranking}</td> </tr> ) })} </tbody> </desk> ) }
} export default withAuth(RatingsListingPage);

Again, you might be bringing within the Okta and Firebase imports. This componentDidMount() is getting the recently logged in person and passing it to Firebase to get an inventory of the scores that this person has entered. All queries to Firebase go back a “snapshot” represented right here via the variable snap and it’s driven onto an array after which the state is ready with that array. If you push each and every “file” onto the array in the state object, the element will redraw each and every time one is driven. That’s the explanation you push onto every other array after which simplest replace the state as soon as. The render() serve as simply lists the scores in a desk.

If you bear in mind, the entire routing goes to “pretend” elements that simply spit out textual content at the moment. You’ll wish to return to the App.js record and ensure the routes are hooked to the elements you simply created. in order that the general record contents are:

import React from 'react';
import { Link, Route } from 'react-router-dom';
import { ProtectedRoute, ImplicitCallback } from '@okta/okta-react'; import RatingPage from './pages/ranking/rating-page';
import RatingsListingPage from './pages/rating-list/rating-list';
import './App.css'; serve as App() { go back ( <div className="App"> <nav> <Link to="/">Home</Link> <Link to="/ranking">Rate</Link> <Link to="/ratinglist">My Ratings</Link> </nav> <primary> <Route actual trail="/" element={()=> 'Home Page')} /> <ProtectedRoute actual trail="/ranking" element={RatingPage} /> <ProtectedRoute actual trail="/ratinglist" element={RatingsListingPage} /> <Route trail="/implicit/callback" element={ImplicitCallback} /> </primary> </div> );
} export default App;

Here, you simply added the imports for the element pages you simply created, then up to date or added routes to these elements.

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

nav { background-color: #333; font-size: 1.5rem;
} nav a { show: inline-block; shade: white; padding: 1rem; text-decoration: none;
} nav a:hover { background-color: black;
}

Now while you run your application, it is possible for you to to visit the Rate menu merchandise and upload a lager and a ranking. If you might be no longer logged in, it’ll steered you to take action. When you might have entered a lager and ranking, you’ll be able to be routed to the checklist web page with an inventory of the entire beers you might have rated.

So what does this all imply? What have you ever realized? You realized easy methods to create a React element with the entire capability encapsulated inside it. You additionally realized easy methods to go values to the element and emit values again as much as the eating element. You additionally realized how to hook up with a Firebase Realtime information retailer and browse and write from it. That’s lovely just right for a few hours of labor!

Check out extra tutorials on those topics:

If you’ve gotten any questions, please don’t hesitate to go away a remark underneath, or ask us on our Okta Developer Forums. Don’t disregard to practice us on Twitter @OktaDev, on Facebook and on YouTube!

Like this newsletter? Follow @leebrandt on Twitter

This content material is subsidized by way of Syndicate Ads.