Before beginning a project there is one thing you must know, and that is what exactly you are passionate about. I’ll leave that fundamental conundrum to you. Personally, I love to dwell on life on Earth and the folks on it with all the disparate forces within their lives. I decided to do this:
This is what the app looks like in the end:
Quick viewing of the app
This guide will be separated as so:
You can definitely do this.
this is possibly, probably, maybe an exaggeration
npm install -g create-react-app
create-react-app name_of_app_you're_creating
npm start
or yarn start
Take a peak at it:
my-app/ README.md node_modules/ package.json public/ index.html favicon.ico src/ App.css App.js App.test.js index.css index.js logo.svg
node-modules?
public?
favicon.ico
, that’s that little image on your tab. It is referenced in the manifest.json
manifest.json
is mainly used for metadatasrc
This is where just about everything you write will be located
See the index.css
and the App.css
. Both do the same thing. I thought of it this way. What will I want to occur visually globally throughout the app? Put that into the index.css
. All else put into the App.css
. It’s a small app.
The registerServiceWorker.js
, this creates ‘workers’ that do tasks to improve a users experience with a website as well as improving the functioning of the site as a whole.
The index.js
is linking the index.html
to everything you are to write.
The juicy part, the App.js
, is your app. This is the dwelling place. The place where everything comes together. Personally, I deleted everything in the return statement except the div
as I did not want a header.
Notice the data folder I have there. Go find some data, personally I used: WHO data and clicked around until I found data of interest. After the JSON is downloaded, move the files to the data folder.
Now, onto the actual writing of code.
How many individual pages will you have? Well, you should create a component for each one. The beginning of my plan was this. Ten files of data to create ten charts. So I will show how to create the charts.
First, create a new folder to hold these components that you will create and simultaneously create the files associated.
I’ll show you how to create this chart:
I did mention expressing something intense about life
Link to data if curious: Data Set
With data originally structured as so:
{
"dimension": [many_irrelevant_objects],"fact": [
{
"dims": {
"COUNTRY": "Afganhistan",
"YEAR": "2010",
"GHO": "Knowledge about sexual
transmission of AIDS",
"SEX": "Female"
},
"Value": "8.8"
},
...many\_more\_objects of same format
\]
}
For my chart I want the data to end up looking like this:
[{"year": "1993","Burkina Faso": 16,"Ghana": 0,"Kenya": 0},...,{"year": "2010","Burkina Faso": 80.4,"Ghana": 75.05,...}]
Formatting the data
Notice how the initial format has the odd “dimension”
field which is unneeded, all that is needed is within "fact"
‘s "dims"
and "Value"
. So, here I am grabbing those values and setting them to an individual object.
const initialReformat = (data) => {let temp = {}return data.fact.map(row => {temp = row.Valuerow = row.dimsrow.Value = tempreturn row})}
I now have this:
[{COUNTRY: 'Afghanistan',YEAR: '2010',GHO: 'Knowledge about sexual transmission of AIDS',SEX: 'Female',Value: '8.8'},...]
Next, I created an object, set each year to a key whose value is to be an array of every piece of data with a YEAR value correlating.
const sortDataByYear = data => {var sortByYear = {}data.forEach( row => {if (!sortByYear[row.YEAR]) {sortByYear[row.YEAR] = [];sortByYear[row.YEAR].push(row);} else {sortByYear[row.YEAR].push(row);}})return sortByYear}
Quite simple, check if year does not exist, if so, create a new array and push that data into it. Otherwise, push the data into the year array that has already been created.
And I end up with this object:
{1992:[ { COUNTRY: 'Malawi',YEAR: '1992',GHO: 'Knowledge about sexual transmission of AIDS',SEX: 'Male',Value: '0.0' },...],1993: [...],...}
Now, the last function to get the necessitated data structure for the chart:
const finalData = []for (const year in sortByYear) {finalData.push({ year })sortByYear[year].forEach(obj => {if (parseFloat(finalData[i][obj.COUNTRY]) > 0) {finalData[i][obj.COUNTRY] = (finalData[i][obj.COUNTRY] + parseFloat(obj.Value)) / 2}else finalData[i][obj.COUNTRY] =parseFloat(obj.Value).toFixed(2)})i++}
First, I made a brand new array to store the final data. Then, I created a new object for each year and set that year as one of the objects key/value pairs (finalData.push({ year })
). Lastly, I looped through the array corresponding with the year from the previous functions returned object: sortByYear
.
Inside this array, I have two layers to check:
(finalData[i][obj.COUNTRY] + parseFloat(obj.Value)) / 2
.i
is used so the next year is in the next index of the array.
We are at the arranged point. Though, a quick description of the creation of a react component will happen next. Then the visual presentation.
Taking a look at the rendering of a React Component
import React, { Component } from 'react'
class HIVKnowledge extends Component {constructor(props) {super(props)this.state = {data: [],quote: ""}}...
3. Import the data and when the “component mounts”, set the state of the data.
const HIVKnowledgeData = require('../data/diseases-hiv-knowledge.json');
...where I left off above
componentDidMount() {const data = []data = initialReformat(HIVKnowledgeData)this.setState({ data })}
componentDidMount
runs when the page is “mounted” or ready to load and whatever is put here affects the initial rendering.
4. Final data management prior to rendering the page:
render() {let sortByYear = [],finalData = [],i = 0if (this.state.data.length > 0) {sortByYear = sortDataByYear(this.state.data)...then run the last data function on sortByYear}
Check if the length is greater than zero, as componentDidMount
does not occur immediately. So, at the first check of this if statement the data will be an empty array, which is not at all what we want to be inputted into our graph.
The creation of the chart and the rendering of the page
npm install recharts
What to import from recharts:
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
What to put into your index.html right above the <title>
tag (also change that if you want the tab to say something other then React App)
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"><script src="https://code.jquery.com/jquery-2.1.3.min.js" integrity="sha256-ivk71nXhz9nsyFDoYoGf2sbjrR9ddh+XDkCcfZxjvcM="crossorigin="anonymous"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
What to put in your render’s return statement:
return (<div className="chart bar HIV"><h6>Knowlege about sexual transmission of AIDS (AverageBoth Sexes)</h6><ResponsiveContainer width='100%' height={300}><LineChartcx="50%"cy="50%"outerRadius="80%"data={finalData}><XAxis dataKey="year" /><YAxis /><CartesianGrid strokeDasharray="3 3" /><Tooltip /><LineconnectNulls={true}type="monotone"dataKey="Zambia"stroke="black"/>...rest of lines and closing tags
This is what will render on the page. This is JSX. The className is React’s way to set class. Otherwise, the <h6>
is as one expects and notice how the graph is quite HTML’y as well. Notice, a defining factor of JSX components is the camelCase.
As for the chart:
The responsive container does exactly what one expects, gets larger and smaller based upon the screen size.
width
cx
cy
outerRadius
are not random numbers. This is a secret recipe to get a centered location for the chart.
data={finalData}
is how the data is inputted into the chart.
<XAxis datakey=”year” />
is what sets the years as the… x-axis, very complex.
<Tooltip />
creates a lovely user interaction when hovering over data.
<Line … />
is each line with the color, country (key in the array), and connectNulls
will do just that, if the next year is a distance away, the line will connect. Helpful with this data as many countries skip many years between studies.
Export the component at end of file
export default HIVKnowledge
Import the component in App.js
import HIVKnowledge from ‘./poem-1-data/HIVKnowledge’
Render the chart component
Put <HIVKnowledge />
inside the div
within the return statement of the render method.
You should now see the the chart on the page.
Hopefully not
1. I used this module readily available on github: https://github.com/natetyler/wikiquotes-api
Copy and paste it into a file in your app. I used a file called WikiQuote.js
.
2. Import jQuery at the top of this file:
import * as $ from ‘jquery’
3. Export the module:
export const WikiquoteApi = ...
4. Create the necessary error callback:
export const error = (err) => {console.error(err)}
5. Import into the chart component:
import { WikiquoteApi, error } from ‘../WikiQuote.js’
6a. Create the success
callback function within the componentDidMount
so that the quote
variable is set when this is called:
const success = (wikiData) => {quote = `"${wikiData.quote}"`}
6b. Create a checkQuoteLength
function that waits for the WikiQuote function to finish (this is an asynchronous challenge), repeatedly calling itself until that occurs, setting state immediately. Put this also in the componentDidMount
const checkQuoteLength = () => {if (quote.length > 0) {this.setState({ quote, data })} else {setTimeout(checkQuoteLength, 100)}}
6c. Invoke checkQuoteLength
:
checkQuoteLength()
6d. Call the WikiquoteApi.getRandomQuote(title_to_search_in_wikiquotes, success, error)
:
componentDidMount () {let quote = "", data = []const success = (wikiData) => {quote = `"${wikiData.quote}"`}const checkQuoteLength = () => {if (quote.length > 0) {this.setState({ quote, data })} else {setTimeout(checkQuoteLength, 100)}}WikiquoteApi.getRandomQuote("HIV/AIDS", success, error)data = initialReformat(HIVKnowledgeData)checkQuoteLength()}
7. Render the Quote:
return (<div className="chart bar HIV"><h6>Knowlege about sexual transmission of AIDS (AverageBoth Sexes)</h6><p>{this.state.quote}</p><ResponsiveContainer width='100%' height={300}>...
Voila! Now the quote exists on the webpage.
Now you can do all sorts of stuff with this in apps
But you only have a single page with one chart, create more charts at this time before moving on if you are planning to. Next, what will be shown is how to go from one page to the next in a simple way.
Creating some buttons:
I used CSS Button Generator to make my life easier.
Creating a buttons component, for a left and a right button:
Buttons
that will contain the HTML for the buttons.leftButtonDisable: true
and rightButtonDisable: false
.
<div className="buttons"><button className="btn" value="left" disabled={this.state.leftButtonDisable} onClick={this.handleClick}>◀</button><button className="btn" value="right" disabled={this.state.rightButtonDisable} onClick={this.handleClick}>▶</button></div>
3. Create the handleClick
class method:
handleClick(event) {let value = 0if (event.target.value === "left") {value--this.props.onPageChange(value)} else {value++this.props.onPageChange(value)}}
Set value to zero, and if it is left then decrement it, otherwise increment it. And pass that value into… this.props.onPageChange
. Will go over this next.
4. Export the component
Within the App.js:
import Buttons from ‘./Buttons’
currentPage: 0
2. Create an onPageChange
class method and set the this
context to allow the corresponding page to render
constructor (props) {...this.onPageChange = this.onPageChange.bind(this)}
onPageChange(value) {const currentPage = this.state.currentPage + valuethis.setState({ currentPage })}
3. Put the Buttons component in the render and pass onPageChange
and currentPage
as props to the Buttons
component
<ButtonscurrentPage={this.state.currentPage}onPageChange={this.onPageChange}/>
Back to the Buttons Component
Use the life cycle method componentWillRecieveProps
to know when to disable buttons
componentWillReceiveProps(nextProps) {let currentPage = nextProps.currentPageif (currentPage === 0) {this.setState({leftButtonDisable: true})}else if (currentPage === 9) {this.setState({rightButtonDisable: true})} else {this.setState({leftButtonDisable: false,rightButtonDisable: false})}}
This runs whenever the props that App.js
is passing down to buttons.js
changes, in this case, currentPage
changes. When currentPage
changes componentWillReceiveProps
runs and nextProps
is an object with key/values of the props being passed down as the incoming value. This allows you to alter the state at this instance, and in turn alter the component being viewed visually on the page.
Ah buttons, wonderful buttons
Rendering different pages based upon **currentPage**
renderPage(curPage) {switch (curPage) {case 0:return <HIVKnowledge />case 1:return <LifeExpectancy />...default:return console.error("Something is wrong with your buttons")}}
2. call this function in the return
statement of the render
class method
<div className="App">{this.renderPage(this.state.currentPage)}<Buttons currentPage={this.state.currentPage} onPageChange={this.onPageChange} /></div>
And now, possibly, hopefully you know how to create a multi-page react app.
If yes, you must feel this way
Info is thanks to this Medium Article.
package.json
and add two new fields as so:
{..."private": true, //below this add:"homepage": "https://github_username.github.io/repository_name", ...
"scripts": {"eject": "react-scripts-eject", //below this add:"deploy: "npm run build&&gh-pages -d build"...}
2. Then do this: npm install --save-dev gh-pages
. Push your code to master.
3. npm run build
which will create a bundle of all the code in your application (after running it, look in your file system and you will see a build
folder containing all of the code in a tightly woven, unreadable format).
4. npm run deploy
is the last step in the terminal
5. Go onto your GitHub account. Go to the applications page, then to settings, scroll down until you reach the GitHub Pages, click the Source dropdown and find gh-pages branch
then wait a moment and you are…
Deployed!
In the future, if you edit some code or something similar be sure to run npm run build
then npm run deploy
for your deployed app to be affected!
buenisimo
No matter your skill level in React, I aimed to make sure this article held nuances that anyone can learn from. This was written directly after completing the aforementioned project so the troubles, the tribulations are fresh on my mind (in relation to the person I am this moment, while I am typing these words, that are reference).
Still there are countless resources in React, and this was only a few of a giant assortment of resources. If you read this but know there is something else you want to do, check this repo out: awesome-react-components.
Here’s a quote by Marcel Proust:
“Every reader finds himself. The writer’s work is merely a kind of optical instrument that makes it possible for the reader to discern what, without this book, he would perhaps never have seen in himself.”
Let’s hope that React is a tool that helps you write code that does the same.
There is a corresponding CSS article… here
And this is the repo: Quote and a Chart
And thank’s Doug and Kyle for the proofreading!
All the best in your journey, I am regularly creating content so follow me if you learned a thing or two, and those clap thingies are surely welcome. Comments too.