pure react: a step-by-step guide to mastering react

154

Upload: others

Post on 11-Sep-2021

12 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Pure React: A step-by-step guide to mastering React
Page 2: Pure React: A step-by-step guide to mastering React

PureReactDaveCeddia

Copyright(c)2017DaveCeddia

Page 3: Pure React: A step-by-step guide to mastering React

IntroductionWelcometoPureReact.

There’saproblemwithfrontenddevelopmenttoday:it’soverwhelming.

Therearethousandsoflibrariesoutthere,eachdoingonelittlething.Groupsofthemevolvetobecomedefactostandards,butwithnoactualstandardsinsight.

TheReactecosystemisespeciallyguiltyofthis:there’sReact,Redux,Webpack,Babel, ReactRouter, and on and on.Hundreds of boilerplate projects exist tomake this “easier” bybundling a bunchof choices together. “It’ll be easier tolearn,”thethinkinggoes,“ifyoudon’thavetomakeallthechoicesyourself.”

Buttheoppositehappens:insteadofbeingoverwhelmedbythechoices,you’renow overwhelmed by the sheer amount of code that came “for free”with theboilerplate,andyouhaveNOIDEAwhatanyofitactuallydoes.That’sascaryplacetobe.

Learningeverythingatonceismassivelyoverwhelming.Sointhisbook,wewilltakeadifferentapproach.Amoresaneapproach.WewilllearnPureReact.

Pure React: The core concepts of React, in isolation, without Redux,Webpack,andtherest.

Whenyouachievewhat’scontainedhere–whenyoulearnReactcold,you’llbeabletogoonandlearnallofitsfriendswithease:Redux,Router,andtherest.

Not onlywill you beable to learn those other libraries, but youwill bewell-equipped.You’llhaveasolidfoundation.

This bookhas beendesigned to get you from zero toReact quickly, andwithmaximumunderstanding.

Whatyouwon’tbedoinghereiswhatplaysoutinmanytutorialsacrossontheweb,whereyoucopyandpasteeachblockofcodeuntilyouhaveaworkingappattheend.“Voila!”theysay.“NowyouknowReactandReduxandWebpack!”

Page 4: Pure React: A step-by-step guide to mastering React

It’s too much at once. You might learn one or two core concepts with thisapproach, but it’s a shaky foundation. Inevitably,when you sit down towriteyourownapp,yougetlostintheforestoflibrariesandconcepts.

Youcanonlygetsofarbycopyingandpastingpiecesofcode.

Andyouknowthisalready,otherwiseyouwouldn’tbehere.

Soherewe’ll followadifferentapproach: I’ll showyouaconcept,with someexamplecode.Then(andthisistheimportantpart)youwillusethatconceptintheexercises that follow,until it’ssecondnature.Rinseandrepeatuntilwe’vecoveredallthecorepiecesofpureReact–andtherearen’ttoomany.

WhatWe’llCover

Wewill start where most programming books start, with HelloWorld. Fromthere,we’lllookathowtocomposecomponentstogetherandhowtoworkwithJSX,React’sHTML-likesyntaxforrenderingelementstothepage.

Onceyouhaveagrasponhow tocreate staticcomponents,you’ll learnabout“props” as a way to pass in the data they need, and “propTypes” fordocumentinganddebuggingthepropsthatacomponentrequires.

We’ll cover React’s special “children” prop, which is a powerful tool forbuildingreusable,composablecomponents.

Finally, you’ll learn about “state,” how it differs from props, and how toorganize it in an application. We’ll look at using form controls and theComponentLifecycle.

WhyJustReact?

Without a solid understanding of React, simultaneously learning libraries likeReduxandtools likeWebpackwillonlyslowdownyour learningprocess.It’svery tempting to dive in and learn it all at once, especially if you have a funprojectinmind(oradeadlinetomeet).

However,learningeverythingatoncewillbeslowerinthelongrun.

Page 5: Pure React: A step-by-step guide to mastering React

Thinkoftheselibrariesandtoolsaslayersinafoundation.

Ifyouwerebuildingahouse,wouldyouskipsomestepstoget itdonefaster?Say, start pouring the concretebefore laying some rocksdown?Start buildingthewallsonbareearth?

Or how about making a wedding cake: the top part looks the most fun todecorate,sowhynotstartthere?Justfigureoutthebottompartlater!

No?

Of course not. You know those things would lead to failure. They would,perhapscounterintuitively,slowthingsdownratherthanspeedthemup.

SodoesitmakesensetoapproachReactbytryingtolearnWebpack+Babel+React+Redux+Routing+AJAXallatonce?Doesn’tthatsoundlikeatonofoverwhelmingconfusion?

Instead, themost efficient approach is to learn these one at a time.This bookwillteachyouhowtouseReact,andthenyou’llbereadytotacklethenextpieceofthepuzzle.

Page 6: Pure React: A step-by-step guide to mastering React

HowThisBookWorks

HowMuchTimeWillThisTake?

ThebasicconceptsofReactcanbelearnedinamatterofdays.Thisbookcoversthose basics and also contains exercises after eachmajor concept to reinforceyourunderstanding.

Most of the exercises are short. There are a few that aremore involved. Theprinciple behind them is the same as the idea behind homework in school: todrilltheideasintoyourheadbycombiningrepetitionandproblemsolving.

The theme behind the whole process is this: avoid getting overwhelmed.Quittingwon’tgetyouanywhere.Slowandsteady,uh,learnstheReact.

BuildSmallThingsandThrowThemAway

Thisistheawkwardmiddlestepthatalotofpeopleskip.MovingontoReduxand other libraries without having a firm grasp of React’s concepts will leadstraightbacktooverwhelmsville.

But this step isn’t verywell-defined:what should you build?A prototype forwork?MaybeafancyFacebookclone,somethingsubstantialthatusesthewholestack?

Well,no,notthosethings.They’reeitherloadedwithbaggageortoolargeforalearningproject.Youwanttobuildsmallthings.

Don’tBuildaPrototype

“Prototypes”(forwork)areusuallyterriblelearningprojects,becauseyouknowin your heart that a “prototype” will never die. It will live long beyond theprototype phase,morph into shipping software, and never be thrown away orrewritten.Assoonassomemanagerseesthatitworks,featureswillbepiledon.“We’llrefactoritsomeday”willturnouttobealie.Thecodewillgrowbloatedanddisorganized.

Page 7: Pure React: A step-by-step guide to mastering React

All of these, andmore, are the reasonswhy a prototype is a bad choice as alearningproject.

Whenyouknowitwon’tbethrowawaycode,thefutureloomslarge.Youstartto worry… Shouldn’t it have tests? I should make sure the architecture willscale…Am I going to have to refactor thismess later?And shouldn’t it havetests?

Worryingaboutarchitectureandscalabilityand“thefuture”isabadstrategyforlearningthebasicsofanewtechnology.

Ontheflipside,ifyoubuildaprototypebelievingthatitisthrowawaycode,itprobably won’t be very good code. Then when your boss’ boss sees howawesometheprototypelooks,hewillabsolutelynotallowyoutorewriteitwithallthebestpracticesyou’velearned.That’sarecipeforanunmaintainablecodebase.

SoWhatShouldYouBuild?

This book exists to answer this question, and help you through it. The shortanswer:

Buildsmall,throwawayapps.

The sweet spot is somewhere between “Hello World” and “entire clone ofTwitter.”

We’ll start off with HelloWorld, of course. No self-respecting programmingbookwouldbecompletewithoutthat.

Asyourskillsetgrows,low-fidelitycopiesofsimpleappsandsiteslikeReddit,HackerNews, andSlackmake good projects.Designers call this “copywork,”and it’sgreatbecause it freesyou fromhaving tomakeproductdecisions like“whereshouldtheusergoafterlogin.”YoucansimplyfocusonlearningReact.

By the end of this book you’ll be building replicas of those popular apps andmore. They’ll come together quickly once you can clearly “think incomponents,”askillyou’lldevelopasyouprogressthroughthebook.

LearningWithSmallProjects

Page 8: Pure React: A step-by-step guide to mastering React

LearningWithSmallProjects

Ibelievethatyoucangetmorelearningvalueoutofsmallprojectsthanlargeorfull-stack ones, at least in the beginning.Here’s an idea ofwhat Imean. Thecoloredbars are periodsofmaximum learning, and thegaps arewhereyou’redoingthingsyoualreadyknowhowtodo:

Atsomepoint,thelargerprojectshavediminishingreturns.Thefirstfewtimesyouuseatextinputandhavetowireituptomaintainitsstate,you’relearning.Bythetenthtime,it’sboring.

That isn’t to say that largeprojects aren’tvaluable,but Idon’tbelieve they’regood first projects. Start small, build a few small things, then build a biggerthingortwo.

Thisistheideabehinddeliberatepractice–thepracticeshouldbejustbeyondyourcurrentskill level.Notsohardthatyougetfrustratedandquit,butnotsoeasythatyoucanbreezerightthrough,either.

EnvironmentSetup

Beforewedivein,we’llneedtosetupanenvironment.

Don’tworry,there’snoboilerplatetoclonefromGitHub.NoWebpackconfig,either.

Instead, we’re using Create React App, a tool Facebook made. It provides astarterprojectandbuilt-inbuildtoolssoyoucanskiptothefunpart–creatingyourapplication!

Prerequisites

Tools

Node.js(atleastv4.0,>=6recommended)NPM(version>=3recommended)oryarnGoogleChrome(orsomeothermodernbrowser)ReactDeveloperToolsYourtexteditororIDEofchoice

Page 9: Pure React: A step-by-step guide to mastering React

You’llneedNodeandNPM(orYarn).Headtohttps://nodejs.organddownloadthelatest“Current”release.Atthetimeofwriting,thisisv7.10.0.

Anymodernbrowsershouldsuffice.ThisbookwasdevelopedagainstChrome,so that’s what I recommend. If you prefer another browser, I expect theJavaScriptwillworkcorrectlybuttheCSScouldrequiresometweaks.

Yarn

Yarn is a relatively new packagemanager for JavaScript, released in June of2016.Compared toNPM, it is faster, andhas thebenefit of a lock file,whichmeans it can reliably install theexact samepackagesevery timeyou runyarn(Yarn’sequivalenttonpminstall).

ThecommandsaresimilartoNPM’s:

Installallpackages:npminstalloryarnInstall a certain package: npm install --save <package> or yarn add<package>

Startthedevelopmentserver:npmstartoryarnstartRunthetests:npmtestoryarntest

Rather than padding the pages with things like “Run npm start (or yarnstart)” throughout thebook, Iwillonlyshow theNPMcommands fromhereon. However, feel free to use Yarn if you’d like. You can install it fromhttps://yarnpkg.com.

ReactDeveloperTools

TheReactDeveloperToolscanbeinstalledfromhere:

https://github.com/facebook/react-devtools

Followtheinstructionstoinstallthetoolsforyourbrowser.TheReactdevtoolsallowyoutoinspecttheReactcomponenttree(asopposedtotheDOMtree)andviewthepropsandstateassignedtoeachcomponent.Itisextremelyusefulfordebugging.

Knowledge

Page 10: Pure React: A step-by-step guide to mastering React

You should already know JavaScript (at least ES5), HTML, and CSS. I’llexplaintheES6featuresastheycomeup(youdon’tneedtoalreadyknowES6).

I don’t recommend learning JavaScript and React at the same time. Wheneverythinglooksnew,itcanbehardtotellwhere“JavaScript”endsand“React”begins.IfyouneedtobrushuponJS,herearesomegood(andfree!)resources:

SpeakingJavaScript(book):http://speakingjs.com/Exercism(exercises):http://exercism.io/languages/javascriptYou Don’t Know JS (book series): https://github.com/getify/You-Dont-Know-JS

Apassingfamiliaritywiththecommandlinewillbehelpfulaswell.

ProjectDirectory

You’llbewritingalotofcodethroughoutthisbook.Tokeepitorganized,createadirectoryfortheexercises.Nameitpure-react,orwhateveryoulike.

InstallCreateReactApp

Runthiscommandtoinstallthetoolglobally(the-gmeansglobal):

$npminstall-gcreate-react-app

Ifyougetanerrorsaying“Permissiondenied”(andyou’reonLinuxoraMac)youmayneedtore-runthecommandwith“sudo”,likethis:

$sudonpminstall-gcreate-react-app

That’sit!Let’sgettocoding.

DebuggingCrashCourse

Whenthingsgowrong,herearethestepstofollow:

1. Don’tpanic.2. Manually refresh the page. Sometimes the auto-refreshing mechanism

breaks.

Page 11: Pure React: A step-by-step guide to mastering React

3. Check the browser dev console, manually refresh the page, and fix anyerrorsyousee.

4. Checkthecommandlineconsolewhereyourannpmstart.Lookforanyerrors,andfixthose.

To open the dev console in Chrome on Mac, press Option+Command+I. InChromeonWindowsorLinux,pressCtrl+Shift+I.Thenmakesureyouareonthe“Console”tab.

If you don’t know how to open your browser’s Developer Tools, Google for“openbrowserconsole[your_browser]”.

Likewise, if you have no idea what an error means, copy and paste it intoGoogle.

Still completely stuck? Stack Overflow has an abundance of React-relatedquestions and answers, and the Reactiflux community athttps://www.reactiflux.com/isfulloffriendlypeoplewhocanhelp.Thereisalsoa#reactIRCchannelonFreenode,whichyouconnecttowithanIRCclientorvisithttp://irc.lc/freenode/reactjs.

KeeptheConsoleOpen!

Thedevconsoleisanamazinglyhelpfultoolfordebugging,andit’sagreatideato leave itopenwhileyouwork through theexamplesandexercises. It’llhelpyoucatchtyposandsaveyoualotoftimehuntingdownproblems.

ReactDevTools

If you installed the React dev tools, you’ll see a “React” tab in the browserdevtools,likethis:

Page 12: Pure React: A step-by-step guide to mastering React

Choosethistab,andlookforyourcomponentinthetree.Youcanusethesearchboxtofinditwithouthavingtodrilldown.Whenyouclickonacomponent,itsProps andStatewill appear in the sidepane.Make sure thevalues agreewithwhatyouexpect to see.Youcanevenmodify thePropsandState right in thesidepane,andseethecomponentre-renderwithyourchanges.Tryitout!

Page 13: Pure React: A step-by-step guide to mastering React

HelloWorldAt this point you havenode,npm, andcreate-react-app installed.All of thetoolsareready.Let’swritesomecode!

Step1

Usethecreate-react-appcommandtogenerateanewproject.Itwillcreateadirectory and install all the necessarypackages, and thenwe’llmove into thatnewdirectory.

$create-react-appreact-hello

$cdreact-hello

The generated project contains some files wewon’t need right now, so we’lldeletethem.Youcouldalsojustignorethem.

$rmsrc/App.*src/index.csssrc/logo.svg

Step2

Openupthefilesrc/index.js.Deleteallofthecontents,andtypethiscodein:

Type it outbyhand?Like a savage? Typing it drills it into your brainmuchbetterthansimplycopyingandpastingit.You’reformingnewneuronpathways. Those pathways are going to understand React one day. Help’emout.

importReactfrom'react';

importReactDOMfrom'react-dom';

functionHelloWorld(){

return(

<div>HelloWorld!</div>

);

}

ReactDOM.render(

Page 14: Pure React: A step-by-step guide to mastering React

<HelloWorld/>,

document.querySelector('#root'));

Theimportstatementsat thetopareanES6feature.Theselineswillbeat thetopofeveryindex.jsfilethatweseeinthisbook.

UnlikewithES5,we can’t simply include a<script> tag and getReact as aglobal object. So, the statement import React from 'react' creates a newvariablecalledReactwiththecontentsofthereactmodule.

Thestrings‘react’and‘react-dom’areimportant:theycorrespondtothenamesof modules installed by npm. If you’re familiar with Node.js, import Reactfrom'react'isequivalenttovarReact=require('react').

Step3

Frominsidethereact-hellodirectory,starttheappbyrunning:

$npmstart

Abrowserwillopenupautomaticallyanddisplay“HelloWorld!”

HowtheCodeWorks

Let’sstartatthebottom,withthecalltoReactDOM.render.That’swhatactuallymakes this work. This bit of code is regular JavaScript, despite the HTML-looking <HelloWorld/> thing there. Try commenting out that line and watchhowHelloWorlddisappears.

React uses the concept of a virtual DOM. It creates a representation of yourcomponenthierarchyandthenrendersthosecomponentsbycreatingrealDOMelements and inserting them where you tell it. In this case, that’s inside theelementwithanidofroot.

ReactDOM.rendertakes2arguments:whatyouwanttorender(yourcomponent,oranyotherReactElement)andwhereyouwanttorenderitinto(arealDOMelement).

ReactDOM.render([ReactElement],[DOMelement]);

Page 15: Pure React: A step-by-step guide to mastering React

Above that, we have a component named HelloWorld. The primary way ofwriting React components is as plain functions like this. They’re often called“statelessfunctioncomponents.”

There are 2 other ways to create components: ES6 classes, and the now-deprecatedReact.createClass.YoumaystillseethecreateClassstyleinoldStackOverflowanswersoroldGitHubprojects,sowe’llgooverhowitworks.Butprimarilywe’llbewritingfunctionswherepossible,andES6classeswhenweneedto.

TheHTML-likesyntaxinsidetherenderfunctioniscalledJSX,andwe’llcoverthatnext.

Page 16: Pure React: A step-by-step guide to mastering React

JSX:WhatandWhyOneofthefirstthingsyouprobablynoticedaboutReactcodeisthatitlookslikethecomponentfunctionisreturningHTML.ThisHTML-likesyntaxisactuallycalledJSX.

WhatIsJSX?

JSXisasyntaxinventedforReactthatlooksverysimilarto(X)HTML.Itallowsyoutocreateelementsbywritinginafamiliar-lookingsyntax,insteadofwritingout function calls by hand. TheHTML-like syntax actually compiles down torealJavaScript,whichwe’lltalkaboutbelow.

HTMLInsideJavaScript?!

BeforeIevenseriouslylookedintoReact,IknewitmixedHTMLandJS,andIthought that was just ugly. I mean, that’s undoing years of best-practicesthinking,right?SincetheDarkDaysofjQuerywe’veknownthatdirectlysettinginnerHTMLonelementsfromwithinJSwashackishandperformedbadly–nottomentionfullofsecurityholes–sowhyisReactmakingthosesamemistakesalloveragain?

Didyounoticehowtherearenoquotesaroundthe“HTML”?That’sbecauseit’snotastring.The lackofquotes isnot justa trick,either.React isnotparsingthatthingandconvertingitintoHTML.

I know, I know, it looks like HTML. In reality though, JSX is just a nicesyntacticsugaroverfunctioncallsthatcreateDOMelements.

Sowhat isReact actually doing here?Howdoes thiswork?Andwhy isn’t itterrible?

JSXIsCompiledtoJavaScript

TheJSXelementsyouwriteareactuallycompileddowntoJavaScriptbyatoolcalledBabel.Babel isa“transpiler,”amade-upword thatmeans it transforms

Page 17: Pure React: A step-by-step guide to mastering React

code into valid ES5 JavaScript that all browsers can understand. Each JSXelementbecomesafunctioncall,whereitsargumentsareitsattributes(“props”)anditscontents(“children”).

Here’sanexampleofsomesimpleJSX:

return<span>Hello!</span>;

AndhereistheJavaScriptgeneratedbythecompiler:

returnReact.createElement(

'span',

{},

'Hello!');

TheReact.createElementfunctionsignaturelookslikethis:

React.createElement(

string|element,

[propsObject],

[children...])

The string|element can be a string describing an HTML or SVG tag (like'div'or'span'),oritcanbeacomponent(likeHelloWorld,withnoquotes).

ThepropsObjectandchildrenareoptional,andyoucanalsosupplymorethanonechildbypassingadditionalarguments:

React.createElement(

'div',

{},

'Hello',

'World'

);

Youcanalsonestthecalls:

React.createElement('div',{},

React.createElement('div',{},'Child1'),

React.createElement('div',{},'Child2',

React.createElement('div',{},'Child2_child')

Page 18: Pure React: A step-by-step guide to mastering React

)

);

Try it yourself! Rewrite the HelloWorld component to callReact.createElementinsteadofreturningJSX.

functionHelloWorld(){

return(

React.createElement(/*...*/)

);

}

HereisaslightlymorecomplicatedbitofJSX:

<spanclassName='song-name'>

{props.song.name}

</span>

Andhereiswhatitcompilesto:

React.createElement('span',

{className:'song-name'},

props.song.name

);

See how JSX is essentially a nice shorthand for writing function calls? Youdon’t even have to use JSX if you don’t want to – you can write out thesefunctioncallsmanually.

YourfirstinstinctmightbetoavoidwritingJSXbecauseyoudon’tlikethelookof “HTML in JS.” Maybe you’d rather write real JavaScript function calls,becauseitfeelsmore“pure”somehow.IsuggestgivingJSXanhonesttrybeforeyougiveuponit.

Writing out the React.createElement calls is not a common approach in theReactcommunity.NearlyallReactdevelopersuseJSX,whichmeanscodethatyouseeinthewild(onGitHub,StackOverflow,etc)islikelytobewrittenwithit.

Page 19: Pure React: A step-by-step guide to mastering React

But…SeparationofConcerns!

Thisisthenumberoneconcernmostnewcomershave:thatpit-of-your-stomachfeelingthatmixingJSwithHTMLisjustwrong.Itmightevenmakeyouangry.

BeforeIgotintousingReact,Ihadthesameapprehensions.Ittooksometime(andwritinga fewsmall apps)before Ibegan tounderstand thepowerof thisunholymixing.Don’tworrythough,becausetheexercisesinthisbookwillgiveyouallthepracticeyouneedtocometogripswithit.

IbelievethedisdainformixingHTMLwithJShasabitofcargo-cult“tradition”behind it. It’s a rite of passage for every newweb developer, a piece of lorepassed down through the generations about the Right Way to build webinterfaces.“It’salwaysbeendonethisway.”

There are some good reasons to separate the view from the logic. Whencombined, the code can turn into a tangled mess of conditional logic withduplicatedHTMLeverywhere,likebadly-writtenPHP.

IfyouhaveflashbackstoPHPorJSPswheredatabasecallsweremixedinwithview code, and you never want to go back to that world, I don’t blame you.React’spatternofbuildingwithcomponentshelpspreventthis.

UnseparatedConcerns

Whenyoustepbackandthinkaboutit,therearesomegoodreasonstocombinethelogicandtheviewtogether.

Ifyou’veeverusedsomethinglikeAngular,you’veprobablywrittenthelogicinonefileandtheHTMLinaseparatetemplatefile.

Tellme,howoftenhaveyouopenedupthetemplatetotweaksomethingwithouthaving to look at (or change!) the associated JS code? How often have youchangedtheJSwithouthavingtotouchthetemplate?

InmostcodeI’veworkedwith,it’srarethatIcanaddnewfunctionalitywithoutchangingboththetemplateanditscontroller.Addafunctioninonefile,callitfromtheother.Passinganextraargument?Gottachangeitintwofiles.

Page 20: Pure React: A step-by-step guide to mastering React

Iftheyweretrulyseparatedconcerns,thiswouldnotbenecessary.

WeliketothinkthatsplittingtheJSandtheHTMLintoseparatefilesmagicallytransformstheminto“separatedconcerns.”Reusabilityherewecome!

Exceptitrarelyworksthatway.TheJScodeanditsrelatedtemplateareusuallyprettytightlycoupled,andnaturallyso–they’retwosidesofthesamecoin.

Splitting code into separate files does not automatically lead to separation ofconcerns.

ThestoryissimilarwithgoodoldjQuery.TheHTMLisblissfullyunawarethatJavaScriptevenexists,andyettheJSandHTMLaretightlycoupledbythefactthat the jQueryselectorsmustknowsomethingabout thepagestructure. If thestructurechanges,thecodemustchange.

If you haven’t noticed, I’m trying tomake the case that the template and theview logic could actually coexist in the same file and itmight actually makemoresensetodoitthatway.

Youdon’thavetobelievemerightnow.Justkeeptheideainthebackofyourmindasyouworkthroughtheexamplesandexercises.Youmayfind(asIdid)thatmerging the logic and viewmakes your code easier to navigate, easier towrite,andeasier todebug.You’ll spend less timehoppingbetweenfileswhenalltherelatedfunctionalityisinoneplace.

Page 21: Pure React: A step-by-step guide to mastering React

WorkingWithJSXComposingComponents

JSX, like HTML, allows you to nest elements inside of one another. This isprobablynotabigsurprise.

Let’s refactor the HelloWorld component from earlier to demonstrate howcompositionworks.Here’stheoriginalHelloWorld:

functionHelloWorld(){

return(

<div>HelloWorld!</div>

);

}

LeavingtheHelloWorldcomponentintactfornow,createtwonewcomponents:one named Hello and one named World. Hello should render<span>Hello</span> and World should render <span>World</span>. You canbasically copy-and-paste the HelloWorld component and just change the textandthefunctionname.

Goaheadandtrymakingthatchangeyourself.I’llwait.

Gotit?It’simportanttoactuallytypethisstuffoutandtryityourself!Don’tjustreadwhilenoddingalong,becauseyouwon’tactuallylearnitthatway.It’sveryeasytolookatcodeandthink,“Thatallmakessense,yep.”You’llneverknowifyouinternalizedituntilyoutryit.

Yourtwonewcomponentsshouldlooklikethis:

functionHello(){

return<span>Hello</span>;

}

Page 22: Pure React: A step-by-step guide to mastering React

functionWorld(){

return<span>World</span>;

}

Now, update the HelloWorld component to use the two new components youjustcreated.Itshouldlooksomethinglikethis:

functionHelloWorld(){

return(

<div>

<Hello/><World/>!

</div>

);

}

Assumingtheappisstillrunning,thepageshouldautomaticallyrefresh.Ifnot,refreshitmanually,andalsomakesuretheappisrunning(runnpmstartifit’snot).

Youshouldsee thesame“HelloWorld!”asbefore. Iknowthisseemssimple,buttherearesomelessonshere,Ipromise.

WrapJSXwithParentheses

The following examplesdependon theHello andWorld components that youshouldhavecreated,somakesurethoseexistbeforecontinuing.

First,anoteonformatting.YoumightnoticeIwrappedthereturnedJSXinsideparentheses,().Thisisn’tstrictlynecessary,butifyouleaveofftheparens,theopeningtagmustbeonthesamelineasthereturn,whichlooksabitawkward:

functionHelloWorld(){

return<div>

<Hello/><World/>!

</div>;

}

Justforkicks,tryremovingtheparensandwatchwhathappens:

functionHelloWorld(){

return

<div>

Page 23: Pure React: A step-by-step guide to mastering React

<Hello/><World/>!

</div>;

}

Thiswillfailwithanerrorinthebrowserconsole:

HelloWorld(…): A valid React element (or null) must be returned. Youmayhavereturnedundefined,anarrayorsomeotherinvalidobject.

You’llalsolikelyseeawarningabout“Unreachablecode.”

This is because JavaScript assumes you wanted a semicolon after that return(because of the newline), effectively turning it into this, which returnsundefined:

functionHelloWorld(){

return;

<div>

<Hello/><World/>!

</div>;

}

So:feelfreetoformatyourJSXhoweveryoulike,butifit’sonmultiplelines,Irecommendwrappingitinparentheses.

ReturnaSingleElement

Notice how the two components are wrapped in a <div> in the HelloWorldexample:

functionHelloWorld(){

return(

<div>

<Hello/><World/>!

</div>

);

}

Here’salittleexercise:tryremovingthe<div>wrapperandseewhathappens.Youshouldgetthiserror:

AdjacentJSXelementsmustbewrappedinanenclosingtag

Page 24: Pure React: A step-by-step guide to mastering React

Ifthisseemssurprising,rememberthatJSXiscompiledtoJSbeforeitruns://ThisJSX:return(<Hello/><World/>);//BecomesthisJS:return(

React.createElement(Hello, null) React.createElement(World, null)

);

Returning two things at once makes no sense. So that leads to this veryimportantrule:

Acomponentfunctionmustreturnasingleelement.

Butwait!Couldyoureturnanarray?It’sjustJavaScriptafterall…

//ThisJSX:

return[<Hello/>,<World/>];

//WouldturnintothisJS

//(noticethebrackets).

//Itlooksvalid...

return[

React.createElement(Hello,null),

React.createElement(World,null)

];

Tryitout!Itshouldfail.Inthedevconsole,you’llsee:

HelloWorld(…): A valid React element (or null) must be returned. Youmayhavereturnedundefined,anarrayorsomeotherinvalidobject.

Sofornow,therulestands:youmustreturnasingleelement.ThisisexpectedtochangewhenReact16isreleased.

JavaScriptinJSX

Youcaninsert realJavaScriptexpressionswithinJSXcode,andinfact,you’lldothisprettyoften.SurroundJavaScriptwithsinglebraceslikethis:

functionSubmitButton(){

varbuttonLabel="Submit";

return(

<button>{buttonLabel}</button>

);

Page 25: Pure React: A step-by-step guide to mastering React

}

Remember that this will be compiled to JavaScript, which means that the JSinsidethebracesmustbeanexpression.Anexpressionproducesavalue.Theseareexpressions:

1+2

buttonLabel

aFunctionCall()

aFunctionName

Eachof theseproduces (aka returns) a singlevalue. In contrast, statements donot produce values and can’t be used inside JSX.Here are some examples ofstatements:

vara=5

if(true){17;}

while(i<7){i++}

Noneofthesethingsproducesavalue.vara=5declaresavariablewiththevalue5,butitdoesnotreturnthatvalue.

Anotherwaytothinkofstatementvsexpressionisthatexpressionscanbeontherighthandofanassignment,butstatementscannot.

//Thesearen'tvalidJS:

a=varb=5;

a=if(true){17;}

I’m hammering home this point because it’s important, and it’s a commontrippingpointforReactbeginners.

“If”inJSX

Thenextquestionyoumightwonderis,“HowdoIwriteaconditionalifIcan’tuse‘if’?”Thereareacoupleofoptions.

Thefirstistheternaryoperator(?).Useitlikethis:

functionValidIndicator(){

varisValid=true;

return(

Page 26: Pure React: A step-by-step guide to mastering React

<span>{isValid?'valid':'notvalid'}</span>

);

}

Youcanalsousebooleanoperatorssuchas&&likethis:

functionValidIndicator(){

varisValid=true;

return(

<span>

{isValid&&'valid'}

{!isValid&&'notvalid'}

</span>

);

}

CommentsinJSX

IfyouneedtoputacommentintoablockofJSX,thesyntaxmaylookalittlestrange.RememberthatJavaScriptneedstobeinsidesinglebraces.CommentsinJSXmustgoinsideaJavaScriptblocklikethis:

functionValidIndicator(){

varisValid=true;

return(

<span>

{/*hereisacomment*/}

{isValid&&'valid'}

{!isValid&&'notvalid'}

{

//Double-slashcommentsare

//OKinmulti-lineblocks.

}

</span>

);

}

CapitalizeComponentNames

The components you write must begin with an uppercase letter. This meansusing names like UserList and Menu and SubmitButton, and not names likeuserList,menu,andsubmitButton.

Page 27: Pure React: A step-by-step guide to mastering React

InJSX,acomponentthatstartswithalowercaseletterisassumedtobeabuilt-inHTMLorSVGelement(div,ul,rect,etc.).

EarlyversionsofReactkepta“whitelist”ofallthebuilt-inelementnamessoitcould tell them apart from custom ones.Maintaining that whitelist was time-consuminganderror-prone–ifanewSVGelementmadeitswayintothespec,youcouldn’tuseituntilReactupdatedthatlist.Sotheykilledthelist,andaddedthisrule.

CloseEveryElement

JSX requires that every element be closed, similar toXML orXHTML. Thisincludes theonesyoumight beused to leavingopen inHTML5, like<br> or<input>ormaybeeven<li>.

//DOTHIS:

return<br/>;

return<inputtype='password'.../>;

return<li>text</li>;

//NOTTHIS:

return<br>;

return<inputtype='password'...>;

return<li>text;

Exercises

Create a new app for these exercises by running: $ create-react-app jsx-exercises

Open thesrc/index.js file and replace the contents, similar toHelloWorld.Fillintherest.

importReactfrom'react';

importReactDOMfrom'react-dom';

functionMyThing(){

//...

}

ReactDOM.render(

Page 28: Pure React: A step-by-step guide to mastering React

<MyThing/>,

document.getElementById('root')

);

Youcandeleteorignorethesrc/App*filesandthelogo.Ifyoudonotexplicitlyimportthem,theydonotgetbundledintoyourapp.

1. CreateacomponentthatrendersthisJSX:

<divclassName='book'>

<divclassName='title'>

TheTitle

</div>

<divclassName='author'>

TheAuthor

</div>

<ulclassName='stats'>

<liclassName='rating'>

5stars

</li>

<liclassName='isbn'>

12-345678-910

</li>

</ul>

</div>

2. See how JSX interprets whitespace. Try rendering these arrangements(replace.withaspacecharacter)andtakenoteoftheoutput(hint:leadingandtrailingspacesareremoved,andsoarenewlines):

a. Singlelines

<div>

Newline

Test

</div>

c. Emptynewlines

<div>

Page 29: Pure React: A step-by-step guide to mastering React

Empty

Newlines

Here

</div>

d. Spaceswithnewlines

<div>

&nbsp;Non-breaking

&nbsp;Spaces&nbsp;

</div>

f. Insertingspaceswhencontentspansmultiplelines

<div>

Line1

{''}

Line2

</div>

3. MakeacopyofthecomponentfromExercise1,andreplacetheJSXwithcallstoReact.createElement.Theoutputshouldbeidentical.

4. ReturntheappropriateJSXfromthiscomponentsothatwhenusernameisundefinedornull, it renders“Not logged in”.Whenusername is a string,render“Hello,username”.

functionGreeting(){

//Tryallofthesevariations:

//varusername="root";

//varusername=undefined;

//varusername=null;

//varusername=false;

//Fillintherest:

//return(...)

});

Page 30: Pure React: A step-by-step guide to mastering React

5. One good way to learn a new syntax is to try breaking it – discover itsboundaries.Trysomeofthethingsthischapterwarnedaboutandseewhatkindoferrorsyouget.Attheveryleast,it’llfamiliarizeyouwithwhattheerrorsmeanifyoumakeoneofthesemistakeslateron.a. Name a component starting with a lowercase letter, like

“testComponent”.b. Tryreturning2elementsatoncec. Tryreturninganarraywith2elementsinsided. Canyouput2expressions insidesinglebraces, like{x&&5;x&&

7}?e. WhathappensifyouusereturninsideaJSexpression?f. Whataboutafunctioncalllikealert('hi')?Doesithaltrendering?g. TryputtingaquotedstringinsideJSX.Doesitstripoutthequotes?

Page 31: Pure React: A step-by-step guide to mastering React

Example:TweetComponent1.Sketch

Thisisyourintroductionto“thinkingincomponents.”Let’sstartwithsomethingconcrete: a humble sketch. This could come from a designer (wireframes ormockups)oritcouldbeyourowncreation.

Evenwhenyoucanalreadyvisualize theendresult, spend the30secondsandsketchitoutonrealdead-treepaper.Itwillhelptremendously,especiallyforthecomplicatedcomponents.

There’ssomethingsatisfyingaboutbuildingacomponentfromasketch:youcantell when you’re “done.” Without a sketch, you’ll compare the on-screencomponenttothegrandvisioninyourhead,anditwillneverbegoodenough.It’seasytowastealotoftimewhenyoudon’tknowwhat“done”lookslike.Asketchgivesyouatargettoaimfor.

Hereisthesketchwe’llbebuildingfrom:

It’s roughonpurpose: youdon’t need a beautifulmockup.A simplepen-and-papersketchworksfine.

2.CarveintoComponents

Thenext step is tobreak this sketch intocomponents.Drawboxesaround the

Page 32: Pure React: A step-by-step guide to mastering React

“parts,”andthinkaboutreusability.

Imagineyouhad3tweets,eachwithadifferentmessageanduser.Whatwouldchange,andwhatwouldstaythesame?Thepartsthatchangewouldmakegoodcomponents.

Anotherstrategy:makeevery“thing”acomponent.Buttons,bitsoftext,images,andsoon.

Tryityourself,thencomparewiththis:

3.NametheComponents

Nowthatwe’vebrokenthesketchintopieces,wecannamethem.

Page 33: Pure React: A step-by-step guide to mastering React

Eachof thesewillbecomeacomponent,withTweet being the“parent”of theothers:

TweetAvatarNameWithHandleTimeMessageReplyButtonLikeButtonRetweetButtonMoreOptionsButton

4.Build

Weknowthecomponenthierarchynow–let’sbuildit.

Top-Down,orBottom-Up?

Therearetwowaystoapproachthis.

1. Start at the top.Build theTweet component first, then build its children.BuildAvatar,thenNameWithHandle,andsoon.

2. Start at the bottom (the “leaves” of the tree). Build Avatar, thenNameWithHandle, then the restof thechildcomponents.Verify that theywork in isolation. Once they’re all done, assemble them into the Tweetcomponent.

Sowhat’sthebestway?Well,itdepends(doesn’titalways?).

Forasimplehierarchylikethisone,itdoesn’tmattermuch.It’seasiesttostartatthetop,sothat’swhatwe’lldohere.

Foramorecomplexhierarchy,startat thebottom.Buildsmallpieces, test thattheywork,andcombinethemasyougo.Thiswayyoucanbeconfidentthatthesmallpieceswork,and,byinduction,thecombinationofthemshouldalsowork(intheory,anyway).

You’ll likely combine the top-down and bottom-up approaches as you build

Page 34: Pure React: A step-by-step guide to mastering React

largerapps.

Forexample,ifwewerebuildingTwitter,wemightbuildtheTweetcomponenttop-down,thenincorporateitintoalistoftweets,thenembedthatlistinapage,thenembedthatpageintothelargerapplication.TheTweetcouldbebuilttop-downwhilethelargerapplicationisbuildbottom-up.

There’sanothercasewherebuildingfromthebottomisagoodidea,andthat’sifyou have an existing app written in another framework like Angular orBackbone and youwant to rewrite in itReact. Starting at the topmakes littlesensehere,becauseit’llhavearippleeffectacrossyourentirecodebase.

Starting at the bottom is manageable and controlled. You can build the “leafnodes”ofyourapp–thesmall,containedpieces.Getthoseworking,thenbuildthenextlevelup,andsoon,untilyoureachthetop.AtthatpointyouhavetheoptiontoreplaceyourcurrentframeworkwithReactifyouchooseto.

Theotheradvantageofbottom-updevelopmentinarewriteisthatitfitsnicelywithReact’sone-waydataflowparadigm.SincetheReactcomponentsoccupythe bottom of the tree, and you’re guaranteed that React components onlycontain other React components, it’s easier to reason about how to pass yourdatatothecomponentsthatneedit.

BuildtheTweetComponent

Here’sourblueprintagain:

We’llbebuildingaplainstatictweetinthissection,startingwiththetop-level

Page 35: Pure React: A step-by-step guide to mastering React

component,Tweet.

CreateanewprojectwithCreateReactAppbyrunningthiscommand:

$create-react-appstatic-tweet&&cdstatic-tweet

Itgaveusafewfileswewon’tusehere,sowecansafelydeletethem:

$rmsrc/App.*src/logo.svg

The newly-generated project comes with an index.html file in the publicdirectory.AddFontAwesomebyputtingthis lineinsidethe<head> tag(put itallononeline):

<linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/

font-awesome/4.7.0/css/font-awesome.min.css">

Wealsohave ageneratedindex.css file in thesrc directory.Open it up andreplaceitscontentswiththis:

.tweet{

border:1pxsolid#ccc;

width:564px;

min-height:68px;

padding:10px;

display:flex;

font-family:"Helvetica",arial,sans-serif;

font-size:14px;

line-height:18px;

}

The index.js file will be very similar to the one from Hello World. It’sbasicallythesamething,with“Tweet”insteadof“HelloWorld”.We’llmakeitbettersoon,Ipromise.Replacethecontentsofsrc/index.jswiththis:

importReactfrom'react';

importReactDOMfrom'react-dom';

import'./index.css';

functionTweet(){

Page 36: Pure React: A step-by-step guide to mastering React

return(

<divclassName="tweet">

Tweet

</div>

);

}

ReactDOM.render(<Tweet/>,

document.querySelector('#root'));

Thatshoulddoit.Startuptheserver,sameasbefore,byrunning:

$npmstart

Andthepageshouldrendersomethinglikethis:

This is nothing you haven’t seen before. It’s a simple component, with theadditionofaspecialclassNameattribute(or“prop”).

Oneothernewthingyoumight’venoticedistheimport'./index.css'whichisimporting…aCSSfileintoaJavaScriptfile?Seemsweirdatfirstglance.

What’s happening is that behind the scenes,whenWebpackbuilds our app, itseesthisCSSimportandlearnsthatindex.jsdependsonindex.css.Webpackreads theCSS fileand includes it in thebundledJavaScript (asa string) tobesenttothebrowser.

You can actually see this in the browser – open the dev console, look at theElementstab,andnoticeunder<head>there’sa<style>tagthatwedidn’tputthere.Itcontainsthecontentsofindex.css.

We’lllearnmoreaboutpropsinthenextsection,butfornow,justthinkofthemlikeHTMLattributes.Mostofthemarenamedidenticallytotheattributesyoualready know, but className is special in that its value becomes the class

Page 37: Pure React: A step-by-step guide to mastering React

attributeontheDOMnode.

Back to our outline. Let’s build the Avatar component. Add the componentfunctiontoindex.js:

functionAvatar(){

return(

<img

src="https://www.gravatar.com/avatar/nothing"

className="avatar"

alt="avatar"/>

);

}

Lateronwe’ll lookatextractingcomponents intofilesandimporting themviaimport,buttokeepthingssimplefornow,we’lljustputallthecomponentsinindex.js.

IfyouwanttouseyourownGravatar,gotodaveceddia.com/gravatartofigureoutitsURL.

NextweneedtochangeTweettorenderAvatar:

functionTweet(){

return(

<divclassName="tweet">

<Avatar/>

Tweet

</div>

);

}

NowjustgiveAvatarsomestyle,inindex.css:

.avatar{

width:48px;

height:48px;

border-radius:5px;

margin-right:10px;

}

Page 38: Pure React: A step-by-step guide to mastering React

It’sgettingbetter:

Nextwe’llcreatetwomorecomponents,theMessageandNameWithHandle:

functionMessage(){

return(

<divclassName="message">

Thisislessthan140characters.

</div>

);

}

functionNameWithHandle(){

return(

<spanclassName="name-with-handle">

<spanclassName="name">YourName</span>

<spanclassName="handle">@yourhandle</span>

</span>

);

}

Ifyourefreshnow,nothingwillhavechangedbecausewestillneed toupdateTweettousethesenewcomponents:

functionTweet(){

return(

<divclassName="tweet">

<Avatar/>

<divclassName="content">

<NameWithHandle/>

<Message/>

</div>

</div>

);

Page 39: Pure React: A step-by-step guide to mastering React

}

It’srenderingnow,butit’sugly.SpruceitupwithsomeCSSforthenameandhandle:

.name{

font-weight:bold;

margin-bottom:0.5em;

margin-right:0.3em;

}

.handle{

color:#8899a6;

font-size:13px;

}

It’slookingmorelikearealtweetnow!Nextup,addtheTimeandthebuttons(we’lltalkaboutthenewsyntaxinasecond):

constTime=()=>(

<spanclassName="time">3hago</span>

);

constReplyButton=()=>(

<iclassName="fafa-replyreply-button"/>

);

constRetweetButton=()=>(

<iclassName="fafa-retweetretweet-button"/>

);

constLikeButton=()=>(

<iclassName="fafa-heartlike-button"/>

);

Page 40: Pure React: A step-by-step guide to mastering React

constMoreOptionsButton=()=>(

<iclassName="fafa-ellipsis-hmore-options-button"/>

);

Thesecomponentsdon’tlooklikethefunctionsyou’vebeenwritinguptothispoint, but they are in fact still functions. They’re arrow functions. Here’s aprogressionfromaregularfunctiontoanarrowfunctionsoyoucanseewhat’shappening:

functionLikeButton(){

return(

<iclassName="fafa-heartlike-button"/>

);

}

//Thiscanberewrittenasan

//anonymousfunction:

constLikeButton=function(){

return(

<iclassName="fafa-heartlike-button"/>

);

}

//...whichcanbeturnedintoan

//arrowfunction:

constLikeButton=()=>{

return(

<iclassName="fafa-heartlike-button"/>

);

}

//Itcanbefurthersimplifiedto:

constLikeButton=()=>(

<iclassName="fafa-heartlike-button"/>

);

//Andifit'sreallysimple,

//youcanevenwriteitononeline:

constHi=()=><span>Hi</span>;

Page 41: Pure React: A step-by-step guide to mastering React

Arrow functions are a nice concise way of writing components. The functionitselfisthe()=>{...}part.

Noticehowthere’snoreturninthelastversions:whenanarrowfunctiononlycontainsoneexpression,itcanbewrittenwithoutbraces.Andwhenit’swrittenwithoutbraces,thesingleexpressionisimplicitlyreturned.

You’ll continue to use arrow functions as you continue through the book, sodon’tworryiftheylookforeignnow.You’llgetusedtothem.

TheletandconstKeywords

If you’re familiar with languages like C or Java, you’re familiar with blockscoping. As you may know, JavaScript’s var is actually function-scoped, notblock-scoped.Thishaslongbeenanannoyance.

ES6addsletandconstastwonewwaysofdeclaringblock-scopedvariables.

Theletkeyworddefinesamutable(changeable)variable.Youcanuseitinsteadofvaralmosteverywhere.

Theconst defines a constant. Itwill throw an error if you try to reassign thevariable,but it’sworthnoting that itdoesnotpreventyoufrommodifying thedatawithinthatvariable.Here’sanexample:

constanswer=42;

answer=43;//error!

constnumbers=[1,2,3];

numbers[0]='thisisfine';//noerror

Usingconst ismoreofasignalofintentthanabulletproofprotectionscheme,butitisstillworthwhile.

Now that we’ve got all those new components, update Tweet again toincorporatethem:

functionTweet(){

Page 42: Pure React: A step-by-step guide to mastering React

return(

<divclassName="tweet">

<Avatar/>

<divclassName="content">

<NameWithHandle/><Time/>

<Message/>

<divclassName="buttons">

<ReplyButton/>

<RetweetButton/>

<LikeButton/>

<MoreOptionsButton/>

</div>

</div>

</div>

);

}

Finally,addafewmorestylestocoverthetimeandbuttons:

.time{

padding-left:0.3em;

color:#8899a6;

}

.time::before{

content:"\00b7";

padding-right:0.3em;

}

.buttons{

margin-top:10px;

margin-left:2px;

font-size:1.4em;

color:#aab8c2;

}

.buttonsi{

width:80px;

}

Andherewehaveafairlyrespectable-lookingtweet!

Page 43: Pure React: A step-by-step guide to mastering React

Youcancustomizeit toyourheart’scontent:changethename,thehandle, themessage,eventheGravataricon.Prettysweet,right?

“But…wait,” I hear you saying. “I thought wewere going to build reusablecomponents!”This tweet is pretty and all, but it’s only good for showingonemessagefromoneperson…

Well don’tworry, because that’s coming up next: parameterizing componentswithprops.

Page 44: Pure React: A step-by-step guide to mastering React

PropsArgumentstoComponents

WhereHTMLelementshave“attributes,”Reactcomponentshave“props”(shortfor“properties”).

We’vealready seen thatReact components canbewrittenas functions, so it’snaturaltoassumethatwecouldpassargumentstothosefunctions.Propsaretheargumentstoyourcomponents.

PassingProps

ThisbitofJSXispassingapropcallednamewithastringvalueof"Dave":

<Personname='Dave'/>

Here’sanotherexample,passingaclassNamepropwiththevalue"person":

<divclassName='person'/>

JSXusesclassNameinsteadofclasstospecifyCSSclasses.

Noticehowthedivisself-closing?Thisabilityisn’tjustfor<input/>anymore:in JSX,every component canbe self-closing. In fact, if thecomponenthasnochildren(nocontents), theconventionis towrite it like this, insteadofusingaclosingtag.

Here,we’repassingastringforclassName,anumberfor theageprop,andanactualJavaScriptexpressionforthename:

functionDave(){

constfirstName="Dave";

constlastName="Ceddia";

Page 45: Pure React: A step-by-step guide to mastering React

return(

<Person

className='person'

age={33}

name={firstName+''+lastName}/>

);

}

Remember that in JSX, singles braces must surround JavaScript expressions.The code in the braces is real JavaScript, and it follows all the same scopingrulesasnormalJavaScript.

It’simportanttounderstandthattheJSinsidethebracesmustbeanexpression,notastatement.HereareafewthingsyoucandoinsideJSXexpressions:

Math,concatenation:{7+5}or{'Your'+'Name'}Functioncalls:{this.getFullName(person)}Ternary(?)operator:{name==='Dave'?'me':'notme'}Booleanexpressions:{isEnabled&&'enabled'}

Herearesomethingsyoucannotdo:

DefinenewvariableswithvarUseif,for,while,etc.Definefunctionswithfunction

Remember that JSX evaluates to JavaScript, and the props become keys andvalues in an object. Here’s that example from above, transformed intoJavaScript:

functionDave(){

constfirstName="Dave";

constlastName="Ceddia";

returnReact.createElement(Person,{

age:33,

name:firstName+''+lastName,

className:'person'

},null);

}

Page 46: Pure React: A step-by-step guide to mastering React

All of the rules that apply to function arguments apply to JSX expressions.Couldyoucallafunctionlikethis?

myFunc(varx=true;x&&'istrue');

Ofcoursenot!Thatlookscompletelywrong.IfyoutriedtopassthatargumenttoaJSXexpression,thisiswhatyou’dget:

<Brokenvalue={varx=true;x&&'istrue'}/>

//getscompiledto:

React.createElement(Broken,{

value:varx=true;x&&'istrue'

},null);

Sowhenyou’retryingtodecidewhattoputinaJSXexpression,askyourself,“CouldIpassthisasafunctionargument?”

ReceivingProps

Propsarepassedasthefirstargumenttoacomponentfunction,likethis:

functionHello(props){

return(

<span>Hello,{props.name}</span>

);

}

//Usedlike:

<Helloname="Dave"/>

Itworksthesamewayforarrowfunctions:

constHello=(props)=>(

<span>Hello,{props.name}</span>

);

ES6 has a new syntax calleddestructuringwhichmakes props easier toworkwith.Itlookslikethis:

Page 47: Pure React: A step-by-step guide to mastering React

constHello=({name})=>(

<span>Hello,{name}</span>

);

Youcanread{name}as“extractthe‘name’keyfromtheobjectpassedasthefirstargument”.Itcanextractmultiplekeys,too:

constHello=({firstName,lastName})=>(

<span>Hello,{firstName}{lastName}</span>

);

Inpractice,propsareveryoftenwrittenusingthisdestructuringsyntax.Justsoyouknow,itworksoutsideoffunctionargumentsaswell.Youcandestructurepropsthisway,forinstance:

constHello=(props)=>{

const{name}=props;

return(

<span>Hello,{name}</span>

);

}

Remember,arrowfunctionsneedareturnifthebodyissurroundedbybraces,anditneedsbracesifthebodycontainsmultiplelines.

ModifyingProps

One important thing to know is that props are read-only. Components thatreceivepropsmustnotchangethem.

If you come from an Angular background this is a change. Angular’s 2-waybinding mechanism allowed modifying scope variables (Angular’s version ofprops) and would automatically propagate those changes to the parentcomponent.

InReact,dataflowsoneway.Propsareread-only,andcanonlybepasseddowntochildren.

Page 48: Pure React: A step-by-step guide to mastering React

CommunicatingWithParentComponents

If you can’t change props, but you need to communicate something up to aparentcomponent,howdoesthatwork?

Ifachildneeds tosenddata to itsparent, theparentcan injecta function asaprop,likethis:

functionhandleAction(event){

console.log('Childdid:',event);

}

functionParent(){

return(

<ChildonAction={handleAction}/>

);

}

The Child component receives a prop named onAction, which it can callwheneveritneedstosendupdataornotifytheparentofanevent.You’llnoticethatthebuilt-inbuttonelementacceptsanonClickprop,whichit’llcallwhenthebuttonisclicked.We’lllookmoredeeplyateventhandlinglateron.

functionChild({onAction}){

return(

<buttononClick={onAction}/>

);

}

Page 49: Pure React: A step-by-step guide to mastering React

Example:TweetWithPropsNowthatyouhaveabasicunderstandingofprops, let’sseehowtheywork inpractice.

We’ll take the static Tweet example fromChapter 5 and rework it to displaydynamicdatabyusingprops.

Forthisexample,makeacopyofthestatic-tweetprojectfoldersothatwecanworkwithout fearofbreaking theoldcode.We’llalsostartupadevelopmentserver(stoptheoldoneifit’sstillrunning).

$cp-astatic-tweetprops-tweet&&cdprops-tweet

$npmstart

Note:Don’tusecp-r since itdoesnotpreserve symlinks, andwillbreaknpmstart.

If, after copying the project, the npm start command fails, delete thenode_modulesfolderandrunnpminstall.Thentrynpmstartagain.

Openupsrc/index.js.Tobeginwith,updatetheTweetcomponenttoacceptatweetpropasshownbelow.ThenaddthetestTweetobject,whichwillserveasour fake data, and update the call to ReactDOM.render to pass the testTweetobjectintothetweetprop.

Refreshthepageaftermakingthesechangesandmakesureeverythinglooksthesameasbefore(nothingshouldbedifferentyet).

//addthe{tweet}destructuring

functionTweet({tweet}){

return(

<divclassName="tweet">

<Avatar/>

<divclassName="content">

<NameWithHandle/><Time/>

<Message/>

<divclassName="buttons">

<ReplyButton/>

Page 50: Pure React: A step-by-step guide to mastering React

<RetweetButton/>

<LikeButton/>

<MoreOptionsButton/>

</div>

</div>

</div>

);

}

//...

vartestTweet={

message:"Somethingaboutcats.",

gravatar:"xyz",

author:{

handle:"catperson",

name:"IAMACatPerson"

},

likes:2,

retweets:0,

timestamp:"2016-07-3021:24:37"

};

ReactDOM.render(<Tweettweet={testTweet}/>,

document.querySelector('#root'));

Avatar

Let’s start converting the static components to accept props, starting withAvatar.IntherendermethodofTweet,replacethisline:

<Avatar/>

Withthisline:

<Avatarhash={tweet.gravatar}/>

This passes the tweet’s gravatar property into the hash prop. Now updateAvatartousethisnewprop:

Page 51: Pure React: A step-by-step guide to mastering React

functionAvatar({hash}){

varurl=`https://www.gravatar.com/avatar/${hash}`;

return(

<img

src={url}

className="avatar"

alt="avatar"/>

);

}

TheGravatar hash, passed in ashash usingES6destructuring, is incorporatedintotheURLandpassedtotheimagetagasbefore.

Here’salittlemoreES6foryou:thebackticksaroundtheURLstringareanewsyntax for template strings. The ${hash} part will be replaced with the hashitself. This new syntax is a bit cleaner than doing string concatenation, as in"https://www.gravatar.com/avatar/"+hash.

Theavatarshouldrenderthesameasbefore.

You can replace the Gravatar hash with your own if you like. Visithttps://daveceddia.com/gravatar, type inyouremail,andcopythehash. (Ifyoudon’thaveaGravataraccount,youcancreateoneathttps://gravatar.com).

Message

Nowwe’lldoMessage.ReplacethislineintherendermethodofTweet:

<Message/>

Withthisline:

<Messagetext={tweet.message}/>

We’reextractingthemessagefromthetweetandpassingitalongtotheMessagecomponentastext.ThenupdatetheMessagecomponenttousethenewprop:

Page 52: Pure React: A step-by-step guide to mastering React

functionMessage({text}){

return(

<divclassName="message">

{text}

</div>

);

}

Insteadofstatictextinthediv,we’rerenderingthetextpropthatwaspassedin.Refresh now, and you’ll see the message is now “Something about cats.”Success!

NameWithHandleandTime

This time we’ll convert two components at once: NameWithHandle and Time.UpdateTweettopasstherelevantdatabyreplacingthislineinrender:

<NameWithHandle/><Time/>

Withtheselines:

<NameWithHandleauthor={tweet.author}/>

<Timetime={tweet.timestamp}/>

We’realsogoingtointroducealibrarycalledMoment.jstoworkwithdatesandtimes.Wewilluseittocalculatetherelativetimestring(“3daysago”).RunthiscommandtoinstallMoment:

npminstallmoment--save

ThenweneedtoimportMomentatthetopofourindex.jsfile,soaddthislineatthetop:

importmomentfrom'moment';

NowupdatetheNameWithHandleandTimecomponents.You’llnoticethatTimeusesthenewmomentlibrary.

Page 53: Pure React: A step-by-step guide to mastering React

functionNameWithHandle({author}){

const{name,handle}=author;

return(

<spanclassName="namewith-handle">

<spanclassName="name">{name}</span>

<spanclassName="handle">@{handle}</span>

</span>

);

}

constTime=({time})=>{

consttimeString=moment(time).fromNow();

return(

<spanclassName="time">

{timeString}

</span>

);

};

Inthestatictweetexample,Timewaswrittenasanarrowfunction,soI’veleftitthatway.However, because it nowhas 2 statements, it needs the surroundingbraces,whichthencausesittoneedareturn.Youcouldofcoursesimplifythisbymoving the moment(time).fromNow() directly inside the <span>, and thenyoucouldremovethereturnandthebraces.I’llleaveituptoyou.

WhatExactlyShouldBePassedasaProp?

Propscanacceptallsortsofthings:Numbers,Booleans,Strings,Objects,evenFunctions.Earlyonyoumightstarttowonder,shouldyoupassanobjectandletthecomponentextractwhatitneeds?Orshouldyoupassthespecificpiecesofdatathatthecomponentrequires?

Intheexampleshere,we’repassinginthespecificpiecesofdata.

We could instead just pass the tweet object itself. For instance, instead ofpassingatimestampdirectlytotheTimecomponent,wecouldpassintweetandletitextractthetimestampfromthetweet.Whynotdoitthatway?Hereareafewreasons:

IfTimeexpectsatweetobjectandpullsoutthetimestampproperty,itwon’t

Page 54: Pure React: A step-by-step guide to mastering React

beusablewithanythingotherthanatweetobject.Whatifyouhaveauserwithantimestamppropertycalled“updated_at”andwanttorenderitwithaTime component?Well, you can’t, without hacking together a temporaryobjectthat“lookslike”atweetwithatimestamp.

The Time component would have knowledge of the inner structure of atweet object. This might not seem like a big deal, until you have 10componentslikethis,andthebackenddeveloperdecidesthat“timeStamp”with a capital “S” looks better than “timestamp” and now you have toupdateallthosecomponents.It’sagoodideatokeeptheknowledgeofdatastructures contained to as few places as possible to reduce the cost ofchange.

GuidelinesforNamingProps

As the saying goes, “There are two hard things in computer science: cacheinvalidation,namingthings,andoff-by-oneerrors.”

Thenamesyouchooseforpropsinfluencehowthecomponentwillbeused,aswellashowcomponentsarecoupled.ImagineifwehadwrittenTime totakeapropnamedtweetTime.Evenifwepassedinthetimestampdirectly(insteadoftheentiretweetobject),thisisstillnotagreatchoiceforapropname.Why?

Considerwhathappenswhenthenextdevelopercomesalong(maybethat’syou,3weeksfromnow)andwantstoreusetheTimecomponentinanewpartoftheappthathasnothingtodowithtweets.Therenderedresultwillcomeoutfine,but the code will be awkward. Writing <Time tweetTime={timestamp}/>

expressesthewrongintentifthetimestampdoesn’tactuallybelongtoatweet.

Morelikely,thedeveloperwilllookatTime,seethatitexpectsatweetTime,andthendecide not to reuse that component – either because they assume it’s notsuitablefortheirpurpose,ortheyworrythatitsunderlyingimplementationcouldchangeandbreaktheircode.

TheRemainingComponents

Let’sgetbacktoconvertingTweetanditschildrentouseprops.Thereareonlytwo components left: RetweetButton and LikeButton, which need to display

Page 55: Pure React: A step-by-step guide to mastering React

counts of retweets and likes.Currently, theydon’t display anynumbers at all,justanicon.

Changethesetwolines:

<RetweetButton/>

<LikeButton/>

Totheselines(passinginthecounts):

<RetweetButtoncount={tweet.retweets}/>

<LikeButtoncount={tweet.likes}/>

Then,updatetheRetweetButtonandLikeButtontoacceptthenewcountpropandrendertheirrespectivenumbers:

functiongetRetweetCount(count){

if(count>0){

return(

<spanclassName="retweet-count">

{count}

</span>

);

}else{

returnnull;

}

}

constRetweetButton=({count})=>(

<spanclassName="retweetbutton">

<iclassName="fafa-retweet"/>

{getRetweetCount(count)}

</span>

);

constLikeButton=({count})=>(

<spanclassName="likebutton">

<iclassName="fafa-heart"/>

{count>0&&

<spanclassName="like-count">

{count}

</span>}

</span>

);

Page 56: Pure React: A step-by-step guide to mastering React

Finally,thestylingneedsanupdatetoo.Taketheselinesout:

.buttonsi{

width:80px;

}

Thenreplacethemwiththis:

.reply-button,.retweetbutton,

.likebutton,.more-options-button{

width:80px;

display:inline-block;

}

.like-count,.retweet-count{

position:relative;

bottom:2px;

font-size:13px;

margin-left:6px;

font-weight:bold;

}

Therewego!

Let’s go over the code. Despite looking different, these components have thesamelogicfordetermininghowtoshowthecount.

RetweetButton

InRetweetButton, thecomputation is extracted intoa separate functioncalledgetRetweetCount.Iteitherreturnsa<span>elementornull.

Page 57: Pure React: A step-by-step guide to mastering React

Noticethatwehadtointroduceawrapperspanelementaroundtheiconandthecount.Remember thatReact components canonly return a single element (tryremovingthespanandseewhathappens).

Also note that when an expression evaluates to null or false inside singlebraceswithinJSX,nothingisrenderedatall.Toproveit,openuptheDevToolsElementInspectorinChromeandinspecttheRetweetbuttonwhenithas0likes:thereshouldbeno“like-count”spaninsight.

It’s worth noting that the getRetweetCount function could be written as acomponentinstead–italmostisonealready.Here’swhatthatmightlooklike:

functionCount({count}){

if(count>0){

return(

<spanclassName="retweet-count">

{count}

</span>

);

}else{

returnnull;

}

}

constRetweetButton=({count})=>(

<spanclassName="retweetbutton">

<iclassName="fafa-retweet"/>

<Countcount={count}/>

</span>

);

The function had to be renamed to start with a capital letter (remember thatcomponentnamesmuststartwithacapitalletter),andsinceitsargumentneededtobereplacedwithadestructuringtoextractcount.Otherwiseitlooksthesame.Then,usingitinRetweeetButtonisjustamatterofinsertingsomeJSX.

LikeButton

ForLikeButton, the logic isdoneslightlydifferently. Insteadofextracting thelogictoafunction,it’sdoneinlinewithabooleanoperator(the&&).Butagain,itrendersaspan,ornull.

Page 58: Pure React: A step-by-step guide to mastering React

AnotherOption

Here’sathirdalternative,justforfun,whichdiffersslightlyinthatitwillalwaysoutputa<span>withclass“like-count”,butthecontentscouldbeempty:

constLikeButton=({count})=>(

<spanclassName="likebutton">

<iclassName="fafa-heart"/>

<spanclassName="like-count">

{count?count:null}

</span>

</span>

);

Asyoucansee, there’smore thanoneway toaccomplish thesame thingwithJSX.

Page 59: Pure React: A step-by-step guide to mastering React

PropTypesDocumentationandDebuggingInOne

We’veseenwhat“props”are,andhowthey’repassedintoReactcomponents–butwhathappensifyouforgettopassoneoftheprops?

Well,itendsupbeingundefined,justasifyou’dforgottentopassanargumenttoaplainoldfunction.Thiscanbetotallyfine,oracode-breakingdisaster(justasifyou’dforgottentopassanargumenttoaplainoldfunction).

Reacthasasecretweaponthough:PropTypes.

Whenyoucreateacomponent,youcandeclarethatcertainpropsareoptionalorrequired,and youcandeclarewhat typeofvalue thatpropexpects.Here’s anexample:

importPropTypesfrom'proptypes';

functionComment({author,message,likes}){

return(

<div>

<divclassName='author'>{author}</div>

<divclassName='message'>{message}</div>

<divclassName='likes'>

{likes>0?likes:'No'}likes

</div>

</div>

);

}

Comment.propTypes={

message:PropTypes.string.isRequired,

author:PropTypes.string.isRequired,

likes:PropTypes.number

}

First, notice that PropTypes must be explicitly imported from the ‘proptypes’package.ThisisachangethatcamewithReact15.5.Youshouldalreadyhave

Page 60: Pure React: A step-by-step guide to mastering React

thispackageinstalledifyou’reusingCreateReactApp,butifnot,aquicknpminstall--saveproptypeswillfixthat.

Next,noticethatpropTypesaresetasapropertyonthefunctionitself.Thisstyleworkswhether the component is a function, or an arrow function, or even anES6class(whichwe’llseelater).

Finally,noticethatthepropTypesattributestartswithalowercase“p”whiletheimportedPropTypesstartswithacapital“P”.

With this set of propTypes, message and author are required, and must bestrings.Thelikespropisoptional,butmustbeanumber if it’sprovided.Tryrenderingitafewdifferentways,andchecktheconsoleeachtime:

<Commentauthor='somebody'message='alikablemessage'likes={1}/>

<Commentauthor='mr_unpopular'message='unlikablemessage'/>

<Commentauthor='mr_unpopular'message='anothermessage'likes={0}/>

<Commentauthor='error_missing_message'/>

<Commentmessage='mysteryauthor'/>

Reactwillwarnyouintheconsoleifyouforgetarequiredprop:

<Commentauthor='an_error'/>

Warning: Failed propType: Required prop message was not specified inComment.

Likewise,you’llgetawarningifyoupassthewrongtype:

<Commentauthor={42}/>

Warning:FailedpropType:InvalidpropauthoroftypenumbersuppliedtoComment,expectedstring.

Thesewarningmessagesare invaluable fordebugging. It tellsyou themistakeyoumade,andgivesyouahintwheretolook!Thisisafairbitbetterthansome

Page 61: Pure React: A step-by-step guide to mastering React

frameworksthatsilentlyfailwhenyouforgetarequiredattribute.

TheCatch

So:what’sthecatch?Allthisniceerrorhandlingmusthaveacatch,right?

Well, sort of. The only catch with propTypes is that you must remember todeclare them. Providing propTypes is optional, andReactwon’t give you anywarningsifyoudon’tspecifypropTypesforoneofyourcomponents.

Youcaneithervowtobesuper-diligentaboutwritingthosepropTypes,oryoucanhavealintertoolcheckforyou.Irecommendthesecondone.

ESLintisapopularchoice,andthereisaReactpluginforESLintthatwillcheckforthingslikemissingPropTypesandthatpropsarepassedcorrectly.

HowDoIValidateThee,LetMeCounttheWays

React’spropTypesallowyoutoexpressmanydifferentvalidations.Bydefault,apropTypevalidationisoptional.Ifyouwanttomakeitrequired,add.requiredtotheend.

First,therearevalidatorsforthestandardJavaScripttypes:

PropTypes.array

PropTypes.bool

PropTypes.func

PropTypes.number

PropTypes.object

PropTypes.string

You saw thestring andnumber validators in theComment component earlier.The others work the same way – validating that an array is passed in, or aboolean,orafunction,orwhatever.

There are validators for node and element. A node is anything that can berendered,meaningnumbers,strings,elements,oranarrayofthose.AnelementisaReactelementcreatedwithJSXorbycallingReact.createElement:

Page 62: Pure React: A step-by-step guide to mastering React

PropTypes.node

PropTypes.element

There’saninstanceOfvalidator forchecking that theprop isan instanceofaspecificclass.Ittakesanargument:

PropTypes.instanceOf(SpecificClass)

YoucanlimittospecificvalueswithoneOf:

PropTypes.oneOf(['person','place',1234])

Youcanvalidatethatthepropisoneofafewtypes:

PropTypes.oneOfType([

PropTypes.string,

PropTypes.boolean

])

You can validate that it’s an array of a certain type, or an object whosepropertiesarevaluesofacertaintype:

PropTypes.arrayOf(PropTypes.string)

Wouldmatch:['a','b','c']Wouldnotmatch:['a','b',42]

PropTypes.objectOf(PropTypes.number)

Wouldmatch:{age:27,birthMonth:9}Wouldnotmatch:{age:27,name:'Joe'}

Youcanvalidatethatanobjecthasacertainshape,meaningthatithasparticularproperties.Theobjectpassedtothispropisallowedtohaveextrapropertiestoo,butitmustatleasthavetheonesintheshapedescription.

PropTypes.shape({

name:PropTypes.string,

age:PropTypes.number

})

Thiswillmatchanobjectoftheexactshape,likethis:

Page 63: Pure React: A step-by-step guide to mastering React

person={

name:'Joe',

age:27

}

Itwillalsomatchanobjectwithadditionalproperties,likethis:

person={

name:'Joe',

age:27,

address:'123FakeSt',

validPerson:false

}

ThisPropTypesshapeisrequiringanobjectthathasnameandagekeys,soifweleaveoneofthemoff,itwon’tpass.Thiswouldgenerateawarning:

person={

age:27

}

Similarly,ifwepassthewrongtypeforoneofthosekeys,we’llgetawarning:

person={

name:false,//booleaninsteadofstring

age:27

}

RequiredProps

AnyPropTypevalidationcanbemaderequiredbyadding.requiredtotheendofit.Theseareallrequired:

PropTypes.bool.isRequired

PropTypes.oneOf(['person','place',1234]).isRequired

Page 64: Pure React: A step-by-step guide to mastering React

PropTypes.shape({

name:PropTypes.string,

age:PropTypes.number

}).isRequired

Remember,allPropTypesareoptionalbydefault,whichmeansyouwon’tgetconsolewarningsifyouforgettopassavalue,ormakeatypoinapropname.Inmostcases,you’llwanttoappend.requiredtoyourPropTypevalidations.

CustomValidators

Ifthebuilt-inPropTypevalidatorsaren’texpressiveenough,youcanwriteyourown!Thefunctionshouldtaketheprops,propName,andcomponentName,andreturn an Error if validation fails. Note that’s return an Error, not throw anError.

A customvalidator to check that the passed prop is exactly length 3 (either astringoranarray)wouldlooklikethis:

functioncustomValidator(props,propName,componentName){

//here,propName==="myCustomProp"

if(props[propName].length!==3){

returnnewError(

'Invalidprop`'+propName+'`suppliedto'+

'`'+componentName+'`.Lengthisnot3.'

);

}

}

constCustomTest=({myCustomProp})=>(

<span>{myCustomProp}</span>

);

CustomTest.propTypes={

myCustomProp:customValidator

}

//Thiswillproduceawarning:

ReactDOM.render(

<CustomTestmyCustomProp='not_three_letters'/>,

document.getElementById('root')

);

Page 65: Pure React: A step-by-step guide to mastering React

//Thiswillwork:

ReactDOM.render(

<CustomTestmyCustomProp={[1,2,3]}/>,

document.getElementById('root')

);

//Thiswillalsowork:

ReactDOM.render(

<CustomTestmyCustomProp="abc"/>,

document.getElementById('root')

);

Example:TweetwithPropTypes

One last time, let’s go back to the Tweet example to see how PropTypes areappliedtoarealsetofcomponents.

Recallthatthestructurelookslikethis:

TweetAvatarNameWithHandleTimeMessageReplyButtonRetweetButtonLikeButtonMoreOptionsButton

Andhere’swhatitshouldlooklike:

Startingfromthebottomup,let’saddPropTypestothesecomponents.Makea

Page 66: Pure React: A step-by-step guide to mastering React

copyoftheprops-tweetcodefromearliersowecanmodifyitwithoutfear:

$cp-aprops-tweetproptypes-tweet&&cdproptypes-tweet

$npmstart

Thebuttonsareagoodplacetostart.MoreOptionsButtondoesn’ttakepropsatall,sowedon’tneedtochangeit.

LikeButtoncanoptionallytakeacountprop,sowe’lladdaPropTypeforthat:

constLikeButton=({count})=>(

<spanclassName="like-button">

<iclassName="fafa-heart"/>

{count>0&&

<spanclassName="like-count">

{count}

</span>}

</span>

);

LikeButton.propTypes={

count:PropTypes.number

};

To verify that it works, go to the Tweet component, and try passing a stringinsteadofanumbertotheLikeButton:

//Replacethis:

<LikeButtoncount={tweet.likes}/>

//Withthis:

<LikeButtoncount='foo'/>

Ifyourefreshnow(orwaitforthebrowsertorefreshautomatically),youshouldseeawarninginthedevconsolelikethis:

Warning:FailedpropType:Invalidprop`count`oftype`string`suppliedto`LikeButton`,expected`number`.Checktherendermethodof`Tweet`.

Good!Everythingisworkingcorrectly.

Changethatlinebacktowhatitwasbefore,andnowaddacountPropTypeto

Page 67: Pure React: A step-by-step guide to mastering React

theRetweetButtoncomponentinthesameway.I’llletyoudothisoneonyourown.Goaheadanddothatnow.

Gotit?Nice!IbetyoucanfigureoutthePropTypesforMessageandTime too(theybothtakeoptionalstrings).Goaheadanddothoseyourselftoo.

NameWithHandleisalittlemoreinteresting:ittakesanauthorobjectthatshouldhavenameandhandleproperties.Anyideahowyou’dimplementthatusingoneofthevalidatorsthatcomeswithReact?

If you said React.PropTypes.shape, you would be correct! I’ll also acceptReact.PropTypes.object,even though that’sabit lessexplicit.Let’sseehowtheshapevalidatorwouldlookinthecomponent:

functionNameWithHandle({author}){

const{name,handle}=author;

return(

<spanclassName="name-with-handle">

<spanclassName="name">{name}</span>

<spanclassName="handle">@{handle}</span>

</span>

);

}

NameWithHandle.propTypes={

author:PropTypes.shape({

name:PropTypes.string.isRequired,

handle:PropTypes.string.isRequired

}).isRequired

};

Here,theauthorpropisrequired,anditmusthaveanameandhandleasstrings.

Let’stryanexperiment:setthetweet’sauthor.nametoanumberlike42insteadofastring.Refreshthepageandchecktheconsole.

Reactcomplains,asexpected:

Warning:FailedpropType: Invalidprop `author.handle` of type `number`suppliedto`NameWithHandle`,expected`string`.Checktherendermethod

Page 68: Pure React: A step-by-step guide to mastering React

of`Tweet`.

Takealookatthecomponentthough:

Reactrenderedthenumbereventhoughitdidn’tpasspropsvalidation!This isanimportantthingtokeepinmind:PropTypesareagreatdebuggingtool,butafailed validation won’t stop your code from running. Keep an eye on thatconsolewindow.

Twocomponentsleft:AvatarandTweet.They’reallyours!

Avatariseasy,itjusttakesastring.

Since Tweet takes an object, you could validate it withReact.PropTypes.object, or use a more complex shape. It’s up to you! (Isuggesttryingtofigureouttheshapeyourself).

HowExplicitShouldYouBe?

Whentheobjectpassedtoacomponentissimple,it’seasytojustifywritingoutashapePropType.Whentheobjectismorecomplex,though,youmightstarttowonderifit’sworththeeffort.Theanswerwilldependonyourparticularcase.Ifyougooverboardwithshapeitcouldbedifficulttomaintainlater.

It’salsogoodtofollowtheDRY(Don’tRepeatYourself)principle.Ifyouhaveanexplicitobjectshaperequiredinoneplace,for instanceinNameWithHandle,there’slittlevalueinduplicatingtheshapeintheparentTweetcomponent.Iftheshape of author changes some day, there will be two places to update code.Havingthatsecondcheckdoesn’tbuyyouanything,andinstead,couldactuallycostyoutimeinthefuture.

PropTypesasDocumentation

Page 69: Pure React: A step-by-step guide to mastering React

OnelastthingaboutPropTypes:inadditiontohelpingoutwithdebugging,theyserveasnicedocumentation.Whenyoucomebacktoacomponentafewdays,aweek,oramonthlater,thePropTypeswillserveasa“README”ofsorts.Youwon’thavetoscanthroughthecodetodecipherwhichpropsarerequired.

Exercises

Who says you have to model components after web interfaces? Real-worldobjectsprovidegoodpracticetoo.You’llbuildsomeofthosebelow.

1. Create anAddressLabel component that takes aperson object as a propandrenderstheirnameandaddresslikeso:

FullName

123FakeSt.

Boston,MA02118

2. Create an Envelope component that takes toPerson and fromPerson asprops and uses your AddressLabel fromExercise 1 to display the returnaddressandtherecipientaddress.MakesuretoincludeaStamptoo!

3. CreateaCreditCardcomponentbasedonthisdesign.StyleitupwithCSSor inline styles.Accept acardInfo prop that contains theperson’sname,

Page 70: Pure React: A step-by-step guide to mastering React

expirationdate,creditcardnumber,andbankname.

4. Create a Poster component that takes image, title, and text as props.Render something like the imagebelow.Google “demotivationalposters”forinspiration.

Page 71: Pure React: A step-by-step guide to mastering React

5. Create a single-line email, as would appear in an inbox. Reference thescreenshot below. It should accept an email prop, which contains thesender,subject,date,andmessage.

Page 72: Pure React: A step-by-step guide to mastering React

ChildrenWe’ve seen how JSX can express nested components, just like HTML. Andwe’ve seen that custom components can accept props as arguments, and usethosepropstorendercontentorpassalongtochildcomponents.

There’saspecialpropwehaven’ttalkedaboutyet:it’scalledchildren.

Let’s say you wanted tomake a reusable IconButton component that lookedsomethinglikethis:

Youmightimagineusingitlikethis:

<IconButton>DoTheThing</IconButton>

What happens to that inner text, “Do The Thing”? Well, that’s where thechildrenpropcomesin.

Youhavetoexplicitlyrenderthechildrensomewhereinyourcomponent.Ifyoudon’t, theyareignored.Forinstance,if theIconButtoncomponentlookedlikethis:

functionIconButton(){

return(

<button>

<iclass="target-icon"/>

</button>

);

}

Page 73: Pure React: A step-by-step guide to mastering React

Therenderedcomponentwouldbeabuttonwithanicon,andnotext.Itwouldlooklikethis:

Withasmalltweak,wecanplacethetextaftertheicon:

functionIconButton({children}){

return(

<button>

<iclass="target-icon"/>

{children}

</button>

);

}

DifferentTypesofChildren

Thechildrenpropisalwayspluralizedaschildrennomatterwhetherthere’sasingle child ormultiple children.Moreover, the type ofchildrenwill changedependingonwhatitcontains.

Whentherearemultiplechildren,childrenisanarrayofReactElements.

However,whenthereisonlyonechild,it isasingleReactElement.Thismightseemalittleweird:wouldn’titbeeasiertodealwithifchildrenwasalwaysanarray?

Well,yes,itwould.Butchildrenisusedsooften,andthesingle-childusecaseiscommonenoughthattheReactteamdecidedtooptimizebynotallocatinganarraywhenthere’sonlyonechild.

DealingwiththeChildren

Page 74: Pure React: A step-by-step guide to mastering React

Reactprovidesutilityfunctionsfordealingwiththisopaquedatastructure.

React.Children.map(children,function)React.Children.forEach(children,function)React.Children.count(children)React.Children.only(children)React.Children.toArray(children)

Thefirst two,mapandforEach,workthesameas themethodsonJavaScript’sbuilt-inArray.Theyacceptchildren,whetherit’sasingleelementoranarray,and a function that’ll be called for each element. forEach iterates over thechildrenandreturnsnothing,whereasmapreturnsanarraymadeupofthevaluesyoureturnfromthefunctionyouprovide.

countisprettyself-explanatory:itreturnsthenumberofitemsinchildren.

toArray issimilarly intuitive: itconvertschildren intoaflatarray,whether itwasanarrayornot.

only returns the singlechild,or throwsanexception if there ismore thanonechild.

Youhaveaccess toeverychildelement individually,soyoucanreorder them,removesome,insertnewones,passthechildrendowntofurtherchildren,andsoon.

PropTypesforChildren

Ifyouwantyourcomponenttoacceptzero,one,ormorechildren,usethe.nodevalidator:

propTypes:{

children:PropTypes.node

}

Ifyouwantittoacceptonlyasinglechild,usetheelementvalidator:

propTypes:{

children:PropTypes.element

}

Page 75: Pure React: A step-by-step guide to mastering React

BewarethatthisexpectsasingleReactElementasachild.Thismeansithastobe a custom component, or a tag like<div>.PropTypes.elementwillwarn ifyoupassastringornumber.Ifyouneedtoallowanelementorastring,youcanusetheoneOfTypevalidatorlikethis:

propTypes:{

children:PropTypes.oneOfType([

PropTypes.element,

PropTypes.string

])

}

AswithanyotherpropType,theyareoptionalunlessyouappend.isRequired.

VersusTransclusioninAngular

Ifyou’veusedAngular,youmaybefamiliarwiththeconceptof“transclusion,”a made-up word that Angular uses to mean “take the child elements of adirectiveandinsertthemwhereng-transcludeis.”

React’schildrenpropisasimilarconcept,butmorepowerfulandabiteasiertowrapyourheadaround.

AMoreInterestingExample

In theexampleabove,wesimply insertedsome text.What ifwewanted todosomethingmoreexpressive,likecreatingourowncustomcomponenthierarchy?

Imaginethatweconstructedourown“API”ofsortsforexpressinganavigationheader:

<Nav>

<NavItemurl='/'>Home</NavItem>

<NavItemurl='/about'>About</NavItem>

<NavItemurl='/contact'>Contact</NavItem>

</Nav>

Using the children prop, the Nav component can do things like insert aseparatorbetweeneachitem:

Page 76: Pure React: A step-by-step guide to mastering React

functionNav({children}){

letitems=React.Children.toArray(children);

for(leti=items.length-1;i>=1;i--){

items.splice(i,0,

<spankey={i}className='separator'>|</span>

);

}

return(

<div>{items}</div>

);

}

Thecodeconvertschildrenintoanarray,thenwalksbackwardfromtheendasitinsertsanewelementbetweeneveryexistingelement.

You will implement NavItem in the exercises coming up. It could render asimplelink,oraniconnexttoalink,oranythingyou’dlike.

Exercises

Nowthatyou’veseenhowthechildrenpropworks,herearesomeexercisestoimproveyourunderstanding.

1. Makeacomponenttodisplayan“errorbox”thatlookslikethis:

Invokingthecomponentshouldlooklikethis:

<ErrorBox>

Somethinghasgonewrong

</ErrorBox>

Use the children prop to place the text properly. The image above uses

Page 77: Pure React: A step-by-step guide to mastering React

Bootstrap for styling and Font Awesome for the icon. You can add theselibrariestoyourpublic/index.htmlfileforthestylingiconifyoulike.

2. EveryReactcomponenthasatypeproperty,whichmeansyoucaninspectthe type of a child component by iterating over the children withReact.Children.forEach.

Create the Nav and NavItem components we looked at earlier. The NavItemshouldtakeaurlpropandthelinktextasachild,thenrenderananchortag(a)elementwiththattext,andpointingtothaturl.

Next, modify the Nav component to check that every one of its children is aNavItem,andthrowanerrorifanyothertypeofcomponentisfound.

Asatest,theexamplecodefromearliershouldworkcorrectly:

<Nav>

<NavItemurl='/'>Home</NavItem>

<NavItemurl='/about'>About</NavItem>

<NavItemurl='/contact'>Contact</NavItem>

</Nav>

Andthiscodeshouldthrowanerror:

<Nav>

<NavItemurl='/'>Home</NavItem>

<NavItemurl='/about'>About</NavItem>

<ahref='/contact'>Contact</a>

</Nav>

3. Create a Dialog component which accepts as children Title, Body, andFooter components, all optional. Dialog should verify that all of itschildrenareoneofthesetypes,andshouldoutputsomethingthatlookslikethis:

Page 78: Pure React: A step-by-step guide to mastering React

FeelfreetouseBootstrap’smodalstyles,orcreateyourown.

Page 79: Pure React: A step-by-step guide to mastering React

Example:GitHubFileListYou have a few components under your belt now. You’ve seen props andpropTypes,andwrittensomeJSX.

Beforewemove into state and interactivity, Iwant to go through an examplethatincorporateseverythingwe’veseensofar.There’llbeafewnewtechniquescoveredheretoo.

Sowiththatinmind,let’screateanewcomponentthatreplicatesGitHub’sfilelist.It’swhatyouseeatthetopofeveryGitHubrepository:

BreakIntoComponents

The first step is to outline or highlight all the components in this screenshot.Basicallywhatwe’redoinghereisdrawingboxesaroundthe“div”elementsinthedesign.

Page 80: Pure React: A step-by-step guide to mastering React

NametheComponents

Onceyou’vedecidedwhichpartswillbecomponents,givethemnames.Forthehighlightsabove,Icameupwith theseones(indentedtoshowtheparent/childrelationships):

FileListFileListItem

FileNameFileIconCommitMessageTime

CanYouReuseAnything?

Look through the list of components and see if you can save yourself workanywhere.

Hey, look! That Time component looks very similar to the one we wrote forTweet.LuckilywewrotethatTimecomponentgenerically,tojustacceptatimeinsteadofatweet.Wecanreuseithere.

WhatDataDoesEachComponentNeed?

Page 81: Pure React: A step-by-step guide to mastering React

WhatDataDoesEachComponentNeed?

Next,let’sfigureoutwhatdataeachcomponentneedstorender.ThiswillgiveusthepropsandpropTypesforeachone.

See if you can figure out the propTypes definition for each of these withoutlookingaheadatthecode.

TheFileListshouldtakeoneprop,files,whichisanarrayoffileobjects.

TheFileListItemwilljusttakeasinglefileobjectasthefileprop.Thatobjectshouldhaveaname,type,acommitwithamessage,andalast-modifiedtime.

FileNamewilltakeafileobjectandexpectthatithasanameproperty.

FileIconwilltakeafileobjectanduseitstypepropertytodecidewhichkindoficontoshow.

CommitMessagewilltakeacommitobjectthatshouldhaveamessageproperty.

Finally,Timewill takeanabsolutetime string.We’regoing to reuse theTimecomponentwemadeforTweet,propTypesandall.

Top-DownorBottom-Up?

We’llstartfromthetopandworkdownforthisone.

KeepitRunning

Here’ssomethingtostriveforwhenyou’rebuildingcomponents:Makesure italwaysworks!

Have you ever had the experience of coding, head down, for a long chunk oftimewithouteverrunningthecode?Inevitably, there’ssomethingwrongwhenyourunitthefirsttime.

It’sdisappointing,andit takes thewindoutofyoursails.Youenduptrackingdownabunchof syntaxerrors, logicerrors, andwhateverelsebeforeyoucanseeyourhardworkcometolife.

Page 82: Pure React: A step-by-step guide to mastering React

So as youwrite, try tomake small changes, and refresh often.Make sure thecodealwaysworks.

FileList

We’vegotenoughdirectiontostartworking.Createanewprojectthesamewaywe’vedoneafewtimesnow:

$create-react-appgithub-file-list&&cdgithub-file-list

$rmsrc/App.*src/logo.svg

Copy theindex.html file from thetweetproject, from the“public”directory.Changethe<title>ifyou’dlike,butasidefromthat,index.htmlisfineasitis.

Openupsrc/index.js.

Doyourememberhowtostartoff thefilewiththeimports,andhowtosetuptheinitialrendercallwithReactDOM?Trytodoitfrommemory.LookbackattheTweetexampleifyourmemoryisfuzzy.

CreatetheFileListcomponent.Intheinterestofdoingthesimplestthingthatcan possibly work, we’ll render a plain unordered list of file names. Once itworkswe’llextractthelistitemsintoaFileListItemcomponent.

//puttheimportshere

constFileList=({files})=>(

<tableclassName="filelist">

<tbody>

{files.map(file=>(

<trclassName="filelist-item"key={file.id}>

<tdclassName="file-name">{file.name}</td>

</tr>

))}

</tbody>

</table>

);

FileList.propTypes={

files:PropTypes.array

};

consttestFiles=[

Page 83: Pure React: A step-by-step guide to mastering React

{

id:1,

name:'src',

type:'folder',

updated_at:"2016-07-1121:24:00",

latestCommit:{

message:'Initialcommit'

}

},

{

id:2,

name:'tests',

type:'folder',

updated_at:"2016-07-1121:24:00",

latestCommit:{

message:'Initialcommit'

}

},

{

id:3,

name:'README',

type:'file',

updated_at:"2016-07-1821:24:00",

latestCommit:{

message:'Addedareadme'

}

},

];

//puttheReactDOM.rendercallhere

//passtestFilesasFileList'sfileprop

Itshouldlooklikethis,niceandugly:

MappingoveranarrayishowyourenderlistsofthingsinReact.

The“key”Prop

Page 84: Pure React: A step-by-step guide to mastering React

Thekeyproponthe<tr>isaspecialone,andit’srequiredanytimeyourenderan array of elements. React uses it to tell components apartwhen reconcilingdifferencesduringare-render.Ifyou’dlikemoredetailsaboutthereasonkeysareimportant,readtheofficialdocsonthereconciliationalgorithm.

Any timeyouusemap to renderanarray,you’ll alsoneedkeyon the topmostelement.Reactconsumesthekeypropbeforerendering,sothecomponentyoupasskeytowillnotactuallyreceivetheprop.

In practice, the important thing to keep inmind is that keys should be stable,permanent,anduniqueforeachelementinthearray.

Stable: An element should always have the same key, regardless of itspositioninthearray.Thismeanskey={index}isabadidea.Permanent: An element’s key must not change between renders. Thismeanskey={Math.random()}isabadidea.Unique:Notwoelementsshouldhavethesamekey.

IfanitemhasauniqueIDattachedtoit,that’sagreatchoiceforthekey.Iftheitemsdon’thaveIDs,tryusingahashingfunction,orgenerateuniqueIDswithalibrarylikeshortid.

The item’s array index is not a good choice because if the index changes, forinstancewhenanelementisaddedtothefrontofthearray,React’smappingofindexeswillbecomeoutdated.Theitemthatwaspreviouslyindex“0”willnowbe “1”, but React doesn’t know that, and it might cause tough-to-diagnoserenderingbugs.

FileListItem

It’s generally a good idea to create a standalone component to render theindividual items in a list, so we’ll do that now. Even though this example issmall,anditcouldbeleftalone,we’llpullitouttodemonstratetheprocess.

Notice that we still need to pass the key prop. The key needs to be assigneddirectlytothetop-levelcomponentsinthearray,nottheirchildren.

constFileList=({files})=>(

<tableclassName="filelist">

Page 85: Pure React: A step-by-step guide to mastering React

<tbody>

{files.map(file=>

/*nowweuseFileListItemhere*/

<FileListItemkey={file.id}file={file}/>

)}

</tbody>

</table>

);

FileList.propTypes={

files:PropTypes.array

};

constFileListItem=({file})=>(

/*thiscodehasbeenextractedfromFileList*/

<trclassName="filelist-item">

<tdclassName="file-name">{file.name}</td>

</tr>

);

FileListItem.propTypes={

file:PropTypes.object.isRequired

};

We’llalsoaddsomeCSStomakeitmorepresentable.Openupsrc/index.cssandreplaceitscontentswiththiscode:

.filelist{

font-family:Helvetica,sans-serif;

width:980px;

color:#333;

margin:0auto;

border:1pxsolid#ccc;

border-collapse:collapse;

}

.filelisttd{

border-top:1pxsolid#ccc;

}

.file-name{

padding:4px;

max-width:180px;

}

Don’tforgettoaddthelinetoimporttheCSSfileifyouhaven’talready(import

Page 86: Pure React: A step-by-step guide to mastering React

'./index.css').

Now that we’ve got some basic structure in place, we can add the remainingcomponents to the row component. FileIcon, FileName, CommitMessage, andTime.

Let’s take care of FileIcon and FileName first, since they’re nested together.Trytowritethecodeyourselfbeforelookingahead.Thisisaweirdone.

Here’sFileIcon.Thisoneisstraightforward–astatelesscomponentwrittenasaplainfunction.

functionFileIcon({file}){

leticon='fa-file-text-o';

if(file.type==='folder'){

icon='fa-folder';

}

return(

<tdclassName="file-icon">

<iclassName={`fa${icon}`}/>

</td>

);

}

FileIcon.propTypes={

file:PropTypes.object.isRequired

};

HereisgetFileName,which,ifyoulookclosely,isnotacomponent.

functiongetFileName(file){

return[

<FileIconfile={file}key={0}/>,

<tdclassName="file-name"key={1}>{file.name}</td>

];

}

Page 87: Pure React: A step-by-step guide to mastering React

This is just a function that takes a file and returns an array of elements.Componentscannotreturnarrayswithoutwrappingthemwithanelement.Whywriteitthisway?

Theproblemisthatwe’reinsideatablerow(<tr>)andweneedtwo<td>snexttoeachother,butatruecomponentcannotreturnabarearraywithoutawrapper.

You could avoid this problem entirely by writing FileIcon and FileName asseparatecomponents thateachreturna<td>cell (rather thannestingFileIconinsideFileName likewe’redoinghere).That isarguablyabetter solution thanthis! I left it thisway to show this edge case in case you run into a situationwhereit’sunavoidable.Avoidthiskindofthingifyoucan.

At the timeofwriting,React16 (a.k.a.ReactFiber) is just around the corner,and promises to solve this problem of returning multiple elements from acomponent.YoucantracktheprogressofFiberathttp://isfiberreadyyet.com/.

Noticethatwe’repassinghardcodedvaluesforkey,here.Didn’tIjustsaynottodothat?

Yeah, this is normally a bad idea, but because this array will always have 2elements in that specific order, it won’t cause a problem here. The key isrequiredbecausewe’rereturninganarray,andelementsinanarraymusthaveakey.

Here’stheupdatedFileListItemthatusesthegetFileNamefunction:

constFileListItem=({file})=>(

<trclassName="filelist-item">

{getFileName(file)}

</tr>

);

FileListItem.propTypes={

file:PropTypes.object.isRequired

};

Alright,withthatweirdnessoutoftheway,let’saddsomeCSSandseehowitlooks.

Page 88: Pure React: A step-by-step guide to mastering React

.file-icon{

width:17px;

padding-left:4px;

}

.file-icon.fa-folder{

color:#508FCA;

}

CommitMessage

Let’s create the CommitMessage component next. This one is verystraightforward, and so is the CSS. We’ll use CommitMessage insideFileListItem.

constFileListItem=({file})=>(

<trclassName="filelist-item">

{getFileName(file)}

<CommitMessage

commit={file.latestCommit}/>

</tr>

);

FileListItem.propTypes={

file:PropTypes.object.isRequired

};

constCommitMessage=({commit})=>(

<tdclassName="commitmessage">

{commit.message}

</td>

);

CommitMessage.propTypes={

commit:PropTypes.object.isRequired

};

.commitmessage{

Page 89: Pure React: A step-by-step guide to mastering React

max-width:442px;

padding-left:10px;

overflow:hidden;

}

Notice thatwe’repassing in thecommit itself insteadof thewhole fileobject.CommitMessage doesn’t need to know anything about files, and the fewercomponentsthathaveknowledgeofdatastructures,thebetter.

Time

We’lladdthetimenow.RememberthatwecanreusetheTimecomponentfromthe Tweet exercise. Rather than just copy-and-paste the Time component intothisfile,we’llextractitintoitsownfilesoothercomponentscanuseittoo.

We’regoingtoneedMoment.jsagain,soinstallthatnow:

$npminstall--savemoment

Thencreatethefilesrc/time.jsandpasteintheTimecomponentfromearlier.Wealsoneedtoaddimportsatthetop,andanexportatthebottom.

importReactfrom'react';

importPropTypesfrom'prop-types';

importmomentfrom'moment';

constTime=({time})=>{

consttimeString=moment(time).fromNow();

return(

<spanclassName="time">

{timeString}

</span>

);

};

Time.propTypes={

time:PropTypes.string.isRequired

Page 90: Pure React: A step-by-step guide to mastering React

};

exportdefaultTime;

YoumightnotrecognizetheexportdefaultTimesyntaxatthebottom.ThisistheES6wayofmakingacomponentavailablesoitcanbeimportedintootherfiles. The “default” means that this is the component we’ll get when we useimportTimefrom'./time'.

Thealternativeistomakethisanamedexport,whichwouldlooklikeexport{Time},withthebraces.Thenthecorrespondingimportwouldlooklikeimport{Time}from'./time'.

Importsareallaboutthebraces.Nobraces?You’reimportingthedefault.Withbraces?You’reimportinganamedexport.Youcanevenmixthem:

importReact,{Component}from'react';

Think of it like destructuring, where the module is the “object” and you’reextractingnameditemsfromit.

Alright, let’suseTime insideFileListItem.Firstweneed to import it, soaddthislinetothetopofindex.js:

importTimefrom'./time';

Sincewe’re importing our own file instead of something fromnode_modules,thepathneedstoberelative(./time)ratherthanjustthemodulename(time).

Then,wecanupdateFileListItem.NotethatTimedoesn’trendera<td>sowehavetowrapitinone:

constFileListItem=({file})=>(

<trclassName="filelist-item">

{getFileName(file)}

<CommitMessagecommit={file.latestCommit}/>

<tdclassName="age">

<Timetime={file.updated_at}/>

Page 91: Pure React: A step-by-step guide to mastering React

</td>

</tr>

);

FileListItem.propTypes={

file:PropTypes.object.isRequired

};

Addalittlebitofstyling…

.age{

width:125px;

text-align:right;

padding-right:4px;

}

Anditworks!

I must say though, the code does not look very nice. We’re rendering 3components in 3 different ways, including the weird getFileName to hackarounda limitation inReact. Itwouldbemuchbetter if thecomponent lookedlikethis:

constFileListItem=({file})=>(

<trclassName="filelist-item">

<td><FileIconfile={file}><td>

<td><FileNamefile={file}><td>

<td><CommitMessagecommit={file.latestCommit}><td>

<td><Timetime={file.updated_at}><td>

</tr>

);

FileListItem.propTypes={

file:PropTypes.object.isRequired

};

AtthispointIcould’vegonebackandeditedtheexamplessothatthecodecame

Page 92: Pure React: A step-by-step guide to mastering React

outbetter.ButIleftitthiswayasanexample:sometimes,maybeevenoften,thecode won’t come out quite as clean as you imagined it. Youmight not fullyrealizetheimpactofadecisionuntilyou’velivedwithitforawhile.

Realityoftengetsintheway.Youforgetthattablecellsmustbedirectchildrenoftheirrows,adheretoocloselytothemockup,andtakeyourselfdownapaththatresultsinbadcode.

But nothing is permanent!Nowwould be a perfect time to refactor this code(andinfact,you’lldothatintheexercises).

Asyou’repluggingaway,writingyourcode…whenthatthoughtpopsintoyourheadthatsays,“Wait!Thesecomponentswon’tbereusableatallbecauseeveryoneofthemcontainsatablecell!”…well,listentothatvoice.Refactorearlyandoften.

Exercises

1. Refactor theGitHub file listing example so that none of the componentsreturnatablecell(<td>).Everycomponentshouldreturna<span>or<div>instead.Thismakes themmore reusable, andalso shouldgreatly improvethecodeinsideFileList.ChangetheCSSifnecessary.

2. Inrealapplications,you’llwanttohaveonlyonecomponentperfilemostofthetime.Sometimesafilewillcontainafewrelatedcomponents,whenthosecomponentsarealwaysusedtogether(asintheNav/NavItemexamplefromearlier),butotherwisetheyshouldbeseparatedout.RefactorthecodefromExercise1topulloutcomponentsintoseparatefiles,usingimportandexport.

3. Nowyouknowhowtocreatelists,usingArray’s.mapfunction.ReusetheTweetcomponentfromearlierandcreatealistofTweets.

Lists are all over the place. It’s been said that most web applications arebasicallyjustabunchoflists.Implementtheseinterfacesfromsitesaroundtheweb.Follow the sameprocesswe’vedonea few timesnow–highlightwhichpiecesofthescreenwillbecomponents,givethemnames,thenbuildthem.

4.Trello

Page 93: Pure React: A step-by-step guide to mastering React

Workonrenderingasinglelistofcards.Formorepractice,rendermultiplelistsofcardsside-by-side.

(screenshotfromhttps://trello.com)

5.HackerNews

Implementthelistofstories.Formorepractice,implementtheheadertoo.

(screenshotfromhttps://news.ycombinator.com/news)

Page 94: Pure React: A step-by-step guide to mastering React

6.Pinterest

Implementthelistofimagecards.Formorepractice,implementtheheadertoo(considerreusingtheNavandNavItemcomponentsfromearlier).

(sceenshotfromhttps://www.pinterest.com/aviationhd/)

Page 95: Pure React: A step-by-step guide to mastering React

7.InternetRadiogenrecloud

Canyoucomeupwithanicewayofsizingthebuttonssotheygetprogressivelylarger?

(screenshotfromhttps://www.internet-radio.com/)

Page 96: Pure React: A step-by-step guide to mastering React
Page 97: Pure React: A step-by-step guide to mastering React

StateUpuntilthispointwe’veusedpropstopassdatatocomponents.Butpropsareread-only.Whatifyouneedtokeeptrackofdatathatcanchange?Thisiswherestatecomesin.

Toseehowstateisuseful,let’slookbackatanexamplefromthePropschapter.Here is aParent component that contains aChild component.Parent passesdownafunctionwhichChildcallswheneverabuttonisclicked:

Example:ACounter

functionhandleAction(event){

console.log('Childdid:',event);

}

functionParent(){

return(

<ChildonAction={handleAction}/>

);

}

functionChild({onAction}){

return(

<buttononClick={onAction}>

ClickMe!

</button>

);

}

What if we wanted Parent to keep track of howmany times the button wasclicked?Inotherwords,ParentshouldtrackhowmanytimesitshandleActionfunctioniscalled.

Todothis,Parentneeds3newthings:aninitialstatewherethecounterissetto0, a call tothis.setState to increment the counter, andaway todisplay thecurrentcount.

Page 98: Pure React: A step-by-step guide to mastering React

Remember, though, that function components are stateless. To make Parentstateful,itneedstobetransformedintoanES6classcomponent.

Here’sthenewversionofParent,renamedtoCountingParent–makesuretoupdatethecalltoReactDOM.render!

classCountingParentextendsReact.Component{

state={

actionCount:0

}

handleAction=(action)=>{

console.log('Childsays',action);

//actionCountisincremented,and

//thenewcountreplacestheexistingone

this.setState({

actionCount:this.state.actionCount+1

});

}

render(){

return(

<div>

<ChildonAction={this.handleAction}/>

<p>Clicked{this.state.actionCount}times</p>

</div>

);

}

}

TheChildcomponentdoesn’thavetochangeatall.Tryitout!Clickthebutton.Watchitincrement.Ooooh.Aaaah.Suchcounter.

Oksomaybeincrementinganumberisnotagroundbreakingachievement,butlet’sseewhat’sgoingonhere.

Whenthecomponentisfirstinstantiated,itsinitialstateissetupviathestate={...}propertyinitializer.AlthoughthissyntaxisnotofficiallypartofES6,itissupported by Babel and Create React App, and widely used in the Reactcommunity.(Analternativeistosetupinitialstateinaconstructor,whichwe’llseelater).

Page 99: Pure React: A step-by-step guide to mastering React

Next,youclickthebutton, likeit tellsyouto.Thebutton’sonClickhandler iscalled. In this case that’s the onAction prop, which ultimately calls thehandleAction function inCountingParent.ThehandleAction function logsamessage and then calls this.setState with an object that describes the newstate.

ThesetState functionwillupdate the state and then re-render thecomponentandallof itschildren.So that’swhathappensnext:CountingParent’srenderfunction is called, which looks at this.state.actionCount, which has nowbeenincrementedto1.Or2,orsomeothernumberifyou’vebeenclick-happywiththatbutton.

Note that every instance of a component has its own state. If you have twoCountingParent components on the page, they’ll each have counters thatwillstart at 0 and increment independently. You can prove this to yourself bycreatingacomponentthatcontainsafewCountingParents:

constPage=()=>(

<div>

<CountingParent/>

<CountingParent/>

<CountingParent/>

</div>

);

Make sure to update the ReactDOM.render call to render a Page instead of aCountingParent. Now click those buttons and watch the counters changeindependently.

Exercise:ResetButton

Here’saquickexercise:adda‘Reset’buttontoCountingParentthatresetsthecounterto0whenclicked.JustputthebuttondirectlyinsideCountingParent.

Once that’sworking, refactoryourcode tomove the ‘Reset’buttondown intoChild.

setStateIsAsynchronous

Page 100: Pure React: A step-by-step guide to mastering React

I lied to you up there. I’m sorry. I implied that the setState functionwouldimmediately update the state and callrender. That’s not reallywhat happens.ThesetStatefunctionisactuallyasynchronous.

If you callsetState and immediatelyconsole.log(this.state), itwill verylikelyprinttheoldstateinsteadoftheoneyoujustset.

Ifyouneedtosetthestateandimmediatelyactonthatchange,youcanpassinacallbackfunctionlikethis:

this.setState({name:'Joe'},function(){

//calledafterstatehasbeenupdated

//andthecomponenthasbeenre-rendered

});

FunctionalsetState

AnotherwaytomakeitsothatsequentialstateupdatesruninsequenceistousethefunctionalformofsetState,likethis:

this.setState((state,props)=>{

return{

value:state.value+1

}

});

Inthisform,youpassafunctiontosetState.Thefunctionreceivesthecurrentstateandpropsasarguments,anditisexpectedtoreturnthedesirednewstate.Ifyouweretorunafewofthesesequentially…

this.setState((state,props)=>{

return{

value:state.value+1

}

});

this.setState((state,props)=>{

return{

value:state.value+1

}

Page 101: Pure React: A step-by-step guide to mastering React

});

this.setState((state,props)=>{

return{

value:state.value+1

}

});

This would work as expected, eventually incrementing value by 3. It worksbecausecallingsetState“queues” theupdates in theorder they’recalled,andwhen they’re executed, they receive the latest state as an argument instead ofusingapotentially-stalethis.state.

A side benefit to the functional style of setState is that the state updatefunctionscanbeextractedfromtheclassandreusedbecausetheyare“pure”–thatis, theyonlyoperateontheirarguments, theydon’tmodifythearguments,andtheyreturnanewvalue.A“pure”functionhasnosideeffects,whichmeansthat calling it multiple times with the same arguments will always return thesameresult.

ShallowvsDeepMerge

WhenyoucallsetState,whetheryoucallitwithanobjectorinthefunctionalform, the result is that itwillshallowmerge theproperties inyourobjectwiththecurrenstate.Here’showthatworks.

The“shallowmerging”behaviormeansthatifyouhaveastatelikethis:

{

score:7,

user:{

name:"somebody",

age:26

},

products:[/*...*/]

}

Andthenyoudothis.setState({score:42}),thenewstatewillbe:

{

Page 102: Pure React: A step-by-step guide to mastering React

score:42,//new!

user:{//unchanged

name:"somebody",//unchanged

age:26//unchanged

},

products:[/*unchanged*/]

}

That is, itmerges theobjectyoupass tosetState(orreturnfromthefunctionalversion)withtheexistingstate.Itdoesn’terasetheexistingstate.

If instead you run this.setState({ user: { age: 4 } }) then it wouldreplacetheentireuserobjectwiththenewone:

{

score:7,//unchanged

user:{//new!

age:4//nomore'name'

},

products:[...],//unchanged

}

A “deep” merge would peek into the user object and only update its agepropertywhile leavingtherestalone.A“shallow”mergeoverwrites thewholeuserobjectwiththenewone.

HandlingEvents

We’ve seen a few components that take an onClick prop. This is just one ofmanyevents thatReactcomponentscanhandle.Infact,ReactcomponentscanrespondtoeveryeventthatplainoldHTMLelementscan,forthemostpart.

TheconventionisthatReact’seventsarenamedwithcamelCaselikeonClick,onSubmit,onKeyDown…whereastheHTMLeventsareall lowercase(onclick,onsubmit, onkeydown). React will actually warn you if you use the wrongcapitalization:

Warning: Unknown event handler property onclick. Did you mean`onClick`?

Page 103: Pure React: A step-by-step guide to mastering React

At least one event differs by more than just capitalization: ondblclick isrenamedtoonDoubleClick.AcompletelistofeventscanbefoundintheofficialFacebookdocs.

Youreventhandlerfunctionwillreceivetheeventobject,whichlooksalotlikea native browser event. It has the standard stopPropagation andpreventDefault functions if you need to prevent bubbling or cancel a formsubmission,forexample.It’snotactuallyanativeeventobject though–it isaSyntheticEvent.

Theeventobjectpassedtoahandlerfunctionisonlyvalidrightatthatmoment.TheSyntheticEventobjectispooledforperformance.Insteadofcreatinganewoneforeveryevent,Reactreplacesthecontentsoftheonesingleinstance.

Ifyouprintitoutwithconsole.log(event),theinstanceloggedtotheconsolewill be cleared out by the time you go to look at it. You also can’t access itasynchronously (say,aftera timeout). Ifyouneed toaccess itasynchronously,youcancallevent.persist()andReactwillkeepitaroundforyou.

WhattoPutinState

Howdoyoudecidewhat shouldgo into state? Is there anywhereelse to storepersistentdata?

Asageneralrule,datathatisstoredinstateshouldbereferencedinsiderendersomewhere.ComponentstateisforstoringUIstate–thingsthataffectthevisualrenderingof thepage.Thismakessensebecauseanytimestate isupdated, thecomponentwillre-render.

Ifmodifyingapieceofdatadoesnotvisuallychangethecomponent, thatdatashouldn’tgointostate.Herearesomethingsthatmakesensetoputinstate:

User-enteredinput(valuesoftextboxesandotherformfields)Currentorselecteditem(thecurrenttab,theselectedrow)Datafromtheserver(alistofproducts,thenumberof“likes”onapage)Open/closedstate(modalopen/closed,sidebarexpanded/hidden)

Other stateful data, like handles to timers, should be stored on the componentinstanceitself.You’vegotathisobjectavailableinclasscomponentsclasses,

Page 104: Pure React: A step-by-step guide to mastering React

feelfreetouseit!

ThinkingDeclaratively

Getting used to React involves changing how you solve certain kinds ofproblems.Itremindsmealittlebitoflearningtodriveontheothersideoftheroad.

ThefirsttimeIexperiencedthis,IwasvisitingTurksandCaicos.Theydriveontheleftthere.BeingfromtheUSwherewedriveontheright,thistookabitofreprogramming.Inearlydiedonthewayoutoftheairport.

The funny thing was, even after I had learned to drive on the left in normalstraight-and-level driving, my brain would revert to old habits whenever adifferentsituationcameup.

Turning into a parking lot?Habit took over and I drove into thewrong lane.Takingaleftatastopsign?Sameproblem.Takingarightatastopsign?You’dthink I would’ve learned by now – but no, to my brain, that was differentsomehow.

I tell this storybecause I had a similar experiencewhen learningReact, and Ithinkmanyothersdotoo.

Passing props to a component (as if that component were a function) makessense–ourbrainsareusedtothat.ItlooksandworkslikeHTML.

The idea of passing datadown and passing eventsup alsomakes sense prettyquickly,forsimplecases.It’sthe“callback”pattern,commonlyusedelsewhere,sonotallthatforeign.PassinganonClickhandlertoabuttonisfairlynormal.

Butwhathappenswhenit’s timetoopenamodaldialog?OrdisplayaGrowl-stylenotificationinthecorner?Oranimateaniconinresponsetoanevent?Youmight find, as I did, that these imperative, “event-based” things don’t comenaturallyinthedeclarativeworldofReact.

HowtoDevelop“Declarative”Thinking

If you came from jQuery or Angular or any other frameworkwhere you call

Page 105: Pure React: A step-by-step guide to mastering React

functions to make things happen (“imperative programming”), you need toadjustyourmentalmodelinordertoworkeffectivelywithReact.You’lladjustprettyquicklywithpractice–you justneeda fewnewexamplesor“patterns”foryourbraintodrawfrom.

Hereareafew.

Expanding/CollapsinganAccordioncontrol

Theoldway:Clickingatogglebuttonopensorclosestheaccordionbycallingitstogglefunction.TheAccordionknowswhetheritisopenorclosed.

The declarative way: The Accordion is either in the “open” state, or the“closed” state, and we store that information as a flag inside the parentcomponent’sstate(notinsidetheAccordion).WetelltheAccordionwhichwaytorenderbypassingisOpenasaprop.WhenisOpenistrue,itrendersasopen.WhenisOpenisfalse,itrendersasclosed.

<AccordionisOpen={true}/>

//or

<AccordionisOpen={false}/>

Thisexampleisprettysimple.Hopefullynothingtoomind-bending.Thebiggestchange is that in thedeclarativeReactway, theexpand/collapse state is storedoutsidetheAccordionandpassedinasaprop.

OpeningandClosingaDialog

The old way: Clicking a button opens the modal. Clicking its Close buttonclosesit.

Thedeclarativeway:WhetherornottheModalisopenisastate.It’seitherinthe“open”stateorthe“closed”state.So,ifit’s“open”,werendertheModal.Ifit’s “closed” we don’t render the modal. Moreover, we can pass an onClosecallback to the Modal – this way the parent component gets to decide whathappenswhentheuserclicksClose.

Page 106: Pure React: A step-by-step guide to mastering React

<div>

{this.state.isModalOpen&&

<ModalonClose={this.handleClose}/>}

</div>

Notifications

Theoldway:Whenaneventoccurs(likeanerror),callanotificationlibrarytodisplayapopup,liketoastr.error("Ohno!").

The declarative way: Think of notifications as state. There can be 0notifications, or 1, or 2… Store those in an array. Put a NotificationTraycomponent somewhere near the root of the app, and pass it the messages todisplay.Youcanmanage thearrayofmessages in the root component’s state,andpassanaddNotificationpropdowntocomponentsthatneedit.

AnimatingaChange

Let’s say you have a badgewith a counter showing the number of logged-inusers. Itgets thisnumber fromaprop.What ifyouwant thebadge toanimatewhenthenumberchanges?

Theoldway:YoumightusejQuerytotoggleaclassthatplaystheanimation,orusejQuerytoanimatetheelementdirectly.

Thedeclarativeway:YoucanrespondwhenpropschangebyimplementingthecomponentWillReceiveProps lifecyclemethod and comparing the old value tothe new one (you’ll learn more about lifecycle methods in Chapter 14). If itchanged, you can set the “animating” state to true. Then in render, when“animating”istrue,addaCSSclassthatdoestheanimation.When“animating”isfalse,don’taddthatclass.Here’swhatthismightlooklike:

classBadgeextendsComponent{

componentWillReceiveProps(nextProps){

if(this.props.counter!==nextProps.counter){

//Setanimatingtotruerightnow.

//Whenthestatechangefinishes,setatimer

//toturnanimatingoffin200ms.

Page 107: Pure React: A step-by-step guide to mastering React

this.setState({animating:true},()=>{

setTimeout(()=>{

this.setState({animating:false});

},200);

});

}

}

render(){

constanimatingClass=

this.state.animating?'animating':'';

return(

<divclassName={`badge${animatingClass}`}>

{this.props.counter}

</div>

);

}

}

I justwant towarnyou that thiswayofwritingcodemayfeelstrangeat first,and that’snormal.You’ll findyourselfwonderinghowto implement thoseoldimperativepatternsinadeclarativeway,andit’lltakealittletimetolearnnewpatterns. But don’tworry – itwill become crystal clear once you do it a fewtimes.

WheretoKeepState

Wheneveryoucan,it’sbesttokeepcomponentsstateless.Sometimesthisisn’tpossible,butoften,piecesofdatayouinitiallythinkshouldgointointernalstatecanactuallybepulleduptotheparentcomponent,orevenhigher.

Thinkaboutasectionofthepagethatcanbeshownorhidden,maybeasidebarlikethis:

Page 108: Pure React: A step-by-step guide to mastering React

Whenyouclick the“Hide”button, the sidebar shoulddisappear,whichmeanssetting a showSidebar flag to false. This flag should be stored in statesomewhere.Butwhere?

Option 1, the showSidebar flag could reside in the Sidebar component. Thisway the Sidebar “knows” whether it is open or not. This is similar to howuncontrolledinputswork,whichwe’llseeinthenextchapter.

Option2,theshowSidebarflagcangointheparentLayoutcomponent,andtheparentcandecidewhethertorendertheSidebarornot:

Page 109: Pure React: A step-by-step guide to mastering React

classLayoutextendsReact.Component{

state={

showSidebar:false

}

toggleSidebar=()=>{

this.setState({

showSidebar:!this.state.showSidebar

});

}

render(){

const{showSidebar}=this.state;

return(

<divclassName="layout">

{showSidebar&&

<Sidebar

onHide={this.toggleSidebar}>

somesidebarcontent

</Sidebar>}

<Content

isSidebarVisible={showSidebar}

onShowSidebar={this.toggleSidebar}>

somecontenthere

</Content>

</div>

);

}

}

constContent=({

children,

isSidebarVisible,

onShowSidebar

})=>(

<divclassName="content">

{children}

{!isSidebarVisible&&(

<buttononClick={onShowSidebar}>

Show

</button>

)}

</div>

);

constSidebar=({

onHide,

Page 110: Pure React: A step-by-step guide to mastering React

children

})=>(

<divclassName="sidebar">

{children}

<buttononClick={onHide}>

Hide

</button>

</div>

);

This way Sidebar doesn’t internally “know” whether it’s visible. It is eitherrendered,ornotrendered.

Keeping the state in a parent component might feel unnatural. It might evenseematfirstglancelikethiswouldmakeSidebarhardtoreuse,sinceyouneedtopassacallbackfunctionthatSidebarcancallwhenitneedstohide.

Onthecontrary,havingfewercomponentscontainingstatemeansfewerplacestolookwhenabugappears.Andifyouneedtodoanythingwiththatstate,forinstance, save it to localStorage to persist across page reloads, that logic onlyneedstoexistinonecomponentinstance.

Browsers comewithlocalStorage, a placeyoucanuse to savedatabetweensessions.Closingandreopeningthebrowserorrefreshingthepagewon’tclearthe data. You could save the Sidebar state withlocalStorage.setItem('showSidebar', true) and later retrieve it withlocalStorage.getItem('showSidebar').

Youcansee that if theSidebarcomponentknewhowtosave itsownstate tolocalStorage, and thereweremultiple Sidebars in the app, theywould need away to differentiate themselves. A single showSidebar key in localStoragewould lead to conflicts, and the Sidebars don’t know where in the app theyreside.

Perhaps the parents could be responsible for passing down a unique “id” or“location”toeachSidebar.However,thatwouldsplittheconcernof“wheretosave”betweenboththeparentandtheSidebar.Thatmakestwocomponentsthatneed toknowaboutsaving, rather thanone.Abetterdesignwouldkeepallofthesaving-relatedlogicinoneplace,andthatiseasiertodoifthestateisstoredalongsideit.

Page 111: Pure React: A step-by-step guide to mastering React

“Kinds”ofComponents

Architecturally, you can segment components into two kinds: Presentational(a.k.a“Dumb”)andContainer(a.k.a.“Smart”).

Presentationalcomponentsarestateless.Theysimplyacceptspropsandrendersomeelementsbasedonthoseprops.Beingstatelessmeansthatthecomponentwillcontainlesslogic,andwillbeeasiertodebugandtest.Theyare,inessence,purefunctions.Theyalwaysreturnthesameresultforagivensetofprops,andthey don’t change anything. Ideally, most of your components will bepresentational.

Sidebar,aswrittenabove,isapresentationalcomponent.SoisChild,andsoistheTweetcomponentwemadeawhileback.Theyacceptdataandrenderit,andif events need to be handled, they call back up to the parent. Other kinds ofcommon presentational components include buttons, navigation bars, links,images,etc.

Containercomponentsarestateful.Theymaintainstateforthemselvesandanychildcomponents,andpassitdowntothemviaprops.Theyusuallypassdownhandlerfunctionstochildren,andrespondtocallbacksbyupdatingtheirinternalstate. Container components are also responsible for asynchronouscommunication,suchasAJAXcallstotheserver.

The Parent component, above, is a presentational component. Perhapssurprisingly, Page is actually presentational. Presentational components cancontain Container components, and Containers can contain Presentationalcomponents–therearen’tanystrictrulesfornesting.

Inanidealworld,you’dtrytoorganizeyourappsothatthecomponentsattheverytoplevel(andmaybeonelevelbelowthat)arecontainers,andeverythingunderthemispresentational.Intherealworldthisisdifficulttoachievebecauseyoumighthavenestedinputsthatcontaintheirownstate.That’sokaythough–perfectionisnotthegoal.

Page 112: Pure React: A step-by-step guide to mastering React

InputControlsNowthatyou’vegotahandleonhowstateworks,wecan talkabouthandlinguserinput.

InputcontrolsinReactcomeintwoflavors:controlledanduncontrolled.

ControlledInputs

The reason they’re called “controlled” is because you are responsible forcontrollingtheirstate.Youneedtopassinavalue,andkeepthatvalueupdatedastheusertypes.It’salittlemorework,butafteryouwriteafewofthemitwillbecomesecondnature.

Rememberhowstateworks:avalueandacallbackfunctionarepassedasprops,andtheparentcomponentisresponsibleforreactingtochangesandpassinganupdatedvaluetothechild.

classControlledInputextendsReact.Component{

state={text:''};

handleChange=(event)=>{

this.setState({

text:event.target.value

});

};

render(){

return(

<inputtype="text"

value={this.state.text}

onChange={this.handleChange}/>

);

}

}

Itworkslikethis:

Page 113: Pure React: A step-by-step guide to mastering React

Now,maybeyourfirstreactionisthatthisseemslikealotofcodeforasimpleinput.You’renotwrong,butthismethoddoesprovidealotofpower.

In the example above, try removing or commenting out the call tothis.setState,thentrytotypeinthebox.Nothinghappens,becausethevalueisstuckattheinitialvalue.

Then, try changing this.setState to ignore the event data, instead alwayssettingthetexttothesamevalue,likethis:

classTrickInputextendsReact.Component{

state={

text:'trytypingsomething'

};

handleChange=(event)=>{

this.setState({

text:'hahanope'

});

};

render(){

return(

<inputtype="text"

value={this.state.text}

onChange={this.handleChange}/>

);

}

}

You’llseethattheinputthwartsyourattemptstochangeit,andmocksyoutoo.

Thisisverypowerfulthough.Ifyouwanttodosomekindofcustomvalidation,orformatting,youcandothatinthehandleChangefunction.Don’twanttheusertotypenumbers?Stripoutthenumbersbeforeupdatingthestate:

classNoNumbersInputextendsReact.Component{

//...

handleChange=(event)=>{

lettext=event.target.value;

text=text.replace(/[0-9]/g,'');

Page 114: Pure React: A step-by-step guide to mastering React

this.setState({text});

};

//...

}

Alittlesidenote–thatsyntaxthis.setState({text})isjustES6shorthandforthis.setState({text:text}).WithES6,if thekeyis thesameasthevariablename,youdon’thavetowriteittwice.

Try typing some numbers in the box. Try pasting in a string that containsnumbers.Flawless!Noflickeringoranyweirdbehavior.

Youcouldformattheinputasaphonenumber,oracreditcardnumber,ormakechangesinresponsetothecursorposition.LikeIsaid,it’sprettypowerful.

Butmaybeyoudon’tcareaboutallthat.Youjustwanttheinputtomanageitsownstate.Uncontrolledinputswilldothat,butthereareafewcaveats.

UncontrolledInputs

Whenaninputisuncontrolled,itmanagesitsowninternalstate.Soyoucanputaninputonthepagelikethis(withoutavalueprop):

constEasyInput=()=>(

<inputtype="text"/>

);

Typeinit,dowhateveryoulike.Itworksnormally.Ifyouwanttogetavalueoutofit,youcanpassanonChangepropandrespondtotheevents.Theproblemwiththisisthatyoucan’teasilyextractavalueatwill.Youstillneedtolistentothechanges,andkeeptrackofthe“mostrecentvalue”somewhere.

UncontrolledinputscanbegivenadefaultValueprop,andthecomponentwillbe initialized with that value. After that initial render though, if you want tochangethevalueyou’llneedtouseacontrolledinput.

Page 115: Pure React: A step-by-step guide to mastering React

TheComponentLifecycleEvery React component goes through a lifecycle as it’s rendered on screen.Thereareasequenceofmethodscalledinaspecificorder.Someofthemonlyrunonce,andsomerunmultipletimes.

Formostofthecomponentsyoubuild,tappingintothelifecyclemethodswon’tbe necessary.Butwhenyouneed them, youneed them.Whether you need tomakeAJAXcallstofetchdata,insertyourownnodesintotheDOM,orsetuptimers,thelifecyclemethodsaretheplacetodoit.

Here’stheentirelifecycleofacomponent:

Usage

Tappingintoalifecyclehookisassimpleasaddingtheappropriatefunctionto

Page 116: Pure React: A step-by-step guide to mastering React

your component class. Lifecycle methods are only available to classcomponents,notfunctionalones.

Here’sanexamplethatuseseverylifecyclemethod.Runit,clickthebutton,andwatchtheconsoletoseetheorderinwhichthey’recalled.

importReactfrom'react';

importReactDOMfrom'react-dom';

classLifecycleDemoextendsReact.Component{

state={counter:0};

constructor(props){

super(props);

console.log('Constructing...');

console.log('Statealreadysetto:',this.state);

}

componentWillMount(){

console.log('Abouttomount...');

}

componentDidMount(){

console.log('Mounted.');

}

componentWillReceiveProps(nextProps){

console.log('Currentprops:',this.props);

console.log('Nextprops:',nextProps);

}

shouldComponentUpdate(nextProps,nextState){

console.log('Decidingtoupdate');

returntrue;

}

componentWillUpdate(nextProps,nextState){

console.log('Abouttoupdate...');

}

componentDidUpdate(){

console.log('Updated.');

}

componentWillUnmount(){

Page 117: Pure React: A step-by-step guide to mastering React

console.log('Goodbyecruelworld.');

}

handleClick=()=>{

this.setState({

counter:this.state.counter+1

});

};

render(){

console.log('Rendering...');

return(

<div>

<span>Counter:{this.state.counter}</span>

<buttononClick={this.handleClick}>

Clicktoincrement

</button>

</div>

);

}

}

ReactDOM.render(

<LifecycleDemo/>,

document.querySelector('#root')

);

Mounting

Thesemethodsarecalledonlyonce,whenthecomponentfirstmounts.

constructor:Thisisthefirstmethodcalledwhenyourcomponentiscreated.Ifstate is initialized with a property initializer, as shown above, then it willalreadybesetbythetimetheconstructorexecutes.

componentWillMount:Calledimmediatelybeforeyourcomponentmountsforthe first time. Children are mounted before the parent, so each child’scomponentWillMountwillbecalledcalledbeforetheparent.

componentDidMount: Called immediately after the first render. Thecomponent’schildrenarealreadyrenderedatthispoint,too.ThisisagoodplacetomakeAJAXrequeststofetchanydatayouneed.

Updating

Page 118: Pure React: A step-by-step guide to mastering React

Updating

Thesearecalled,inorder,beforeandaftereachrender.Noneofthemarecalledduringtheinitialrender.

componentWillReceiveProps(nextProps): This is passed the nextProps thatwillbe received.Oldpropsare still availableasthis.props, andyoucancallthis.setStateifnecessary.

shouldComponentUpdate(nextProps, nextState): This is an opportunity topreventrenderingifyouknowthatpropsandstatehavenotchanged.Thedefaultimplementation always returns true. If you return false, the render will notoccur(andchildrenwillnotrendereither),andtheremaininglifecyclemethodswillbeskipped.

componentWillUpdate(nextProps, nextState): Rendering is a foregoneconclusionat thispoint.Use this todoanyprepworkbeforerender iscalled.Youcannotcallthis.setStatefromwithinthismethod.

render: You know this one well. It fits in the lifecycle right betweencomponentWillUpdateandcomponentDidUpdate.

componentDidUpdate:Renderisdone.YoucanusethisopportunitytooperateontheDOMifyouneedto.Priortothis,DOMnodescouldstillbeinflux.

Unmounting

componentWillUnmount:Thecomponentisabouttobeunmounted.Maybeitsitem was removed from a list, maybe the user navigated to another tab…whatever the case, this component’s time is numbered.You should invalidateany timers you created (using clearInterval() or clearTimeout()), disableeventhandlers(withremoveEventListener()),andperformanyothernecessarycleanup.

Page 119: Pure React: A step-by-step guide to mastering React

Example:ShoppingSiteInthisexamplewe’llbuildasimpleshoppingsitethatwilldemonstratesomeofthethingswe’vecovered.

Here’swhatitwilllooklikewhenwe’redone.It’llhaveapagefullofitemstobuy:

Anditwillhaveashoppingcartshowingtheselecteditems,with+/-buttonsandacountforeachone:

BreakIntoComponents

Page 120: Pure React: A step-by-step guide to mastering React

Followingthesamestepsasbefore,let’sstartbybreakingdowntheappintoitscomponentsandgivingthemnames.HereistheItemspage:

AndhereistheCartpage:

There’salsoatop-level“App”componentwhichcontainseverythingelse.

You’ll notice that the two pages are very similar. Compared to previousexamples, this one will have less-granular components. The components willhavelargerrenderfunctionswithmore“stuff”inthem.Youcanalwaysbreakthingsdownlater,andsometimesit’sbettertoavoidabstractingintotinypiecesearlyon.

Page 121: Pure React: A step-by-step guide to mastering React

Herearethecomponentswe’llbuild:

AppItemPageCartPageItemNav

StartBuilding

We’llstartbygeneratinganewproject:

$create-react-appshopper&&cdshopper

Openupsrc/App.jsandreplaceitscontentswiththistogetstarted:

importReactfrom'react';

importNavfrom'./Nav';

import'./App.css';

classAppextendsReact.Component{

renderContent(){

return(

<span>Empty</span>

);

}

render(){

return(

<divclassName="App">

<Nav/>

<mainclassName="App-content">

{this.renderContent()}

</main>

</div>

);

}

}

exportdefaultApp;

Thencreatesrc/Nav.jsandstartitoffwiththis:

Page 122: Pure React: A step-by-step guide to mastering React

importReactfrom'react';

constNav=()=>(

<navclassName="App-nav">

<ul>

<liclassName="App-nav-item"><a>Items</a></li>

<liclassName="App-nav-item"><a>Cart</a></li>

</ul>

</nav>

);

exportdefaultNav;

Startuptheappandseewhatwehave:

$npmstart

It’s working, but it needs styling. Open up src/App.css, and replace thecontentswiththis:

.App{

max-width:800px;

margin:0auto;

}

.App-nav{

margin:20px;

padding:10px;

border-bottom:2pxsolid#508FCA;

}

.App-navul{

padding:0;

margin:0;

}

.App-nav-item{

Page 123: Pure React: A step-by-step guide to mastering React

list-style:none;

display:inline-block;

margin-right:32px;

font-size:24px;

border-bottom:4pxsolidtransparent;

}

.App-content{

margin:020px;

}

That’salittlebetter:

Next, we need to keep track of the active tab, and choose whether to renderItemPageorCartPage.Todothis,we’llstoretheactivetabinstate,andmaketheNavtriggerastatechange.TheAppcomponentisthebesthomeforthisstate,sinceAppwillneedittoknowwhichpagetorender.

Initializethestateatthetop,andaddafunctiontoselectatab:

classAppextendsComponent{

state={

activeTab:0

};

handleTabChange=(index)=>{

this.setState({

activeTab:index

});

}

//...

}

Now,weneedtopassthehandleTabChangefunctiondownintoNav,alongwith

Page 124: Pure React: A step-by-step guide to mastering React

theactiveTab so it can render correctly.We’ll also updaterenderContent todisplaytheactivepage.

classAppextendsComponent{

//...

renderContent(){

switch(this.state.activeTab){

default:

case0:return<span>Items</span>;

case1:return<span>Cart</span>;

}

}

render(){

let{activeTab}=this.state;

return(

<divclassName="App">

<NavactiveTab={activeTab}onTabChange={this.handleTabChange

<mainclassName="App-content">

{this.renderContent()}

</main>

</div>

);

}

}

UpdatetheNavcomponentinsrc/Nav.jstousethenewprops:

constNav=({activeTab,onTabChange})=>(

<navclassName="App-nav">

<ul>

<liclassName={`App-nav-item${activeTab===0&&'selected'}`

<aonClick={()=>onTabChange(0)}>Items</a>

</li>

<liclassName={`App-nav-item${activeTab===1&&'selected'}`

<aonClick={()=>onTabChange(1)}>Cart</a>

</li>

</ul>

</nav>

);

Page 125: Pure React: A step-by-step guide to mastering React

Thenaddsomestylestothebottomofsrc/App.css:

.App-nav-itema{

cursor:pointer;

}

.App-nav-itema:hover{

color:#999;

}

.App-nav-item.selected{

border-bottom-color:#FFAA3F;

}

Here’swhatitshouldlooklikenow:

ArrowFunctiononClick

You’llnoticethatwe’repassinganarrowfunctiontotheonClickhandlershere.At first glance this may look strange. You might think onClick=

{onTabChange(0)}looksbetter.

But remember, props are evaluatedbefore they’re passeddown.Whenwrittenwithout the arrow function, onTabChange would be calledwhen Nav renders,insteadofwhenthelinkisclicked.That’snotwhatwewant.Wrappingitinanarrowfunctiondelaysexecutionuntiltheuserclicksthelink.

CreatingFunctionsinRender:Bad?

Anothernoteaboutthis:it’sgenerallyabadideatocreatenewfunctionstopassinto props like the code above, but it’sworth understandingwhy, so you canapplytherulewhenitmakessense.

The reason it’s bad to create new functions is for performance: not becausefunctionsareexpensivetocreate,butbecausepassinganewfunctiondowntoa

Page 126: Pure React: A step-by-step guide to mastering React

purecomponenteverytimeitrendersmeansthatitwillalwayssee“new”props,andthereforealwaysre-render(needlessly).

Avoidcreatinganewfunctioninrenderwhen(a)thechildcomponentreceivingthefunctionpropispureand(b)youexpecttheparentcomponenttore-renderoften.

FunctioncomponentsandclassesthatextendComponentarenotpure.Theywillalways re-render, even if their props haven’t changed. If a class extendsPureComponentinstead,though,itispureanditwillskipre-renderingwhenitspropsareunchanged.Avoidingneedlessre-rendersistheeasiestwaytoimproveperformanceinaReactapp.

The Nav component abovewill probably not re-render very often, but for thesakeofexample,howwouldyougetridofthearrowfunctions?Here’sonewaytodoit:

constNav=({activeTab,onTabChange})=>(

<navclassName="App-nav">

<ul>

<liclassName={`App-nav-item${activeTab===0&&'selected'}`

<NavLinkindex={0}onClick={onTabChange}>Items</NavLink>

</li>

<liclassName={`App-nav-item${activeTab===1&&'selected'}`

<NavLinkindex={1}onClick={onTabChange}>Cart</NavLink>

</li>

</ul>

</nav>

);

classNavLinkextendsReact.Component{

handleClick=()=>{

this.props.onClick(this.props.index);

}

render(){

return(

<aonClick={this.handleClick}>

{this.props.children}

</a>

);

}

}

Page 127: Pure React: A step-by-step guide to mastering React

Here,thehandleClickfunctionwillonlybecreatedonce,whenthecomponentis rendered the first time. This fixes the problem (render no longer creates afunction),butattheexpenseofmorecomplexcode(awholenewcomponent!).It’satradeoff,andoneworthconsideringwhenyourunintothiswithyourownprojects.

CreateItemPage

Let’s turn our attention to the list of items for sale. We’ll create a newcomponenttodisplaythisdata.CreateafilecalledItemPage.jsandtypethisin:

importReactfrom'react';

importPropTypesfrom'prop-types';

import'./ItemPage.css';

functionItemPage({items}){

return(

<ulclassName="ItemPage-items">

{items.map(item=>

<likey={item.id}className="ItemPage-item">

{item.name}

</li>

)}

</ul>

);

}

ItemPage.propTypes={

items:PropTypes.array.isRequired

};

exportdefaultItemPage;

Nothingnewhere:we’re just iteratingover a list of itemsand rendering themout.Sincetheindividualitemsaregoingtobemoreinvolvedthanjusta“name”,we’llextractthatoutintoitsowncomponentshortly.

This component relies on ItemPage.css, which doesn’t exist yet. Create thatfile,andaddthisCSStostylethelist:

Page 128: Pure React: A step-by-step guide to mastering React

.ItemPage-items{

margin:0;

padding:0;

}

.ItemPage-itemsli{

list-style:none;

margin-bottom:20px;

}

We’re going to need some items to sell. We’ll use static data here, same asbefore.

IliketostartwithstaticdataevenwhenIhaveaserverthatcanreturnrealdata.Youcangofromnothingtosomethingprettyquicklywhentherearenomovingparts.Andhonestly,it’sjustmorefunwhenthere’ssomethingonthescreenthatIcantweak.

Createafilecalledstatic-data.js(undersrc)andpasteinthiscode.

letitems=[

{

id:0,

name:"AppleiPadMini216GB",

description:"AniPadlikenoother.16GB,WiFi,4G.",

price:229.00

},

{

id:1,

name:"AppleiPadMini232GB",

description:"Evenlargerthanthe16GB.",

price:279.00

},

{

id:2,

name:"CanonT7i",

description:"DSLRcamerawithlotsofmegapixels.",

price:749.99

},

{

id:3,

name:"AppleWatchSport",

description:"Awatch",

price:249.99

},

Page 129: Pure React: A step-by-step guide to mastering React

{

id:4,

name:"AppleWatchSilver",

description:"Amoreexpensivewatch",

price:599.99

}

];

export{items};

Then we will pass those items into the new ItemPage component. Back inApp.js,importtheItemPageandthestaticitemsdata.

importItemPagefrom'./ItemPage';

import{items}from'./static-data';

AndthenupdatetherenderContentfunctiontousethenewItemPage:

classAppextendsComponent{

//...

renderContent(){

switch(this.state.activeTab){

default:

case0:return<ItemPageitems={items}/>

case1:return<span>Cart</span>;

}

}

//...

}

Nowyoushouldseesomeitemsrendering!

Page 130: Pure React: A step-by-step guide to mastering React

CreateItem

Let’snowcreatetherealItemcomponenttouseinItemList.It’lltakeanitemprop, andwe’ll alsowant to be able to add it to the cart, so it should take anonAddToCartproptoo.

Thiscomponentwillbestateless.Itsresponsibilitiesaretodisplayanitem,andtoletitsparentknowwhenthe“AddtoCart”buttonisclicked.

LearningfromtheGitHubexample,wewon’treturnanlifromthiscomponent.Wedon’twanttoforceusersofItemtoputitinsidea<ul>.Theparentcanputthisitemintoanliifitchoosesto.

Withthatinmind,createsrc/Item.jsandsrc/Item.css.InItem.js,writeoutthecomponent:

importReactfrom'react';

importPropTypesfrom'prop-types';

import'./Item.css';

constItem=({item,onAddToCart})=>(

<divclassName="Item">

<divclassName="Item-left">

<divclassName="Item-image"/>

<divclassName="Item-title">

{item.name}

</div>

<divclassName="Item-description">

{item.description}

</div>

</div>

<divclassName="Item-right">

<divclassName="Item-price">

${item.price}

</div>

<button

className="Item-addToCart"

onClick={onAddToCart}

>

AddtoCart

Page 131: Pure React: A step-by-step guide to mastering React

</button>

</div>

</div>

);

Item.propTypes={

item:PropTypes.object.isRequired,

onAddToCart:PropTypes.func.isRequired

};

exportdefaultItem;

Now let’s wire it in to the ItemPage component. In ItemPage.js, update thecodetolooklikethis:

importReactfrom'react';

importPropTypesfrom'prop-types';

importItemfrom'./Item';

import'./ItemPage.css';

functionItemPage({items,onAddToCart}){

return(

<ulclassName="ItemPage-items">

{items.map(item=>

<likey={item.id}className="ItemPage-item">

<Item

item={item}

onAddToCart={()=>onAddToCart(item)}/>

</li>

)}

</ul>

);

}

ItemPage.propTypes={

items:PropTypes.array.isRequired,

onAddToCart:PropTypes.func.isRequired

};

exportdefaultItemPage;

We’re importingItem,and then inside the<li>we’renowrendering theItemcomponent insteadof just the item’sname.Alsonew is theonAddToCartpropanditsassociatedpropType.

Page 132: Pure React: A step-by-step guide to mastering React

Atthispoint,thecodewon’twork,andtherewillbeawarningaboutthemissingonAddToCartprop.

To complete the chain of passed-down props, we need to pass something toItemPage’sonAddToCartpropfrominsidetheAppcomponent.Whilewe’reatit,wemayaswell actuallyadd the items toacart!Thatwill requireanewcartpropertyinsidestate.

Let’swalk through it.First, inApp.js, addacartproperty to the initial state,andinitializeittoanemptyarray:

classAppextendsComponent{

state={

activeTab:0,

cart:[]

}

//...

}

ThenupdaterenderContenttopassafunctionastheonAddToCartprop:

classAppextendsComponent{

//...

renderContent(){

switch(this.state.activeTab){

default:

case0:

return(

<ItemPage

items={items}

onAddToCart={this.handleAddToCart}>

);

case1:

return<span>Cart<span>;

}

}

//...

}

Thefunctionthis.handleAddToCartdoesn’texistyet,socreatethatnext.Itwill

Page 133: Pure React: A step-by-step guide to mastering React

acceptanitem,andaddtheitemtothecart:

classAppextendsComponent{

//...

handleAddToCart=(item)=>{

this.setState({

cart:[...this.state.cart,item.id]

});

}

//...

}

Whatthisisdoingissettingthecartstatetoacopyofthecurrentcart,plusonenewitem.

There’ssomenewES6syntaxhere:...this.state.cartisthespreadoperator,anditexpandsthegivenarrayintoitsindividualitems.Here’sanexampleofthespreadoperator:

//Witharrays:

vara=[1,2,3];

varb=[a,4];//=>[[1,2,3],4]

varc=[...a,4];//=>[1,2,3,4]

//Withobjects(technicallynotES6)

varo1={a:1,b:2};

varo2={...o1,c:3};//=>{a:1,b:2,c:3}

NotethatthespreadoperatorforarraysisofficiallypartofES6,buttheoneforobjects is not. However, object spread is supported by Babel, which CreateReact App is using under the hood to turn our code into browser-compatibleES5.

UpdatingStateImmutably

You might be wondering… why not just modify state directly and then callsetState,likethis?

this.state.cart.push(item.id);

Page 134: Pure React: A step-by-step guide to mastering React

this.setState({cart:this.state.cart});

Thisisabadideafortworeasons(eventhoughitwouldworkinthisexample).

Youshouldnevermodify(“mutate”)stateoritschildpropertiesdirectly.Don’tdothis.state.something=x,anddon’tdothis.state=x.Reactreliesonyoutocallthis.setStatewhenyouwanttomakeachange,sothatitwillknowsomethingchangedandtriggerare-render.IfyoucircumventsetState theUIwillgetoutofsyncwiththedata.

Mutating state directly can lead to odd bugs, and components that are hard tooptimize.

Here’s an example. Recall that one of the commonways to optimize aReactcomponentistomakeit“pure,”meaningthatitonlyre-renderswhenitspropschange. This can be done automatically by extending React.PureComponentinstead of React.Component, or manually by implementing theshouldComponentUpdate lifecyclemethod to compare nextProps with currentprops.Ifthepropslookthesame,itskipstherender,andsavessometime.

Here is a simple component that renders a list of items.Notice that it extendsReact.PureComponent:

(Ifyouhavetheexamplecode,thiscodeisunderthe“impure-state”folder.)

classItemListextendsReact.PureComponent{

staticpropTypes={

items:PropTypes.array.isRequired

}

render(){

return(

<ul>

{this.props.items.map(item=>

<likey={item.id}>{item.value}</li>

)}

</ul>

);

}

}

Page 135: Pure React: A step-by-step guide to mastering React

Now,hereisatinyappthatrenderstheItemListandallowsyoutomutablyorimmutablyadditemstothelist:

classAppextendsComponent{

//Initializeitemstoanemptyarray

state={

items:[]

}

//Initializeacounterthatwillincrement

//foreachitemID

nextItemId=0;

makeItem(){

//CreateanewIDanduse

//arandomnumberasthevalue

return{

id:this.nextItemId++,

value:Math.random()

};

}

//TheRightWay:

//copytheexistingitemsandaddanewone

addItemImmutably=()=>{

this.setState({

items:[

...this.state.items,

this.makeItem()

]

});

}

//TheWrongWay:

//mutateitemsandsetitback

addItemMutably=()=>{

this.state.items.push(this.makeItem());

this.setState({items:this.state.items});

}

render(){

return(

<div>

<buttononClick={this.addItemImmutably}>

Additemimmutably(good)

</button>

Page 136: Pure React: A step-by-step guide to mastering React

<buttononClick={this.addItemMutably}>

Additemmutably(bad)

</button>

<ItemListitems={this.state.items}/>

</div>

);

}

}

Tryitout.ClicktheimmutableAddbuttonafewtimesandnoticehowthelistupdatesasexpected.ThenclickthemutableAddbuttonandnoticehowthenewitemsdon’tappear,eventhoughstateisbeingchanged.

ThishappensbecauseItemListispure,andbecausepushinganewitemonthethis.state.items array does not replace the underlying array.ItemListwillnoticethatitspropshaven’tchangedanditwillnotre-render.

Finally,clicktheimmutableAddbuttonagain,andwatchhowtheItemListre-renderswithallthemissing(mutably-added)items.

That is a long way of saying that you shouldn’t mutate state, even if youimmediately call setState. Optimized components (ones that implementshouldComponentUpdate)mightnotre-renderifyoudo.

Instead, you should create new objects and arrays when you call setState,whichiswhatwedidabovewiththespreadoperator.

BacktotheCode

Let’stryitnow:clicking“AddtoCart”shouldupdatethecart.Butwehavenowaytoverifythecartisfillingup!Justfordebugging,addthisbitofcodeinsideApp.js’srendermethod:

<div>

{this.state.cart.length}items

</div>

Nowtry itagain.The itemcount should increaseeach timeyouclick“Add toCart.”Onceyouverifythatit’sworking,youcanremovethatdebuggingcode.

Page 137: Pure React: A step-by-step guide to mastering React

Let’s add some CSS to make it look a little better. In Item.css (which youcreatedearlier,butiscurrentlyempty),addthisCSS:

.Item{

display:flex;

justify-content:space-between;

border-bottom:1pxsolid#ccc;

padding:10px;

}

.Item-image{

width:64px;

height:64px;

border:1pxsolid#ccc;

margin-right:10px;

float:left;

}

.Item-title{

font-weight:bold;

margin-bottom:0.4em;

}

.Item-price{

text-align:right;

font-size:18px;

color:#555;

}

.Item-addToCart{

margin-bottom:5px;

font-size:14px;

border:2pxsolid#FFAA3F;

background-color:#fff;

border-radius:3px;

cursor:pointer;

}

.Item-addToCart:hover{

background-color:#FFDDB2;

}

Page 138: Pure React: A step-by-step guide to mastering React

.Item-addToCart:active{

background-color:#ED8400;

color:#fff;

outline:none;

}

.Item-addToCart:focus{

outline:none;

}

.Item-left{

flex:1;

}

.Item-right{

display:flex;

flex-direction:column;

justify-content:space-between;

}

That’sabitbetter:

CreateCartPage

Nowthatwecanadd items to thecart,we’llbuild thecomponent that rendersthecartitself.

Create src/CartPage.css and src/CartPage.js, and open up CartPage.js.Initially,thecodewillbeverysimilartothecodefromItemPage.

importReactfrom'react';

importPropTypesfrom'prop-types';

Page 139: Pure React: A step-by-step guide to mastering React

importItemfrom'./Item';

import'./CartPage.css';

functionCartPage({items}){

return(

<ulclassName="CartPage-items">

{items.map(item=>

<likey={item.id}className="CartPage-item">

<Itemitem={item}/>

</li>

)}

</ul>

);

}

CartPage.propTypes={

items:PropTypes.array.isRequired

};

exportdefaultCartPage;

Thissimplyrenderstheitemsthatit’sgiven.You’llnoticethatwearen’tpassinganonAddToCarthandlertoItem,whichwillcauseapropwarning.We’llfixthatshortly.

PassItemstoCartPage

Thecartneedsitemstodisplay.Thetroubleis,ourcartstatejustcontainsitemindices, not actual items.Additionally, there canbeduplicate indiceswhen anitemisaddedmultipletimes.We’llneedtomassagethecartstateintoanarrayofitemssuitabletopassintoCartPage.

InApp.js,importCartPageatthetop:

importCartPagefrom'./CartPage';

ThenaddarenderCartfunction,andupdaterenderContenttocallit.

classAppextendsComponent{

//...

renderContent(){

Page 140: Pure React: A step-by-step guide to mastering React

switch(this.state.activeTab){

default:

case0:

return(

<ItemPage

items={items}

onAddToCart={this.handleAddToCart}/>

);

case1:

returnthis.renderCart();

}

}

renderCart(){

//Counthowmanyofeachitemisinthecart

letitemCounts=this.state.cart.reduce((itemCounts,itemId)=>{

itemCounts[itemId]=itemCounts[itemId]||0;

itemCounts[itemId]++;

returnitemCounts;

},{});

//Createanarrayofitems

letcartItems=Object.keys(itemCounts).map(itemId=>{

//Findtheitembyitsid

varitem=items.find(item=>

item.id===parseInt(itemId,10)

);

//Createanew"item"thatalsohasa'count'property

return{

...item,

count:itemCounts[itemId]

}

});

return(

<CartPageitems={cartItems}/>

);

}

//...

}

Thereareacouplethingsinthatcodeyoumaynothaveseenbefore.

Array.prototype.reduce

Page 141: Pure React: A step-by-step guide to mastering React

Array.prototype.reduce

The reduce function works like a summation operation. It takes an optionalinitial value ({} here), and then calls the given functionwith the accumulatedtotalandthecurrentarrayitem.Thevaluereturnedfromthefunctionbecomesthenewtotal,and itmoveson to thenext itemin thearray.Whenreduce isdone,itreturnsthefinaltotal.We’reusingreducetobuildupanobjectwherethekeysareitemIdsandthevaluesarethenumberofeachiteminthecart.

Here’sasmaller,simplerexampleofreduceinaction:

leta=[1,2,3,4];

lettotal=a.reduce((sum,value)=>{

returnsum+value;

},0);

//Thearrowfunctioniscalled4times:

//(0,1)=>returns1

//(1,2)=>returns3

//(3,3)=>returns6

//(6,4)=>returns10

//Thenreducereturnsthelastreturnvalue(10)

Thereducefunctionisafunctional-styleshorthandforcodelikethis:

consta=[1,2,3,4];

lettotal=0;

for(leti=0;i<a.length;i++){

total+=a[i];

}

Anytimeyouwanttoiterateoverthevaluesofanarraytocreateanaggregateresult,considerusingreduce.

Object.keys

Object.keys returns an array of the keys in an object. For instanceObject.keys({a:1,b:2})would return['a','b'].Objectsdon’t have a

Page 142: Pure React: A step-by-step guide to mastering React

built-initeratorforEachfunctionlikearraysdo,soObject.keysmakesiteasiertoiterateoverthekeysofanobject.

ModifyingtheCart

Atthispoint,the“Cart”tabshouldbeworking.Click“AddtoCart”afewitems,thenclickovertothe“Cart”tab,andtheitemsshouldbethere.Westillhavethe“Add to Cart” buttons, and we’re getting propType warnings aboutonAddToCart.We’llfixbothofthosenext.

Eventhoughthecartisworking,it’smissingimportantfeatures.Thereisnowaytoaddor remove items,no totalprice, andwedon’tevenknowhowmanyofeachitemisinthecart.

Remember howwe reused theItem component insideCartPage? Itwould beniceifwecouldrenderacustomizedItemthathadAddandRemovebuttonsinplaceofthe“AddtoCart”button.

Thereareafewwaystogoaboutthis.

One idea:wecouldmakeacopyof theItem component, rename itCartItem,andcustomizeitasnecessary.Thisisquickandeasy,butduplicatescode.

Anotheridea:wecouldmakeItemtakeapropthattellsittobea“regularitem”ora“cartitem.”Usingthatprop,Itemwoulddecidewhethertorenderan“AddtoCart” button or “Add/Remove” buttons.Thiswouldwork, but it’smessy –Itemwouldhavemultipleresponsibilities.IfitbecamenecessarytoreuseIteminathirdsituation,wemightendupaddingmoreconditionallogic,makingItem

Page 143: Pure React: A step-by-step guide to mastering React

harderandhardertounderstandandmaintain.

AthirdideaistopasschildrentoItemandletitdecidewheretoputthem.Thisistheideawe’llimplement.ItmakesItemfairlyreusable.Ifyouwantan“Addto Cart” button, pass that in. If you want Add/Remove buttons instead, passthosein.Let’sseehowthisworks.

RefactoringItem

ThenewItemanditspropTypeslookslikethis:

constItem=({item,children})=>(

<divclassName="Item">

<divclassName="Item-left">

<divclassName="Item-image"/>

<divclassName="Item-title">{item.name}</div>

<divclassName="Item-description">{item.description}</div>

</div>

<divclassName="Item-right">

<divclassName="Item-price">${item.price}</div>

{children}

</div>

</div>

);

Item.propTypes={

item:PropTypes.object.isRequired,

children:PropTypes.node

};

Whereoncetherewasa<button>,wenowhave{children}instead.Also,ItemnolongerneedstoknowabouttheonAddToCartfunction,sowe’veremoveditspropType.

Ifyou refreshat thispoint,you’ll see that the“Add toCart”buttonsaregonefrom theCartpageand the Itemspage.To fix that,openupItemPage.js andpassa<button>asachildoftheItem:

functionItemPage({items,onAddToCart}){

return(

<ulclassName="ItemPage-items">

{items.map(item=>

Page 144: Pure React: A step-by-step guide to mastering React

<likey={item.id}className="ItemPage-item">

<Itemitem={item}>

<button

className="Item-addToCart"

onClick={()=>onAddToCart(item)}>

AddtoCart

</button>

</Item>

</li>

)}

</ul>

);

}

Therewe go, back to normal. Now for the new part: open CartPage.js, andinsideItem,passintheplus/minusbuttonsandtheitemcount.We’realsogoingtoneedhandlerfunctionsforwhenanitemisaddedorremoved.

functionCartPage({items,onAddOne,onRemoveOne}){

return(

<ulclassName="CartPage-items">

{items.map(item=>

<likey={item.id}className="CartPage-item">

<Itemitem={item}>

<divclassName="CartItem-controls">

<button

className="CartItem-removeOne"

onClick={()=>onRemoveOne(item)}>&ndash;</button>

<spanclassName="CartItem-count">{item.count}</span>

<button

className="CartItem-addOne"

onClick={()=>onAddOne(item)}>+</button>

</div>

</Item>

</li>

)}

</ul>

);

}

CartPage.propTypes={

items:PropTypes.array.isRequired,

onAddOne:PropTypes.func.isRequired,

onRemoveOne:PropTypes.func.isRequired

};

Page 145: Pure React: A step-by-step guide to mastering React

Thisisallstuffyou’veseenbefore.

Thecodewon’tworkuntilwepassthosefunctions,soheadlet’sbacktoApp.jsandcreatethose.AtthebottomofrenderCart,weneedtopasstheextrapropstoCartPage:

classAppextendsComponent{

//...

renderCart(){

//...

return(

<CartPage

items={cartItems}

onAddOne={this.handleAddToCart}

onRemoveOne={this.handleRemoveOne}/>

);

}

//...

}

WealreadyhavethehandleAddToCartfunctionthattakesanitemandaddsitsidtothecart,sowecanjustreusethathere.

Weneed tocreate thehandleRemoveOne function. Itwill takean item, findanoccurrenceofthatiteminthecart,andthenupdatethecartstatetoremovethatoccurrence.

classAppextendsComponent{

//...

handleRemoveOne=(item)=>{

letindex=this.state.cart.indexOf(item.id);

this.setState({

cart:[

...this.state.cart.slice(0,index),

...this.state.cart.slice(index+1)

]

});

}

//...

}

Page 146: Pure React: A step-by-step guide to mastering React

We’reusingthearrayspreadoperatortwicehere,inordertoavoidmutatingthestate.We’retakingthelefthalfofthearray(uptotheitemwewanttoremove)and concatenating itwith the right half of the array (everything after the itembeingremoved).

This is a littlemorework than ifwewere to allowmutationsbut it is a goodhabit to get into. It’ll be especially important if you start using Redux in thefuture.Reduxreliesheavilyonimmutability.

Towrapuptheadd/removebuttons,let’sgivethemabitofstyle.AddthisCSStoCartPage.css:

.CartPage-items{

margin:0;

padding:0;

}

.CartPage-itemsli{

list-style:none;

}

.CartItem-count{

padding:5px10px;

border:1pxsolid#ccc;

}

.CartItem-addOne,

.CartItem-removeOne{

padding:5px10px;

border:1pxsolid#ccc;

background:#fff;

}

.CartItem-addOne{

border-left:none;

}

.CartItem-removeOne{

border-right:none;

}

Hereitisinallitsglory:

Page 147: Pure React: A step-by-step guide to mastering React

Exercises

1. Itwouldbeniceiftheshoppingcarttoldushowmanythousandsofdollarswewere about to spend on Apple products, rather than letting us guess.Adda“Total”tothebottomoftheCartpagesimilartothis:

2. The Cart page is completely blank when the cart is empty.Modify it todisplay“Yourcartisempty”whentherearenoitemsinthecart,somethinglikethis:

Page 148: Pure React: A step-by-step guide to mastering React

3. Displayasummaryoftheshoppingcartinthetop-rightofthenavbar,asshown below. Include the total number of items in the cart and the totalprice.Make sure to account for the fact that each item in the cart arraycould have a count greater than 1. As a bonus, make the cart summaryclickable, and switch to theCartpageonclick.Adda shoppingcart icontooifyoulike(thisoneisfromFontAwesome).

4. BuildasimplifiedversionofReddit,modeledafterthisscreenshot:

Herearethefeaturesitshouldhave:

Page 149: Pure React: A step-by-step guide to mastering React

DisplaythenumberofvotesFunctioningUpvoteandDownvotecontrolsDisplaythenumberofcommentsSortthestoriesbynumberofupvotesThetitleshouldbeareallinkOtherlinksdonotneedtobefunctional(share,save,hide,etc.)

Follow the same process we’ve done throughout this book: outline thecomponentsinthescreenshot,decidewhichpiecesofstateyouneedtokeepandwhichcomponentsshouldownthatstate,thenstartbuildingcomponents.

Reddit’sAPIispublicandyoudon’tneedakey.Youcanusestaticdatafrome.g. http://www.reddit.com/r/reactjs.json – save this to a file and import it aswe’vedoneinafewoftheexamples.

5. BuildasimplifiedversionofSlack,modeledafterthisUI:

Page 150: Pure React: A step-by-step guide to mastering React

Herearethefeaturesitshouldhave:

AlistofChannelsAlistofUsersMaintainstateforthecurrently-selectedChannelorUserClickaChannelorUsertoselectitOnlyoneChannelorUsercanbeselectedatatimeEachChannelandUserhasitsownhistoryofmessagesGeneratesomefakemessagesforeachchannelShowaninputboxatthebottom,onlywhenaChannelorUserisselectedTypingintheinputboxandhittingENTERshouldaddthatmessagetotheselectedChannel/Userandcleartheinput.

Page 151: Pure React: A step-by-step guide to mastering React

WhereToGoFromHereFirstofall,congratulationsformakingittotheendofthebook.IhopethatyouhadasmuchfunreadingitasIdidwritingit.

Now,withthatsaid,thisisnottheend.ThisisonlythebeginningofyourReactjourney.

AtthispointyouhaveasolidgraspofReact’sfundamentals,andyou’velearnedsomeES6too.Fromhere,youcangoontolearnaboutAJAX,Webpack,ReactRouter,ReduxorMobX,andwhateverelseyouneed.

Here’s a suggested order in which to tackle them. I think it’s important thatReduxbelearnedlast,buttheotherscanbereorderedasyouseefit.

AJAX

Learninghowtoconnectyourapptoaserverisfun,becauseyougettoseeyourappcomealivewithrealdata.

Reactdoesn’thaveanybuilt-inwaystotalktoservers,soyou’llneedtouseanHTTPlibrary.Thereareanumberofthemavailable.Ilikefetchbecauseitwilleventually be part of the JavaScript standard library, but axios and superagentarealsogoodchoices.

HowandwhereshouldyoumakeHTTPrequests?AcommonpracticeistofetchdatawithinthecomponentDidMountlifecyclemethod.Whentheresponsecomesback,callthis.setStatetosavethatdata,andusepropstopassitdowntothecomponentsthatneedit.

Aswithall state, it’sbest tokeep it ashighup in thecomponenthierarchyaspossible.Forinstance,ifaPagecontainsmultipleListcomponents,don’thaveeachListfetchitsowndata–fetchthatdatainthePagecomponent,andpassitdowntotheListsasprops.

Testing

Page 152: Pure React: A step-by-step guide to mastering React

Testing is an important part of building software.Whether you decide to usetest-drivendevelopment(TDD)ornot,havingtestsaroundyourcodegivesyouconfidencethateverythingworksasitshould.

ThereareahandfulofnewtoolstolearnfortestingReact.You’llneedawaytomakeassertions(Chai),probablywantalibraryforsettingupspies(Sinon),andwill definitely want to use Enzyme, a library for testing React componentscreatedbyAirbnb.

IputtogetheranarticletogetyoustartedwithtestingReacthere.

Webpack

Buildtoolsareamajorstumblingblockinthebeginning,whichiswhysettingupWebpackwasn’tpartofthisbook.Ifyoudon’tneedtocustomizeyourbuildyet,postponelearningWebpackuntillater.

I recommend reading the article Webpack – The Confusing Parts as anintroduction toWebpackand itswayof thinking.Egghead.iohas anicequickintroduction toWebpack as well, at https://egghead.io/lessons/javascript-intro-to-webpack.

You should know that the create-react-app tool we’ve been using has aproduction build mode (run npm run build) which will create a production-optimizedbuildwithminifiedfilesandaproductionbuildofReact.Ifyoudon’tneed to customize anything, Create React App is great out of the box, andperfectlysuitedtowriterealproductionReactapps.

It also has an “eject” command (run npm run eject) which extracts thegeneratedWebpackbuildandexposesitforyoutocustomize.

ES6

You’ve already seen some of this throughout the book: arrow functions,let/const, classes, destructuring, and import/export. This is most of the ES6you’lluseonaregularbasis,butthereareanumberofotherniceadditions,likepromises,for..ofloops,dynamicobjectkeys,iterators,andmore.

Youcanlearnthesefeaturesasyougo,butit’sagoodideatoatleastdoaquick

Page 153: Pure React: A step-by-step guide to mastering React

survey of everything so you know what’s available. This Overview of ES6Featuresisagreatresource.

Routing

Now that you have a fairly good handle on howReactworks,ReactRouter’sconceptswillbuilduponthem.Itmakesuseofcomponentstolayouttheroutingforyourapplication,effectivelymappingyourcomponentstoURLs.CheckoutReactRouteronGithubtogetstarted.

Some people conflate React Router and Redux in their head – they’re notactuallyrelatedordependentoneachother,althoughthereisalibrarythatlinkstherouter’sstatewithRedux.Youcan(andshould!)learntouseReactRouterbeforedivingintoRedux.

Redux

Dan Abramov, the creator of Redux, will tell you not to add Redux to yourprojecttooearly,andforgoodreason–youdon’tactuallyneedReduxuntilyouencounter the problems it solves, and it’s a dose of complexity that can bedisastrousearlyon.

The concepts behind Redux are fairly simple, but there is a gap betweenunderstandinghowthepiecesworkandknowinghowtouseReduxinarealapp.It will take some practice to truly understand Redux. It’s also influenced byfunctionalprogrammingconcepts,whichcantakesomegettingusedtoifyou’renotfamiliarwiththem.

So,beforeyoucommittoaddingReduxtoalargerproject,repeatwhatyoudidthroughoutthisbook:buildsmallprojects.TakecodethatusesplainReactstateandconvertittouseRedux.BuildabunchoflittleReduxexperimentstoreallyinternalizehowitworks.

OnBoilerplateProjects

I recommend building your project brick-by-brick, adding pieces as you needthem.Thatwayyouwill understandhowall of themovingpartswork,whichmeansyoucanfixthemwhentheybreakorextendthemwhenyououtgrowthedefaultsettings.

Page 154: Pure React: A step-by-step guide to mastering React

It’seasy to fall into the trapof thinking that learning“PureReact” isonly forbeginners, and thatyou’ll basicallybe required touse aboilerplateproject foranysubstantialproject.Idon’tthinkanythingcouldbefurtherfromthetruth.

PartofthepowerofReactisbeingabletobuildyourownstackthewayyoulikeit.Even if youabdicate control anduse aboilerplateproject, you still have tolearn the conventions of that particular project. I suggest steering clear ofboilerplateprojectsmostofthetime.

Oneofthealluresofaboilerplateisthatitprescribesaprojectstructureforyou.If thestructureoffilesondisk iskeepingyoufrommakingprogress, readthisarticle:https://daveceddia.com/react-project-structure/

ThankYou

Thankssomuchfornotonlypurchasing,butfinishingthisbook.Iwouldlovetoknow your thoughts – what was good, what needs improvement, or anysuggestionsforfuturetopics.Feelfreetoreachmeatdave@daveceddia.comorcontactmeonTwitter@dceddia.