Table of Contents · npm run build : to build the React application files in the build folder,...
Transcript of Table of Contents · npm run build : to build the React application files in the build folder,...
TableofContentsIntroduction
Overview
IntroductiontoReact
HowtoinstallReact
ModernJavaScriptcoreconceptsyouneedtoknowtouseReact
HowmuchJSyouneedtouseReact
Variables
Arrowfunctions
WorkwithobjectsandarraysusingRestandSpread
Objectandarraydestructuring
Templateliterals
Classes
Callbacks
Promises
Async/Await
ESModules
ReactConcepts
SinglePageApps
Declarative
Immutability
Purity
Composition
TheVirtualDOM
UnidirectionalDataFlow
In-depth
JSX
Components
State
Props
Presentationalvscontainercomponents
2
StatevsProps
PropTypes
Fragment
Events
Lifecycleevents
Handlingforms
ReferenceaDOMelement
Serversiderendering
ContextAPI
Higher-ordercomponents
RenderProps
Hooks
Codesplitting
Styling
CSSinReact
SASSwithReact
StyledComponents
Tooling
Babel
Webpack
Prettier
Testing
IntroductiontoJest
TestingReactComponents
AlookattheReactEcosystem
ReactRouter
Redux
Next.js
Gatsby
Wrappingup
3
4
Introduction
TheReactHandbookfollowsthe80/20rule:learnin20%ofthetimethe80%ofatopic.
Ifindthisapproachgivesawell-roundedoverview.ThisbookdoesnottrytocovereverythingunderthesunrelatedtoReact.Ifyouthinksomespecifictopicshouldbeincluded,tellme.
InthisbookImakeuseofReacthooks,soyouneedtosettherequiredversionsofReactandReactDOMtouse 16.7.0-alpha.2(ifwhenyou'rereadingthisHooksarereleasedofficially,youdon'tneedtodothis).YoucandosoinCodeSandboxbyclicking"AddDependency"andsearching reactandchoosing 16.7.0-alpha.2fromtheselect,andrepeatthisfor react-dom.Whenusing create-react-app,run [email protected]@16.7.0-alpha.2afteryoucreatetheproject.
Ihopethecontentsofthisbookwillhelpyouachievewhatyouwant:learnthebasicsofReact.
ThisbookiswrittenbyFlavio.Ipublishwebdevelopmenttutorialseverydayonmywebsiteflaviocopes.com.
YoucanreachmeonTwitter@flaviocopes.
Introduction
5
Enjoy!
Introduction
6
IntroductiontoReactAnintroductiontotheReactviewlibrary
WhatisReact?ReactisaJavaScriptlibrarythataimstosimplifydevelopmentofvisualinterfaces.
DevelopedatFacebookandreleasedtotheworldin2013,itdrivessomeofthemostwidelyusedapps,poweringFacebookandInstagramamongcountlessotherapplications.
Itsprimarygoalistomakeiteasytoreasonaboutaninterfaceanditsstateatanypointintime,bydividingtheUIintoacollectionofcomponents.
WhyisReactsopopular?Reacthastakenthefrontendwebdevelopmentworldbystorm.Why?
Lesscomplexthantheotheralternatives
AtthetimewhenReactwasannounced,Ember.jsandAngular1.xwerethepredominantchoicesasaframework.Boththeseimposedsomanyconventionsonthecodethatportinganexistingappwasnotconvenientatall.Reactmadeachoicetobeveryeasytointegrateintoanexistingproject,becausethat'showtheyhadtodoitatFacebookinordertointroduceittotheexistingcodebase.Also,those2frameworksbroughttoomuchtothetable,whileReactonlychosetoimplementtheViewlayerinsteadofthefullMVCstack.
Perfecttiming
Atthetime,Angular2.xwasannouncedbyGoogle,alongwiththebackwardsincompatibilityandmajorchangesitwasgoingtobring.MovingfromAngular1to2waslikemovingtoadifferentframework,sothis,alongwithexecutionspeedimprovementsthatReactpromised,madeitsomethingdeveloperswereeagertotry.
BackedbyFacebook
BeingbackedbyFacebookobviouslyisgoingtobenefitaprojectifitturnsouttobesuccessful.
IntroductiontoReact
7
FacebookcurrentlyhasastronginterestinReact,seesthevalueofitbeingOpenSource,andthisisahugeplusforallthedevelopersusingitintheirownprojects.
IsReactsimpletolearn?EventhoughIsaidthatReactissimplerthanalternativeframeworks,divingintoReactisstillcomplicated,butmostlybecauseofthecorollarytechnologiesthatcanbeintegratedwithReact,likeReduxandGraphQL.
ReactinitselfhasaverysmallAPI,andyoubasicallyneedtounderstand4conceptstogetstarted:
ComponentsJSXStateProps
Allthese(andmore)areexplainedinthishandbook.
IntroductiontoReact
8
HowtoinstallReactHowtoinstallReactonyourdevelopmentcomputer
HowdoyouinstallReact?
Reactisalibrary,sosayinginstallmightsoundabitweird.Maybesetupisabetterword,butyougettheconcept.
TherearevariouswaystosetupReactsothatitcanbeusedonyourapporsite.
LoadReactdirectlyinthewebpageThesimplestoneistoaddtheReactJavaScriptfileintothepagedirectly.ThisisbestwhenyourReactappwillinteractwiththeelementspresentonasinglepage,andnotactuallycontrolsthewholenavigationaspect.
Inthiscase,youadd2scripttagstotheendofthe bodytag:
<html>
...
<body>
...
<script
src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.developme
nt.js"
crossorigin
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.p
roduction.min.js"
crossorigin
></script>
</body>
</html>
The 16.7.0-alpha.2versioninthelinkspointstothelatestAlphaof16.7(atthetimeofwriting),whichhasHooksavailable.PleasechangeittothelatestversionofReactthatisavailable.
HereweloadedbothReactandReactDOM.Why2libraries?BecauseReactis100%independentfromthebrowserandcanbeusedoutsideit(forexampleonMobiledeviceswithReactNative).HencetheneedforReactDOM,toaddthewrappersforthebrowser.
HowtoinstallReact
9
AfterthosetagsyoucanloadyourJavaScriptfilesthatuseReact,oreveninlineJavaScriptina scripttag:
<scriptsrc="app.js"></script>
<!--or-->
<script>
//myapp
</script>
TouseJSXyouneedanextrastep:loadBabel
<scriptsrc="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
andloadyourscriptswiththespecial text/babelMIMEtype:
<scriptsrc="app.js"type="text/babel"></script>
NowyoucanaddJSXinyourapp.jsfile:
constButton=()=>{
return<button>Clickme!</button>
}
ReactDOM.render(<Button/>,document.getElementById('root'))
CheckoutthissimpleGlitchexample:https://glitch.com/edit/#!/react-example-inline-jsx?path=script.js
Startinginthiswaywithscripttagsisgoodforbuildingprototypesandenablesaquickstartwithouthavingtosetupacomplexworkflow.
Usecreate-react-appcreate-react-appisaprojectaimedatgettingyouuptospeedwithReactinnotime,andanyReactappthatneedstooutgrowasinglepagewillfindthat create-react-appmeetsthatneed.
Youstartbyusing npx,whichisaneasywaytodownloadandexecuteNode.jscommandswithoutinstallingthem. npxcomeswith npm(sinceversion5.2)andifyoudon'thavenpminstalledalready,doitnowfromhttps://nodejs.org(npmisinstalledwithNode).
Ifyouareunsurewhichversionofnpmyouhave,run npm-vtocheckifyouneedtoupdate.
HowtoinstallReact
10
Tip:checkoutmyOSXterminaltutorialathttps://flaviocopes.com/macos-terminal/ifyou'reunfamiliarwithusingtheterminal,appliestoLinuxaswell-I'msorrybutIdon'thaveatutorialforWindowsatthemoment,butGoogleisyourfriend.
Whenyourun npxcreate-react-app<app-name>, npxisgoingtodownloadthemostrecentcreate-react-apprelease,runit,andthenremoveitfromyoursystem.Thisisgreatbecauseyouwillneverhaveanoutdatedversiononyoursystem,andeverytimeyourunit,you'regettingthelatestandgreatestcodeavailable.
Let'sstartthen:
npxcreate-react-apptodolist
Thisiswhenitfinishedrunning:
HowtoinstallReact
11
create-react-appcreatedafilesstructureinthefolderyoutold( todolistinthiscase),andinitializedaGitrepository.
Italsoaddedafewcommandsinthe package.jsonfile,soyoucanimmediatelystarttheappbygoingintothefolderandrun npmstart.
HowtoinstallReact
12
Inadditionto npmstart, create-react-appaddedafewothercommands:
HowtoinstallReact
13
npmrunbuild:tobuildtheReactapplicationfilesinthe buildfolder,readytobedeployedtoaservernpmtest:torunthetestingsuiteusingJestnpmeject:toejectfrom create-react-app
Ejectingistheactofdecidingthat create-react-apphasdoneenoughforyou,butyouwanttodomorethanwhatitallows.
Since create-react-appisasetofcommondenominatorconventionsandalimitedamountofoptions,it'sprobablethatatsomepointyourneedswilldemandsomethinguniquethatoutgrowsthecapabilitiesof create-react-app.
Whenyoueject,youlosetheabilityofautomaticupdatesbutyougainmoreflexibilityintheBabelandWebpackconfiguration.
Whenyouejecttheactionisirreversible.Youwillget2newfoldersinyourapplicationdirectory, configand scripts.Thosecontaintheconfigurations-andnowyoucanstarteditingthem.
IfyoualreadyhaveaReactappinstalledusinganolderversionofReact,firstchecktheversionbyadding console.log(React.version)inyourapp,thenyoucanupdatebyrunning [email protected],andyarnwillpromptyoutoupdate(choosethelatestversionavailable).Repeatfor [email protected](change"16.7"withwhateveristhenewestversionofReactatthemoment)
CodeSandboxAneasywaytohavethe create-react-appstructure,withoutinstallingit,istogotohttps://codesandbox.io/sandchoose"React".
HowtoinstallReact
14
CodeSandboxisagreatwaytostartaReactprojectwithouthavingtoinstallitlocally.
CodepenAnothergreatsolutionisCodepen.
YoucanusethisCodepenstarterprojectwhichalreadycomespre-configuredwithReact,withsupportforHooks:https://codepen.io/flaviocopes/pen/VqeaxB
Codepen"pens"aregreatforquickprojectswithoneJavaScriptfile,while"projects"aregreatforprojectswithmultiplefiles,liketheoneswe'llusethemostwhenbuildingReactapps.
OnethingtonoteisthatinCodepen,duetohowitworksinternally,youdon'tusetheregularESModules importsyntax,butrathertoimportforexample useState,youuse
HowtoinstallReact
15
const{useState}=React
andnot
import{useState}from'react'
HowtoinstallReact
16
HowmuchJSyouneedtouseReactFindoutifyouhavetolearnsomethingbeforedivingintolearningReact
IfyouarewillingtolearnReact,youfirstneedtohaveafewthingsunderyourbelt.Therearesomeprerequisitetechnologiesyouhavetobefamiliarwith,inparticularrelatedtosomeofthemorerecentJavaScriptfeaturesyou'lluseoverandoverinReact.
SometimespeoplethinkoneparticularfeatureisprovidedbyReact,butinsteadit'sjustmodernJavaScriptsyntax.
Thereisnopointinbeinganexpertinthosetopicsrightaway,butthemoreyoudiveintoReact,themoreyou'llneedtomasterthose.
Iwillmentionalistofthingstogetyouuptospeedquickly.
HowmuchJSyouneedtouseReact
17
VariablesAvariableisaliteralassignedtoanidentifier,soyoucanreferenceanduseitlaterintheprogram.LearnhowtodeclareonewithJavaScript
Avariableisaliteralassignedtoanidentifier,soyoucanreferenceanduseitlaterintheprogram.
VariablesinJavaScriptdonothaveanytypeattached.Onceyouassignaspecificliteraltypetoavariable,youcanlaterreassignthevariabletohostanyothertype,withouttypeerrorsoranyissue.
ThisiswhyJavaScriptissometimesreferredtoas"untyped".
Avariablemustbedeclaredbeforeyoucanuseit.Thereare3waystodothis,using var,letor const,andthose3waysdifferinhowyoucaninteractwiththevariablelateron.
Using varUntilES2015, varwastheonlyconstructavailablefordefiningvariables.
vara=0
Ifyouforgettoadd varyouwillbeassigningavaluetoanundeclaredvariable,andtheresultsmightvary.
Inmodernenvironments,withstrictmodeenabled,youwillgetanerror.Inolderenvironments(orwithstrictmodedisabled)thiswillsimplyinitializethevariableandassignittotheglobalobject.
Ifyoudon'tinitializethevariablewhenyoudeclareit,itwillhavethe undefinedvalueuntilyouassignavaluetoit.
vara//typeofa==='undefined'
Youcanredeclarethevariablemanytimes,overridingit:
vara=1
vara=2
Youcanalsodeclaremultiplevariablesatonceinthesamestatement:
Variables
18
vara=1,b=2
Thescopeistheportionofcodewherethevariableisvisible.
Avariableinitializedwith varoutsideofanyfunctionisassignedtotheglobalobject,hasaglobalscopeandisvisibleeverywhere.Avariableinitializedwith varinsideafunctionisassignedtothatfunction,it'slocalandisvisibleonlyinsideit,justlikeafunctionparameter.
Anyvariabledefinedinafunctionwiththesamenameasaglobalvariabletakesprecedenceovertheglobalvariable,shadowingit.
It'simportanttounderstandthatablock(identifiedbyapairofcurlybraces)doesnotdefineanewscope.Anewscopeisonlycreatedwhenafunctioniscreated,because vardoesnothaveblockscope,butfunctionscope.
Insideafunction,anyvariabledefinedinitisvisiblethroughoutallthefunctioncode,evenifthevariableisdeclaredattheendofthefunctionitcanstillbereferencedinthebeginning,becauseJavaScriptbeforeexecutingthecodeactuallymovesallvariablesontop(somethingthatiscalledhoisting).Toavoidconfusion,alwaysdeclarevariablesatthebeginningofafunction.
Using letletisanewfeatureintroducedinES2015andit'sessentiallyablockscopedversionofvar.Itsscopeislimitedtotheblock,statementorexpressionwhereit'sdefined,andallthecontainedinnerblocks.
ModernJavaScriptdevelopersmightchoosetoonlyuse letandcompletelydiscardtheuseof var.
If letseemsanobscureterm,justread letcolor='red'asletthecolorberedanditallmakesmuchmoresense
Defining letoutsideofanyfunction-contraryto var-doesnotcreateaglobalvariable.
Using constVariablesdeclaredwith varor letcanbechangedlateronintheprogram,andreassigned.Oncea constisinitialized,itsvaluecanneverbechangedagain,anditcan'tbereassignedtoadifferentvalue.
consta='test'
Variables
19
Wecan'tassignadifferentliteraltothe aconst.Wecanhowevermutate aifit'sanobjectthatprovidesmethodsthatmutateitscontents.
constdoesnotprovideimmutability,justmakessurethatthereferencecan'tbechanged.
consthasblockscope,sameas let.
ModernJavaScriptdevelopersmightchoosetoalwaysuse constforvariablesthatdon'tneedtobereassignedlaterintheprogram.
Why?Becauseweshouldalwaysusethesimplestconstructavailabletoavoidmakingerrorsdowntheroad.
Variables
20
ArrowfunctionsArrowFunctionsareoneofthemostimpactfulchangesinES6/ES2015,andtheyarewidelyusednowadays.Theyslightlydifferfromregularfunctions.Findouthow
ArrowfunctionswereintroducedinES6/ECMAScript2015,andsincetheirintroductiontheychangedforeverhowJavaScriptcodelooks(andworks).
Inmyopinionthischangewassowelcomingthatyounowrarelyseetheusageofthefunctionkeywordinmoderncodebases.
Visually,it’sasimpleandwelcomechange,whichallowsyoutowritefunctionswithashortersyntax,from:
constmyFunction=function(){
//...
}
to
constmyFunction=()=>{
//...
}
Ifthefunctionbodycontainsjustasinglestatement,youcanomitthebracketsandwriteallonasingleline:
constmyFunction=()=>doSomething()
Parametersarepassedintheparentheses:
constmyFunction=(param1,param2)=>doSomething(param1,param2)
Ifyouhaveone(andjustone)parameter,youcouldomittheparenthesescompletely:
constmyFunction=param=>doSomething(param)
Thankstothisshortsyntax,arrowfunctionsencouragetheuseofsmallfunctions.
Implicitreturn
Arrowfunctions
21
Arrowfunctionsallowyoutohaveanimplicitreturn:valuesarereturnedwithouthavingtousethe returnkeyword.
Itworkswhenthereisaone-linestatementinthefunctionbody:
constmyFunction=()=>'test'
myFunction()//'test'
Anotherexample,whenreturninganobject,remembertowrapthecurlybracketsinparenthesestoavoiditbeingconsideredthewrappingfunctionbodybrackets:
constmyFunction=()=>({value:'test'})
myFunction()//{value:'test'}
How thisworksinarrowfunctionsthisisaconceptthatcanbecomplicatedtograsp,asitvariesalotdependingonthecontextandalsovariesdependingonthemodeofJavaScript(strictmodeornot).
It'simportanttoclarifythisconceptbecausearrowfunctionsbehaveverydifferentlycomparedtoregularfunctions.
Whendefinedasamethodofanobject,inaregularfunction thisreferstotheobject,soyoucando:
constcar={
model:'Fiesta',
manufacturer:'Ford',
fullName:function(){
return`${this.manufacturer}${this.model}`
}
}
calling car.fullName()willreturn "FordFiesta".
The thisscopewitharrowfunctionsisinheritedfromtheexecutioncontext.Anarrowfunctiondoesnotbind thisatall,soitsvaluewillbelookedupinthecallstack,sointhiscode car.fullName()willnotwork,andwillreturnthestring "undefinedundefined":
constcar={
model:'Fiesta',
manufacturer:'Ford',
fullName:()=>{
Arrowfunctions
22
return`${this.manufacturer}${this.model}`
}
}
Duetothis,arrowfunctionsarenotsuitedasobjectmethods.
Arrowfunctionscannotbeusedasconstructorseither,wheninstantiatinganobjectwillraiseaTypeError.
Thisiswhereregularfunctionsshouldbeusedinstead,whendynamiccontextisnotneeded.
Thisisalsoaproblemwhenhandlingevents.DOMEventlistenersset thistobethetargetelement,andifyourelyon thisinaneventhandler,aregularfunctionisnecessary:
constlink=document.querySelector('#link')
link.addEventListener('click',()=>{
//this===window
})
constlink=document.querySelector('#link')
link.addEventListener('click',function(){
//this===link
})
Arrowfunctions
23
WorkwithobjectsandarraysusingRestandSpreadLearntwomoderntechniquestoworkwitharraysandobjectsinJavaScript
Youcanexpandanarray,anobjectorastringusingthespreadoperator ....
Let'sstartwithanarrayexample.Given
consta=[1,2,3]
youcancreateanewarrayusing
constb=[...a,4,5,6]
Youcanalsocreateacopyofanarrayusing
constc=[...a]
Thisworksforobjectsaswell.Cloneanobjectwith:
constnewObj={...oldObj}
Usingstrings,thespreadoperatorcreatesanarraywitheachcharinthestring:
consthey='hey'
constarrayized=[...hey]//['h','e','y']
Thisoperatorhassomeprettyusefulapplications.Themostimportantoneistheabilitytouseanarrayasfunctionargumentinaverysimpleway:
constf=(foo,bar)=>{}
consta=[1,2]
f(...a)
(inthepastyoucoulddothisusing f.apply(null,a)butthat'snotasniceandreadable)
Therestelementisusefulwhenworkingwitharraydestructuring:
constnumbers=[1,2,3,4,5]
[first,second,...others]=numbers
WorkwithobjectsandarraysusingRestandSpread
24
andspreadelements:
constnumbers=[1,2,3,4,5]
constsum=(a,b,c,d,e)=>a+b+c+d+e
constsum=sum(...numbers)
ES2018introducesrestproperties,whicharethesamebutforobjects.
Restproperties:
const{first,second,...others}={
first:1,
second:2,
third:3,
fourth:4,
fifth:5
}
first//1
second//2
others//{third:3,fourth:4,fifth:5}
Spreadpropertiesallowtocreateanewobjectbycombiningthepropertiesoftheobjectpassedafterthespreadoperator:
constitems={first,second,...others}
items//{first:1,second:2,third:3,fourth:4,fifth:5}
WorkwithobjectsandarraysusingRestandSpread
25
ObjectandarraydestructuringLearnhowtousethedestructuringsyntaxtoworkwitharraysandobjectsinJavaScript
Givenanobject,usingthedestructuringsyntaxyoucanextractjustsomevaluesandputthemintonamedvariables:
constperson={
firstName:'Tom',
lastName:'Cruise',
actor:true,
age:54//madeup
}
const{firstName:name,age}=person//name:Tom,age:54
nameand agecontainthedesiredvalues.
Thesyntaxalsoworksonarrays:
consta=[1,2,3,4,5]
const[first,second]=a
Thisstatementcreates3newvariablesbygettingtheitemswithindex0,1,4fromthearraya:
const[first,second,,,fifth]=a
Objectandarraydestructuring
26
TemplateliteralsIntroducedinES2015,akaES6,TemplateLiteralsofferanewwaytodeclarestrings,butalsosomenewinterestingconstructswhicharealreadywidelypopular.
IntroductiontoTemplateLiteralsTemplateLiteralsareanewES2015/ES6featurethatallowsyoutoworkwithstringsinanovelwaycomparedtoES5andbelow.
Thesyntaxatafirstglanceisverysimple,justusebackticksinsteadofsingleordoublequotes:
consta_string=`something`
Theyareuniquebecausetheyprovidealotoffeaturesthatnormalstringsbuiltwithquotesdonot,inparticular:
theyofferagreatsyntaxtodefinemultilinestringstheyprovideaneasywaytointerpolatevariablesandexpressionsinstringstheyallowyoutocreateDSLswithtemplatetags(DSLmeansdomainspecificlanguage,andit'sforexampleusedinReactbyStyledComponents,todefineCSSforacomponent)
Let'sdiveintoeachoftheseindetail.
MultilinestringsPre-ES6,tocreateastringspanningovertwolinesyouhadtousethe \characterattheendofaline:
conststring=
'firstpart\
secondpart'
Thisallowstocreateastringon2lines,butit'srenderedonjustoneline:
firstpartsecondpart
Templateliterals
27
Torenderthestringonmultiplelinesaswell,youexplicitlyneedtoadd \nattheendofeachline,likethis:
conststring=
'firstline\n\
secondline'
or
conststring='firstline\n'+'secondline'
Templateliteralsmakemultilinestringsmuchsimpler.
Onceatemplateliteralisopenedwiththebacktick,youjustpressentertocreateanewline,withnospecialcharacters,andit'srenderedas-is:
conststring=`Hey
this
string
isawesome!`
Keepinmindthatspaceismeaningful,sodoingthis:
conststring=`First
Second`
isgoingtocreateastringlikethis:
First
Second
aneasywaytofixthisproblemisbyhavinganemptyfirstline,andappendingthetrim()methodrightaftertheclosingbacktick,whichwilleliminateanyspacebeforethefirstcharacter:
conststring=`
First
Second`.trim()
Interpolation
Templateliterals
28
Templateliteralsprovideaneasywaytointerpolatevariablesandexpressionsintostrings.
Youdosobyusingthe ${...}syntax:
constvar='test'
conststring=`something${var}`//somethingtest
insidethe ${}youcanaddanything,evenexpressions:
conststring=`something${1+2+3}`
conststring2=`something${foo()?'x':'y'}`
TemplatetagsTaggedtemplatesisonefeaturethatmightsoundlessusefulatfirstforyou,butit'sactuallyusedbylotsofpopularlibrariesaround,likeStyledComponentsorApollo,theGraphQLclient/serverlib,soit'sessentialtounderstandhowitworks.
InStyledComponentstemplatetagsareusedtodefineCSSstrings:
constButton=styled.button`
font-size:1.5em;
background-color:black;
color:white;
`
InApollotemplatetagsareusedtodefineaGraphQLqueryschema:
constquery=gql`
query{
...
}
`
The styled.buttonand gqltemplatetagshighlightedinthoseexamplesarejustfunctions:
functiongql(literals,...expressions){}
thisfunctionreturnsastring,whichcanbetheresultofanykindofcomputation.
literalsisanarraycontainingthetemplateliteralcontenttokenizedbytheexpressionsinterpolations.
expressionscontainsalltheinterpolations.
Templateliterals
29
Ifwetakeanexampleabove:
conststring=`something${1+2+3}`
literalsisanarraywithtwoitems.Thefirstis something,thestringuntilthefirstinterpolation,andthesecondisanemptystring,thespacebetweentheendofthefirstinterpolation(weonlyhaveone)andtheendofthestring.
expressionsinthiscaseisanarraywithasingleitem, 6.
Amorecomplexexampleis:
conststring=`something
another${'x'}
newline${1+2+3}
test`
inthiscase literalsisanarraywherethefirstitemis:
;`something
another`
thesecondis:
;`
newline`
andthethirdis:
;`
test`
expressionsinthiscaseisanarraywithtwoitems, xand 6.
Thefunctionthatispassedthosevaluescandoanythingwiththem,andthisisthepowerofthiskindfeature.
Themostsimpleexampleisreplicatingwhatthestringinterpolationdoes,bysimplyjoiningliteralsand expressions:
constinterpolated=interpolate`Ipaid${10}€`
andthisishow interpolateworks:
Templateliterals
30
functioninterpolate(literals,...expressions){
letstring=``
for(const[i,val]ofexpressions){
string+=literals[i]+val
}
string+=literals[literals.length-1]
returnstring
}
Templateliterals
31
ClassesIn2015theECMAScript6(ES6)standardintroducedclasses.Learnallaboutthem
In2015theECMAScript6(ES6)standardintroducedclasses.
JavaScripthasaquiteuncommonwaytoimplementinheritance:prototypicalinheritance.Prototypalinheritance,whileinmyopiniongreat,isunlikemostotherpopularprogramminglanguage'simplementationofinheritance,whichisclass-based.
PeoplecomingfromJavaorPythonorotherlanguageshadahardtimeunderstandingtheintricaciesofprototypalinheritance,sotheECMAScriptcommitteedecidedtosprinklesyntacticsugarontopofprototypicalinheritancesothatitresembleshowclass-basedinheritanceworksinotherpopularimplementations.
Thisisimportant:JavaScriptunderthehoodisstillthesame,andyoucanaccessanobjectprototypeintheusualway.
AclassdefinitionThisishowaclasslooks.
classPerson{
constructor(name){
this.name=name
}
hello(){
return'Hello,Iam'+this.name+'.'
}
}
Aclasshasanidentifier,whichwecanusetocreatenewobjectsusing newClassIdentifier().
Whentheobjectisinitialized,the constructormethodiscalled,withanyparameterspassed.
Aclassalsohasasmanymethodsasitneeds.Inthiscase helloisamethodandcanbecalledonallobjectsderivedfromthisclass:
constflavio=newPerson('Flavio')
flavio.hello()
Classes
32
ClassinheritanceAclasscanextendanotherclass,andobjectsinitializedusingthatclassinheritallthemethodsofbothclasses.
Iftheinheritedclasshasamethodwiththesamenameasoneoftheclasseshigherinthehierarchy,theclosestmethodtakesprecedence:
classProgrammerextendsPerson{
hello(){
returnsuper.hello()+'Iamaprogrammer.'
}
}
constflavio=newProgrammer('Flavio')
flavio.hello()
(theaboveprogramprints"Hello,IamFlavio.Iamaprogrammer.")
Classesdonothaveexplicitclassvariabledeclarations,butyoumustinitializeanyvariableintheconstructor.
Insideaclass,youcanreferencetheparentclasscalling super().
StaticmethodsNormallymethodsaredefinedontheinstance,notontheclass.
Staticmethodsareexecutedontheclassinstead:
classPerson{
staticgenericHello(){
return'Hello'
}
}
Person.genericHello()//Hello
PrivatemethodsJavaScriptdoesnothaveabuilt-inwaytodefineprivateorprotectedmethods.
Thereareworkarounds,butIwon'tdescribethemhere.
Classes
33
GettersandsettersYoucanaddmethodsprefixedwith getor settocreateagetterandsetter,whicharetwodifferentpiecesofcodethatareexecutedbasedonwhatyouaredoing:accessingthevariable,ormodifyingitsvalue.
classPerson{
constructor(name){
this.name=name
}
setname(value){
this.name=value
}
getname(){
returnthis.name
}
}
Ifyouonlyhaveagetter,thepropertycannotbeset,andanyattemptatdoingsowillbeignored:
classPerson{
constructor(name){
this.name=name
}
getname(){
returnthis.name
}
}
Ifyouonlyhaveasetter,youcanchangethevaluebutnotaccessitfromtheoutside:
classPerson{
constructor(name){
this.name=name
}
setname(value){
this.name=value
}
}
Classes
34
Classes
35
PromisesPromisesareonewaytodealwithasynchronouscodeinJavaScript,withoutwritingtoomanycallbacksinyourcode.
Apromiseiscommonlydefinedasaproxyforavaluethatwilleventuallybecomeavailable.
Promisesareonewaytodealwithasynchronouscode,withoutwritingtoomanycallbacksinyourcode.
Althoughthey'vebeenaroundforyears,theywerestandardizedandintroducedinES2015,andnowtheyhavebeensupersededinES2017byasyncfunctions.
AsyncfunctionsusethepromisesAPIastheirbuildingblock,sounderstandingthemisfundamentalevenifinnewercodeyou'lllikelyuseasyncfunctionsinsteadofpromises.
Howpromiseswork,inbrief
Onceapromisehasbeencalled,itwillstartinpendingstate.Thismeansthatthecallerfunctioncontinuestheexecution,whileitwaitsforthepromisetodoitsownprocessing,andgivethecallerfunctionsomefeedback.
Atthispoint,thecallerfunctionwaitsforittoeitherreturnthepromiseinaresolvedstate,orinarejectedstate,butasyouknowJavaScriptisasynchronous,sothefunctioncontinuesitsexecutionwhilethepromisedoesitwork.
WhichJSAPIusepromises?
Inadditiontoyourowncodeandlibrarycode,promisesareusedbystandardmodernWebAPIssuchas:
theBatteryAPItheFetchAPIServiceWorkers
It'sunlikelythatinmodernJavaScriptyou'llfindyourselfnotusingpromises,solet'sstartdivingrightintothem.
Creatingapromise
Promises
36
ThePromiseAPIexposesaPromiseconstructor,whichyouinitializeusing newPromise():
letdone=true
constisItDoneYet=newPromise((resolve,reject)=>{
if(done){
constworkDone='HereisthethingIbuilt'
resolve(workDone)
}else{
constwhy='Stillworkingonsomethingelse'
reject(why)
}
})
Asyoucanseethepromisechecksthe doneglobalconstant,andifthat'strue,wereturnaresolvedpromise,otherwisearejectedpromise.
Using resolveand rejectwecancommunicatebackavalue,intheabovecasewejustreturnastring,butitcouldbeanobjectaswell.
ConsumingapromiseInthelastsection,weintroducedhowapromiseiscreated.
Nowlet'sseehowthepromisecanbeconsumedorused.
constisItDoneYet=newPromise()
//...
constcheckIfItsDone=()=>{
isItDoneYet
.then(ok=>{
console.log(ok)
})
.catch(err=>{
console.error(err)
})
}
Running checkIfItsDone()willexecutethe isItDoneYet()promiseandwillwaitforittoresolve,usingthe thencallback,andifthereisanerror,itwillhandleitinthe catchcallback.
Promises
37
ChainingpromisesApromisecanbereturnedtoanotherpromise,creatingachainofpromises.
AgreatexampleofchainingpromisesisgivenbytheFetchAPI,alayerontopoftheXMLHttpRequestAPI,whichwecanusetogetaresourceandqueueachainofpromisestoexecutewhentheresourceisfetched.
TheFetchAPIisapromise-basedmechanism,andcalling fetch()isequivalenttodefiningourownpromiseusing newPromise().
Exampleofchainingpromises
conststatus=response=>{
if(response.status>=200&&response.status<300){
returnPromise.resolve(response)
}
returnPromise.reject(newError(response.statusText))
}
constjson=response=>response.json()
fetch('/todos.json')
.then(status)
.then(json)
.then(data=>{
console.log('RequestsucceededwithJSONresponse',data)
})
.catch(error=>{
console.log('Requestfailed',error)
})
Inthisexample,wecall fetch()togetalistofTODOitemsfromthe todos.jsonfilefoundinthedomainroot,andwecreateachainofpromises.
Running fetch()returnsaresponse,whichhasmanyproperties,andwithinthosewereference:
status,anumericvaluerepresentingtheHTTPstatuscodestatusText,astatusmessage,whichis OKiftherequestsucceeded
responsealsohasa json()method,whichreturnsapromisethatwillresolvewiththecontentofthebodyprocessedandtransformedintoJSON.
Sogiventhosepremises,thisiswhathappens:thefirstpromiseinthechainisafunctionthatwedefined,called status(),thatcheckstheresponsestatusandifit'snotasuccessresponse(between200and299),itrejectsthepromise.
Promises
38
Thisoperationwillcausethepromisechaintoskipallthechainedpromiseslistedandwillskipdirectlytothe catch()statementatthebottom,loggingthe Requestfailedtextalongwiththeerrormessage.
Ifthatsucceedsinstead,itcallsthejson()functionwedefined.Sincethepreviouspromise,whensuccessful,returnedthe responseobject,wegetitasaninputtothesecondpromise.
Inthiscase,wereturnthedataJSONprocessed,sothethirdpromisereceivestheJSONdirectly:
.then((data)=>{
console.log('RequestsucceededwithJSONresponse',data)
})
andwesimplylogittotheconsole.
HandlingerrorsIntheaboveexample,intheprevioussection,wehada catchthatwasappendedtothechainofpromises.
Whenanythinginthechainofpromisesfailsandraisesanerrororrejectsthepromise,thecontrolgoestothenearest catch()statementdownthechain.
newPromise((resolve,reject)=>{
thrownewError('Error')
}).catch(err=>{
console.error(err)
})
//or
newPromise((resolve,reject)=>{
reject('Error')
}).catch(err=>{
console.error(err)
})
Cascadingerrors
Ifinsidethe catch()youraiseanerror,youcanappendasecond catch()tohandleit,andsoon.
newPromise((resolve,reject)=>{
Promises
39
thrownewError('Error')
})
.catch(err=>{
thrownewError('Error')
})
.catch(err=>{
console.error(err)
})
Orchestratingpromises
Promise.all()
Ifyouneedtosynchronizedifferentpromises, Promise.all()helpsyoudefinealistofpromises,andexecutesomethingwhentheyareallresolved.
Example:
constf1=fetch('/something.json')
constf2=fetch('/something2.json')
Promise.all([f1,f2])
.then(res=>{
console.log('Arrayofresults',res)
})
.catch(err=>{
console.error(err)
})
TheES2015destructuringassignmentsyntaxallowsyoutoalsodo
Promise.all([f1,f2]).then(([res1,res2])=>{
console.log('Results',res1,res2)
})
Youarenotlimitedtousing fetchofcourse,anypromiseisgoodtogo.
Promise.race()
Promise.race()runsassoonasoneofthepromisesyoupasstoitresolves,anditrunstheattachedcallbackjustoncewiththeresultofthefirstpromiseresolved.
Example:
constpromiseOne=newPromise((resolve,reject)=>{
Promises
40
setTimeout(resolve,500,'one')
})
constpromiseTwo=newPromise((resolve,reject)=>{
setTimeout(resolve,100,'two')
})
Promise.race([promiseOne,promiseTwo]).then(result=>{
console.log(result)//'two'
})
Commonerrors
UncaughtTypeError:undefinedisnotapromise
Ifyougetthe UncaughtTypeError:undefinedisnotapromiseerrorintheconsole,makesureyouuse newPromise()insteadofjust Promise()
Promises
41
Async/AwaitDiscoverthemodernapproachtoasynchronousfunctionsinJavaScript.JavaScriptevolvedinaveryshorttimefromcallbackstoPromises,andsinceES2017asynchronousJavaScriptisevensimplerwiththeasync/awaitsyntax
IntroductionJavaScriptevolvedinaveryshorttimefromcallbackstopromises(ES2015),andsinceES2017asynchronousJavaScriptisevensimplerwiththeasync/awaitsyntax.
Asyncfunctionsareacombinationofpromisesandgenerators,andbasically,theyareahigherlevelabstractionoverpromises.Letmerepeat:async/awaitisbuiltonpromises.
Whywereasync/awaitintroduced?Theyreducetheboilerplatearoundpromises,andthe"don'tbreakthechain"limitationofchainingpromises.
WhenPromiseswereintroducedinES2015,theyweremeanttosolveaproblemwithasynchronouscode,andtheydid,butoverthe2yearsthatseparatedES2015andES2017,itwasclearthatpromisescouldnotbethefinalsolution.
Promiseswereintroducedtosolvethefamouscallbackhellproblem,buttheyintroducedcomplexityontheirown,andsyntaxcomplexity.
Theyweregoodprimitivesaroundwhichabettersyntaxcouldbeexposedtodevelopers,sowhenthetimewasrightwegotasyncfunctions.
Theymakethecodelooklikeit'ssynchronous,butit'sasynchronousandnon-blockingbehindthescenes.
HowitworksAnasyncfunctionreturnsapromise,likeinthisexample:
constdoSomethingAsync=()=>{
returnnewPromise(resolve=>{
setTimeout(()=>resolve('Ididsomething'),3000)
})
Async/Await
42
}
Whenyouwanttocallthisfunctionyouprepend await,andthecallingcodewillstopuntilthepromiseisresolvedorrejected.Onecaveat:theclientfunctionmustbedefinedasasync.Here'sanexample:
constdoSomething=async()=>{
console.log(awaitdoSomethingAsync())
}
AquickexampleThisisasimpleexampleofasync/awaitusedtorunafunctionasynchronously:
constdoSomethingAsync=()=>{
returnnewPromise(resolve=>{
setTimeout(()=>resolve('Ididsomething'),3000)
})
}
constdoSomething=async()=>{
console.log(awaitdoSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
Theabovecodewillprintthefollowingtothebrowserconsole:
Before
After
Ididsomething//after3s
PromiseallthethingsPrependingthe asynckeywordtoanyfunctionmeansthatthefunctionwillreturnapromise.
Evenifit'snotdoingsoexplicitly,itwillinternallymakeitreturnapromise.
Thisiswhythiscodeisvalid:
constaFunction=async()=>{
return'test'
}
Async/Await
43
aFunction().then(alert)//Thiswillalert'test'
andit'sthesameas:
constaFunction=async()=>{
returnPromise.resolve('test')
}
aFunction().then(alert)//Thiswillalert'test'
ThecodeismuchsimplertoreadAsyoucanseeintheexampleabove,ourcodelooksverysimple.Compareittocodeusingplainpromises,withchainingandcallbackfunctions.
Andthisisaverysimpleexample,themajorbenefitswillarisewhenthecodeismuchmorecomplex.
Forexamplehere'showyouwouldgetaJSONresource,andparseit,usingpromises:
constgetFirstUserData=()=>{
returnfetch('/users.json')//getuserslist
.then(response=>response.json())//parseJSON
.then(users=>users[0])//pickfirstuser
.then(user=>fetch(`/users/${user.name}`))//getuserdata
.then(userResponse=>response.json())//parseJSON
}
getFirstUserData()
Andhereisthesamefunctionalityprovidedusingawait/async:
constgetFirstUserData=async()=>{
constresponse=awaitfetch('/users.json')//getuserslist
constusers=awaitresponse.json()//parseJSON
constuser=users[0]//pickfirstuser
constuserResponse=awaitfetch(`/users/${user.name}`)//getuserdata
constuserData=awaituser.json()//parseJSON
returnuserData
}
getFirstUserData()
Multipleasyncfunctionsinseries
Async/Await
44
Asyncfunctionscanbechainedveryeasily,andthesyntaxismuchmorereadablethanwithplainpromises:
constpromiseToDoSomething=()=>{
returnnewPromise(resolve=>{
setTimeout(()=>resolve('Ididsomething'),10000)
})
}
constwatchOverSomeoneDoingSomething=async()=>{
constsomething=awaitpromiseToDoSomething()
returnsomething+'andIwatched'
}
constwatchOverSomeoneWatchingSomeoneDoingSomething=async()=>{
constsomething=awaitwatchOverSomeoneDoingSomething()
returnsomething+'andIwatchedaswell'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then(res=>{
console.log(res)
})
Willprint:
IdidsomethingandIwatchedandIwatchedaswell
EasierdebuggingDebuggingpromisesishardbecausethedebuggerwillnotstepoverasynchronouscode.
Async/awaitmakesthisveryeasybecausetothecompilerit'sjustlikesynchronouscode.
Async/Await
45
ESModulesESModulesistheECMAScriptstandardforworkingwithmodules.WhileNode.jshasbeenusingtheCommonJSstandardforyears,thebrowserneverhadamodulesystem,aseverymajordecisionsuchasamodulesystemmustbefirststandardizedbyECMAScriptandthenimplemented
ESModulesistheECMAScriptstandardforworkingwithmodules.
WhileNode.jshasbeenusingtheCommonJSstandardforyears,thebrowserneverhadamodulesystem,aseverymajordecisionsuchasamodulesystemmustbefirststandardizedbyECMAScriptandthenimplementedbythebrowser.
ThisstandardizationprocesscompletedwithES6andbrowsersstartedimplementingthisstandardtryingtokeepeverythingwellaligned,workingallinthesameway,andnowESModulesaresupportedinChrome,Safari,EdgeandFirefox(sinceversion60).
Modulesareverycool,becausetheyletyouencapsulateallsortsoffunctionality,andexposethisfunctionalitytootherJavaScriptfiles,aslibraries.
ESModules
46
TheESModulesSyntaxThesyntaxtoimportamoduleis:
importpackagefrom'module-name'
whileCommonJSuses
constpackage=require('module-name')
AmoduleisaJavaScriptfilethatexportsoneormorevalues(objects,functionsorvariables),usingthe exportkeyword.Forexample,thismoduleexportsafunctionthatreturnsastringuppercase:
uppercase.js
exportdefaultstr=>str.toUpperCase()
Inthisexample,themoduledefinesasingle,defaultexport,soitcanbeananonymousfunction.Otherwiseitwouldneedanametodistinguishitfromotherexports.
ESModules
47
Now,anyotherJavaScriptmodulecanimportthefunctionalityofferedbyuppercase.jsbyimportingit.
AnHTMLpagecanaddamodulebyusinga <script>tagwiththespecial type="module"attribute:
<scripttype="module"src="index.js"></script>
Note:thismoduleimportbehaveslikea deferscriptload.SeeefficientlyloadJavaScriptwithdeferandasync
It'simportanttonotethatanyscriptloadedwith type="module"isloadedinstrictmode.
Inthisexample,the uppercase.jsmoduledefinesadefaultexport,sowhenweimportit,wecanassignitanameweprefer:
importtoUpperCasefrom'./uppercase.js'
andwecanuseit:
toUpperCase('test')//'TEST'
Youcanalsouseanabsolutepathforthemoduleimport,toreferencemodulesdefinedonanotherdomain:
importtoUpperCasefrom'https://flavio-es-modules-example.glitch.me/uppercase.js'
Thisisalsovalidimportsyntax:
import{foo}from'/uppercase.js'
import{foo}from'../uppercase.js'
Thisisnot:
import{foo}from'uppercase.js'
import{foo}from'utils/uppercase.js'
It'seitherabsolute,orhasa ./or /beforethename.
Otherimport/exportoptions
ESModules
48
Wesawthisexampleabove:
exportdefaultstr=>str.toUpperCase()
Thiscreatesonedefaultexport.Inafilehoweveryoucanexportmorethanonething,byusingthissyntax:
consta=1
constb=2
constc=3
export{a,b,c}
Anothermodulecanimportallthoseexportsusing
import*from'module'
Youcanimportjustafewofthoseexports,usingthedestructuringassignment:
import{a}from'module'
import{a,b}from'module'
Youcanrenameanyimport,forconvenience,using as:
import{a,bastwo}from'module'
Youcanimportthedefaultexport,andanynon-defaultexportbyname,likeinthiscommonReactimport:
importReact,{Component}from'react'
YoucanseeanESModulesexamplehere:https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html
CORSModulesarefetchedusingCORS.Thismeansthatifyoureferencescriptsfromotherdomains,theymusthaveavalidCORSheaderthatallowscross-siteloading(like Access-Control-Allow-Origin:*)
ESModules
49
Whataboutbrowsersthatdonotsupportmodules?Useacombinationof type="module"and nomodule:
<scripttype="module"src="module.js"></script>
<scriptnomodulesrc="fallback.js"></script>
ConclusionESModulesareoneofthebiggestfeaturesintroducedinmodernbrowsers.TheyarepartofES6buttheroadtoimplementthemhasbeenlong.
Wecannowusethem!Butwemustalsorememberthathavingmorethanafewmodulesisgoingtohaveaperformancehitonourpages,asit'sonemorestepthatthebrowsermustperformatruntime.
WebpackisprobablygoingtostillbeahugeplayerevenifESModuleslandinthebrowser,buthavingsuchafeaturedirectlybuiltinthelanguageishugeforaunificationofhowmodulesworkclient-sideandonNode.jsaswell.
ESModules
50
SinglePageAppsReactApplicationsarealsocalledSinglePageApplications.Whatdoesthismean?
Inthepast,whenbrowsersweremuchlesscapablethantoday,andJavaScriptperformancewaspoor,everypagewascomingfromaserver.Everytimeyouclickedsomething,anewrequestwasmadetotheserverandthebrowsersubsequentlyloadedthenewpage.
Onlyveryinnovativeproductsworkeddifferently,andexperimentedwithnewapproaches.
Today,popularizedbymodernfrontendJavaScriptframeworkslikeReact,anappisusuallybuiltasasinglepageapplication:youonlyloadtheapplicationcode(HTML,CSS,JavaScript)once,andwhenyouinteractwiththeapplication,whatgenerallyhappensisthatJavaScriptinterceptsthebrowsereventsandinsteadofmakinganewrequesttotheserverthatthenreturnsanewdocument,theclientrequestssomeJSONorperformsanactionontheserverbutthepagethattheuserseesisnevercompletelywipedaway,andbehavesmorelikeadesktopapplication.
SinglepageapplicationsarebuiltinJavaScript(oratleastcompiledtoJavaScript)andworkinthebrowser.
Thetechnologyisalwaysthesame,butthephilosophyandsomekeycomponentsofhowtheapplicationworksaredifferent.
ExamplesofSinglePageApplicationsSomenotableexamples:
GmailGoogleMapsFacebookTwitterGoogleDrive
ProsandconsofSPAsAnSPAfeelsmuchfastertotheuser,becauseinsteadofwaitingfortheclient-servercommunicationtohappen,andwaitforthebrowsertore-renderthepage,youcannowhaveinstantfeedback.Thisistheresponsibilityoftheapplicationmaker,butyoucanhave
SinglePageApps
51
transitionsandspinnersandanykindofUXimprovementthatiscertainlybetterthanthetraditionalworkflow.
Inadditiontomakingtheexperiencefastertotheuser,theserverwillconsumelessresourcesbecauseyoucanfocusonprovidinganefficientAPIinsteadofbuildingthelayoutsserver-side.
ThismakesitidealifyoualsobuildamobileappontopoftheAPI,asyoucancompletelyreuseyourexistingserver-sidecode.
SinglePageApplicationsareeasytotransformintoProgressiveWebApps,whichinturnenablesyoutoprovidelocalcachingandtosupportofflineexperiencesforyourservices(orsimplyabettererrormessageifyourusersneedtobeonline).
SPAsarebestusedwhenthereisnoneedforSEO(searchengineoptimization).Forexampleforappsthatworkbehindalogin.
Searchengines,whileimprovingeveryday,stillhavetroubleindexingsitesbuiltwithanSPAapproachratherthanthetraditionalserver-renderedpages.Thisisthecaseforblogs.Ifyouaregoingtorelyonsearchengines,don'tevenbotherwithcreatingasinglepageapplicationwithouthavingaserverrenderedpartaswell.
WhencodinganSPA,youaregoingtowriteagreatdealofJavaScript.Sincetheappcanbelong-running,youaregoingtoneedtopayalotmoreattentiontopossiblememoryleaks-ifinthepastyourpagehadalifespanthatwascountedinminutes,nowanSPAmightstayopenforhoursatatimeandifthereisanymemoryissuethat'sgoingtoincreasethebrowsermemoryusagebyalotmoreandit'sgoingtocauseanunpleasantlyslowexperienceifyoudon'ttakecareofit.
SPAsaregreatwhenworkinginteams.BackenddeveloperscanjustfocusontheAPI,andfrontenddeveloperscanfocusoncreatingthebestuserexperience,makinguseoftheAPIbuiltinthebackend.
Asacon,SinglePageAppsrelyheavilyonJavaScript.Thismightmakeusinganapplicationrunningonlowpowerdevicesapoorexperienceintermsofspeed.Also,someofyourvisitorsmightjusthaveJavaScriptdisabled,andyoualsoneedtoconsideraccessibilityforanythingyoubuild.
OverridingthenavigationSinceyougetridofthedefaultbrowsernavigation,URLsmustbemanagedmanually.
Thispartofanapplicationiscalledtherouter.Someframeworksalreadytakecareofthemforyou(likeEmber),othersrequirelibrariesthatwilldothisjob(likeReactRouter).
SinglePageApps
52
What'stheproblem?Inthebeginning,thiswasanafterthoughtfordevelopersbuildingSinglePageApplications.Thiscausedthecommon"brokenbackbutton"issue:whennavigatinginsidetheapplicationtheURLdidn'tchange(sincethebrowserdefaultnavigationwashijacked)andhittingthebackbutton,acommonoperationthatusersdotogotothepreviousscreen,mightmovetoawebsiteyouvisitedalongtimeago.
ThisproblemcannowbesolvedusingtheHistoryAPIofferedbybrowsers,butmostofthetimeyou'llusealibrarythatinternallyusesthatAPI,likeReactRouter.
SinglePageApps
53
DeclarativeWhatdoesitmeanwhenyoureadthatReactisdeclarative
You'llrunacrossarticlesdescribingReactasadeclarativeapproachtobuildingUIs.
Reactmadeits"declarativeapproach"quitepopularandupfrontsoitpermeatedthefrontendworldalongwithReact.
It'sreallynotanewconcept,butReacttookbuildingUIsalotmoredeclarativelythanwithHTMLtemplates:
youcanbuildWebinterfaceswithouteventouchingtheDOMdirectlyyoucanhaveaneventsystemwithouthavingtointeractwiththeactualDOMEvents.
Theoppositeofdeclarativeisiterative.AcommonexampleofaniterativeapproachislookingupelementsintheDOMusingjQueryorDOMevents.Youtellthebrowserexactlywhattodo,insteadoftellingitwhatyouneed.
TheReactdeclarativeapproachabstractsthatforus.WejusttellReactwewantacomponenttoberenderedinaspecificway,andweneverhavetointeractwiththeDOMtoreferenceitlater.
Declarative
54
ImmutabilityWhatisimmutability?AndhowdoesitfitintheReactworld?
OneconceptyouwilllikelymeetwhenprogramminginReactisimmutability(anditsopposite,mutability).
It'sacontroversialtopic,butwhateveryoumightthinkabouttheconceptofimmutability,Reactandmostofitsecosystemkindofforcesthis,soyouneedtoatleasthaveagraspofwhyit'ssoimportantandtheimplicationsofit.
Inprogramming,avariableisimmutablewhenitsvaluecannotchangeafterit'screated.
Youarealreadyusingimmutablevariableswithoutknowingitwhenyoumanipulateastring.Stringsareimmutablebydefault,whenyouchangetheminrealityyoucreateanewstringandassignittothesamevariablename.
Animmutablevariablecanneverbechanged.Toupdateitsvalue,youcreateanewvariable.
Thesameappliestoobjectsandarrays.
Insteadofchanginganarray,toaddanewitemyoucreateanewarraybyconcatenatingtheoldarray,plusthenewitem.
Anobjectisneverupdated,butcopiedbeforechangingit.
ThisappliestoReactinmanyplaces.
Forexample,youshouldnevermutatethe statepropertyofacomponentdirectly,butonlythroughthe setState()method.
InRedux,younevermutatethestatedirectly,butonlythroughreducers,whicharefunctions.
Thequestionis,why?
Therearevariousreasons,themostimportantofwhichare:
Mutationscanbecentralized,likeinthecaseofRedux,whichimprovesyourdebuggingcapabilitiesandreducessourcesoferrors.Codelookscleanerandsimplertounderstand.Youneverexpectafunctiontochangesomevaluewithoutyouknowing,whichgivesyoupredictability.Whenafunctiondoesnotmutateobjectsbutjustreturnsanewobject,it'scalledapurefunction.ThelibrarycanoptimizethecodebecauseforexampleJavaScriptisfasterwhenswappinganoldobjectreferenceforanentirelynewobject,ratherthanmutatinganexistingobject.Thisgivesyouperformance.
Immutability
55
Immutability
56
PurityWhatispurity,apurefunctionandapurecomponent
InJavaScript,whenafunctiondoesnotmutateobjectsbutjustreturnsanewobject,it'scalledapurefunction.
Afunction,oramethod,inordertobecalledpureshouldnotcausesideeffectsandshouldreturnthesameoutputwhencalledmultipletimeswiththesameinput.
Apurefunctiontakesaninputandreturnsanoutputwithoutchangingtheinputnoranythingelse.
Itsoutputisonlydeterminedbythearguments.Youcouldcallthisfunction1Mtimes,andgiventhesamesetofarguments,theoutputwillalwaysbethesame.
Reactappliesthisconcepttocomponents.AReactcomponentisapurecomponentwhenitsoutputisonlydependantonitsprops.
Allfunctionalcomponentsarepurecomponents:
constButton=props=>{
return<button>{props.message}</button>
}
Classcomponentscanbepureiftheiroutputonlydependsontheprops:
classButtonextendsReact.Component{
render(){
return<button>{this.props.message}</button>
}
}
Purity
57
CompositionWhatiscompositionandwhyisitakeyconceptinyourReactapps
Inprogramming,compositionallowsyoutobuildmorecomplexfunctionalitybycombiningsmallandfocusedfunctions.
Forexample,thinkaboutusing map()tocreateanewarrayfromaninitialset,andthenfilteringtheresultusing filter():
constlist=['Apple','Orange','Egg']
list.map(item=>item[0]).filter(item=>item==='A')//'A'
InReact,compositionallowsyoutohavesomeprettycooladvantages.
Youcreatesmallandleancomponentsandusethemtocomposemorefunctionalityontopofthem.How?
CreatespecializedversionofacomponentUseanoutercomponenttoexpandandspecializeamoregenericcomponent:
constButton=props=>{
return<button>{props.text}</button>
}
constSubmitButton=()=>{
return<Buttontext="Submit"/>
}
constLoginButton=()=>{
return<Buttontext="Login"/>
}
PassmethodsaspropsAcomponentcanfocusontrackingaclickevent,forexample,andwhatactuallyhappenswhentheclickeventhappensisuptothecontainercomponent:
constButton=props=>{
return<buttononClick={props.onClickHandler}>{props.text}</button>
}
Composition
58
constLoginButton=props=>{
return<Buttontext="Login"onClickHandler={props.onClickHandler}/>
}
constContainer=()=>{
constonClickHandler=()=>{
alert('clicked')
}
return<LoginButtononClickHandler={onClickHandler}/>
}
UsingchildrenThe props.childrenpropertyallowsyoutoinjectcomponentsinsideothercomponents.
Thecomponentneedstooutput props.childreninitsJSX:
constSidebar=props=>{
return<aside>{props.children}</aside>
}
andyouembedmorecomponentsintoitinatransparentway:
<Sidebar>
<Linktitle="Firstlink"/>
<Linktitle="Secondlink"/>
</Sidebar>
HigherordercomponentsWhenacomponentreceivesacomponentasapropandreturnsacomponent,it'scalledhigherordercomponent.
We'llseetheminalittlewhile.
Composition
59
TheVirtualDOMTheVirtualDOMisatechniquethatReactusestooptimizeinteractingwiththebrowser
Manyexistingframeworks,beforeReactcameonthescene,weredirectlymanipulatingtheDOMoneverychange.
First,whatistheDOM?
TheDOM(DocumentObjectModel)isaTreerepresentationofthepage,startingfromthe<html>tag,goingdownintoeverychild,whicharecallednodes.
It'skeptinthebrowsermemory,anddirectlylinkedtowhatyouseeinapage.TheDOMhasanAPIthatyoucanusetotraverseit,accesseverysinglenode,filterthem,modifythem.
TheAPIisthefamiliarsyntaxyouhavelikelyseenmanytimes,ifyouwerenotusingtheabstractAPIprovidedbyjQueryandfriends:
document.getElementById(id)
document.getElementsByTagName(name)
document.createElement(name)
parentNode.appendChild(node)
element.innerHTML
element.style.left
element.setAttribute()
element.getAttribute()
element.addEventListener()
window.content
window.onload
window.dump()
window.scrollTo()
ReactkeepsacopyoftheDOMrepresentation,forwhatconcernstheReactrendering:theVirtualDOM
TheVirtualDOMExplained
EverytimetheDOMchanges,thebrowserhastodotwointensiveoperations:repaint(visualorcontentchangestoanelementthatdonotaffectthelayoutandpositioningrelativetootherelements)andreflow(recalculatethelayoutofaportionofthepage-orthewholepagelayout).
ReactusesaVirtualDOMtohelpthebrowseruselessresourceswhenchangesneedtobedoneonapage.
TheVirtualDOM
60
Whenyoucall setState()onaComponent,specifyingastatedifferentthanthepreviousone,ReactmarksthatComponentasdirty.Thisiskey:ReactonlyupdateswhenaComponentchangesthestateexplicitly.
Whathappensnextis:
ReactupdatestheVirtualDOMrelativetothecomponentsmarkedasdirty(withsomeadditionalchecks,liketriggering shouldComponentUpdate())RunsthediffingalgorithmtoreconcilethechangesUpdatestherealDOM
WhyistheVirtualDOMhelpful:batching
ThekeythingisthatReactbatchesmuchofthechangesandperformsauniqueupdatetotherealDOM,bychangingalltheelementsthatneedtobechangedatthesametime,sotherepaintandreflowthebrowsermustperformtorenderthechangesareexecutedjustonce.
TheVirtualDOM
61
UnidirectionalDataFlowWorkingwithReactyoumightencounterthetermUnidirectionalDataFlow.Whatdoesitmean?
UnidirectionalDataFlowisnotaconceptuniquetoReact,butasaJavaScriptdeveloperthismightbethefirsttimeyouhearit.
Ingeneralthisconceptmeansthatdatahasone,andonlyone,waytobetransferredtootherpartsoftheapplication.
InReactthismeansthat:
stateispassedtotheviewandtochildcomponentsactionsaretriggeredbytheviewactionscanupdatethestatethestatechangeispassedtotheviewandtochildcomponents
Theviewisaresultoftheapplicationstate.Statecanonlychangewhenactionshappen.Whenactionshappen,thestateisupdated.
UnidirectionalDataFlow
62
Thankstoone-waybindings,datacannotflowintheoppositeway(aswouldhappenwithtwo-waybindings,forexample),andthishassomekeyadvantages:
it'slesserrorprone,asyouhavemorecontroloveryourdatait'seasiertodebug,asyouknowwhatiscomingfromwhereit'smoreefficient,asthelibraryalreadyknowswhattheboundariesareofeachpartofthesystem
AstateisalwaysownedbyoneComponent.Anydatathat'saffectedbythisstatecanonlyaffectComponentsbelowit:itschildren.
ChangingstateonaComponentwillneveraffectitsparent,oritssiblings,oranyotherComponentintheapplication:justitschildren.
ThisisthereasonthatthestateisoftenmovedupintheComponenttree,sothatitcanbesharedbetweencomponentsthatneedtoaccessit.
UnidirectionalDataFlow
63
JSXJSXisatechnologythatwasintroducedbyReact.Let'sdiveintoit
IntroductiontoJSXJSXisatechnologythatwasintroducedbyReact.
AlthoughReactcanworkcompletelyfinewithoutusingJSX,it'sanidealtechnologytoworkwithcomponents,soReactbenefitsalotfromJSX.
Atfirst,youmightthinkthatusingJSXislikemixingHTMLandJavaScript(andasyou'llseeCSS).
Butthisisnottrue,becausewhatyouarereallydoingwhenusingJSXsyntaxiswritingadeclarativesyntaxofwhatacomponentUIshouldbe.
Andyou'redescribingthatUInotusingstrings,butinsteadusingJavaScript,whichallowsyoutodomanynicethings.
AJSXprimerHereishowyoudefineah1tagcontainingastring:
constelement=<h1>Hello,world!</h1>
JSX
64
ItlookslikeastrangemixofJavaScriptandHTML,butinrealityit'sallJavaScript.
WhatlookslikeHTML,isactuallysyntacticsugarfordefiningcomponentsandtheirpositioninginsidethemarkup.
InsideaJSXexpression,attributescanbeinsertedveryeasily:
constmyId='test'
constelement=<h1id={myId}>Hello,world!</h1>
Youjustneedtopayattentionwhenanattributehasadash( -)whichisconvertedtocamelCasesyntaxinstead,andthese2specialcases:
classbecomes classNameforbecomes htmlFor
becausetheyarereservedwordsinJavaScript.
Here'saJSXsnippetthatwrapstwocomponentsintoa divtag:
<div>
<BlogPostsList/>
<Sidebar/>
</div>
Atagalwaysneedstobeclosed,becausethisismoreXMLthanHTML(ifyouremembertheXHTMLdays,thiswillbefamiliar,butsincethentheHTML5loosesyntaxwon).Inthiscaseaself-closingtagisused.
NoticehowIwrappedthe2componentsintoa div.Why?Becausetherender()functioncanonlyreturnasinglenode,soincaseyouwanttoreturn2siblings,justaddaparent.Itcanbeanytag,notjust div.
TranspilingJSXAbrowsercannotexecuteJavaScriptfilescontainingJSXcode.TheymustbefirsttransformedtoregularJS.
How?Bydoingaprocesscalledtranspiling.
WealreadysaidthatJSXisoptional,becausetoeveryJSXline,acorrespondingplainJavaScriptalternativeisavailable,andthat'swhatJSXistranspiledto.
Forexamplethefollowingtwoconstructsareequivalent:
JSX
65
PlainJS
ReactDOM.render(
React.DOM.div(
{id:'test'},
React.DOM.h1(null,'Atitle'),
React.DOM.p(null,'Aparagraph')
),
document.getElementById('myapp')
)
JSX
ReactDOM.render(
<divid="test">
<h1>Atitle</h1>
<p>Aparagraph</p>
</div>,
document.getElementById('myapp')
)
Thisverybasicexampleisjustthestartingpoint,butyoucanalreadyseehowmorecomplicatedtheplainJSsyntaxiscomparedtousingJSX.
AtthetimeofwritingthemostpopularwaytoperformthetranspilationistouseBabel,whichisthedefaultoptionwhenrunning create-react-app,soifyouuseityoudon'thavetoworry,everythinghappensunderthehoodforyou.
Ifyoudon'tuse create-react-appyouneedtosetupBabelyourself.
JSinJSXJSXacceptsanykindofJavaScriptmixedintoit.
WheneveryouneedtoaddsomeJS,justputitinsidecurlybraces {}.Forexamplehere'showtouseaconstantvaluedefinedelsewhere:
constparagraph='Aparagraph'
ReactDOM.render(
<divid="test">
<h1>Atitle</h1>
<p>{paragraph}</p>
</div>,
document.getElementById('myapp')
)
Thisisabasicexample.CurlybracesacceptanyJScode:
JSX
66
constparagraph='Aparagraph'
ReactDOM.render(
<table>
{rows.map((row,i)=>{
return<tr>{row.text}</tr>
})}
</div>,
document.getElementById('myapp')
)
AsyoucanseewenestedJavaScriptinsideJSXdefinedinsideJavaScriptnestedinJSX.Youcangoasdeepasyouneed.
HTMLinJSXJSXresemblesHTMLalot,butit'sactuallyXMLsyntax.
IntheendyourenderHTML,soyouneedtoknowafewdifferencesbetweenhowyouwoulddefinesomethingsinHTML,andhowyoudefinetheminJSX.
Youneedtoclosealltags
JustlikeinXHTML,ifyouhaveeverusedit,youneedtoclosealltags:nomore <br>butinsteadusetheself-closingtag: <br/>(thesamegoesforothertags)
camelCaseisthenewstandard
InHTMLyou'llfindattributeswithoutanycase(e.g. onchange).InJSX,theyarerenamedtotheircamelCaseequivalent:
onchange=> onChangeonclick=> onClickonsubmit=> onSubmit
classbecomes className
DuetothefactthatJSXisJavaScript,and classisareservedword,youcan'twrite
<pclass="description">
butyouneedtouse
<pclassName="description">
JSX
67
Thesameappliesto forwhichistranslatedto htmlFor.
Thestyleattributechangesitssemantics
The styleattributeinHTMLallowstospecifyinlinestyle.InJSXitnolongeracceptsastring,andinCSSinReactyou'llseewhyit'saveryconvenientchange.
Forms
FormfieldsdefinitionandeventsarechangedinJSXtoprovidemoreconsistencyandutility.
FormsinJSXgoesintomoredetailsonforms.
CSSinReactJSXprovidesacoolwaytodefineCSS.
IfyouhavealittleexperiencewithHTMLinlinestyles,atfirstglanceyou'llfindyourselfpushedback10or15years,toaworldwhereinlineCSSwascompletelynormal(nowadaysit'sdemonizedandusuallyjusta"quickfix"go-tosolution).
JSXstyleisnotthesamething:firstofall,insteadofacceptingastringcontainingCSSproperties,theJSX styleattributeonlyacceptsanobject.Thismeansyoudefinepropertiesinanobject:
vardivStyle={
color:'white'
}
ReactDOM.render(<divstyle={divStyle}>HelloWorld!</div>,mountNode)
or
ReactDOM.render(<divstyle={{color:'white'}}>HelloWorld!</div>,mountNode)
TheCSSvaluesyouwriteinJSXareslightlydifferentfromplainCSS:
thekeyspropertynamesarecamelCasedvaluesarejuststringsyouseparateeachtuplewithacomma
WhyisthispreferredoverplainCSS/SASS/LESS?
JSX
68
CSSisanunsolvedproblem.Sinceitsinception,dozensoftoolsarounditroseandthenfell.ThemainproblemwithJSisthatthereisnoscopingandit'seasytowriteCSSthatisnotenforcedinanyway,thusa"quickfix"canimpactelementsthatshouldnotbetouched.
JSXallowscomponents(definedinReactforexample)tocompletelyencapsulatetheirstyle.
Isthisthego-tosolution?
InlinestylesinJSXaregooduntilyouneedto
1. writemediaqueries2. styleanimations3. referencepseudoclasses(e.g. :hover)4. referencepseudoelements(e.g. ::first-letter)
Inshort,theycoverthebasics,butit'snotthefinalsolution.
FormsinJSXJSXaddssomechangestohowHTMLformswork,withthegoalofmakingthingseasierforthedeveloper.
valueand defaultValue
The valueattributealwaysholdsthecurrentvalueofthefield.
The defaultValueattributeholdsthedefaultvaluethatwassetwhenthefieldwascreated.
ThishelpssolvesomeweirdbehaviorofregularDOMinteractionwheninspectinginput.valueand input.getAttribute('value')returningonethecurrentvalueandonetheoriginaldefaultvalue.
Thisalsoappliestothe textareafield,e.g.
<textarea>Sometext</textarea>
butinstead
<textareadefaultValue={'Sometext'}/>
For selectfields,insteadofusing
<select>
JSX
69
<optionvalue="x"selected>
...
</option>
</select>
use
<selectdefaultValue="x">
<optionvalue="x">...</option>
</select>
AmoreconsistentonChange
Passingafunctiontothe onChangeattributeyoucansubscribetoeventsonformfields.
Itworksconsistentlyacrossfields,even radio, selectand checkboxinputfieldsfireaonChangeevent.
onChangealsofireswhentypingacharacterintoan inputor textareafield.
JSXautoescapesTomitigatetheeverpresentriskofXSSexploits,JSXforcesautomaticescapinginexpressions.
ThismeansthatyoumightrunintoissueswhenusinganHTMLentityinastringexpression.
Youexpectthefollowingtoprint ©2017:
<p>{'©2017'}</p>
Butit'snot,it'sprinting ©2017becausethestringisescaped.
Tofixthisyoucaneithermovetheentitiesoutsidetheexpression:
<p>©2017</p>
orbyusingaconstantthatprintstheUnicoderepresentationcorrespondingtotheHTMLentityyouneedtoprint:
<p>{'\u00A92017'}</p>
JSX
70
WhitespaceinJSXToaddwhitespaceinJSXthereare2rules:
Horizontalwhitespaceistrimmedto1
Ifyouhavewhitespacebetweenelementsinthesameline,it'salltrimmedto1whitespace.
<p>Somethingbecomesthis</p>
becomes
<p>Somethingbecomesthis</p>
Verticalwhitespaceiseliminated
<p>
Something
becomes
this
</p>
becomes
<p>Somethingbecomesthis</p>
Tofixthisproblemyouneedtoexplicitlyaddwhitespace,byaddingaspaceexpressionlikethis:
<p>
Something
{''}becomes
{''}this
</p>
orbyembeddingthestringinaspaceexpression:
<p>
Something
{'becomes'}
this
</p>
JSX
71
AddingcommentsinJSXYoucanaddcommentstoJSXbyusingthenormalJavaScriptcommentsinsideanexpression:
<p>
{/*acomment*/}
{
//anothercomment
}
</p>
SpreadattributesInJSXacommonoperationisassigningvaluestoattributes.
Insteadofdoingitmanually,e.g.
<div>
<BlogPosttitle={data.title}date={data.date}/>
</div>
youcanpass
<div>
<BlogPost{...data}/>
</div>
andthepropertiesofthe dataobjectwillbeusedasattributesautomatically,thankstotheES6spreadoperator
HowtoloopinJSXIfyouhaveasetofelementsyouneedtoloopupontogenerateaJSXpartial,youcancreatealoop,andthenaddJSXtoanarray:
constelements=[]//..somearray
constitems=[]
for(const[index,value]ofelements.entries(){
items.push(<Elementkey={index}/>)
}
JSX
72
NowwhenrenderingtheJSXyoucanembedthe itemsarraysimplybywrappingitincurlybraces:
constelements=['one','two','three'];
constitems=[]
for(const[index,value]ofelements.entries(){
items.push(<likey={index}>{value}</li>)
}
return(
<div>
{items}
</div>
)
YoucandothesamedirectlyintheJSX,using mapinsteadofafor-ofloop:
constelements=['one','two','three'];
return(
<ul>
{elements.map((value,index)=>{
return<likey={index}>{value}</li>
})}
</ul>
)
JSX
73
ComponentsAbriefintroductiontoReactComponents
Acomponentisoneisolatedpieceofinterface.ForexampleinatypicalbloghomepageyoumightfindtheSidebarcomponent,andtheBlogPostsListcomponent.Theyareinturncomposedofcomponentsthemselves,soyoucouldhavealistofBlogpostcomponents,eachforeveryblogpost,andeachwithitsownpeculiarproperties.
Reactmakesitverysimple:everythingisacomponent.
EvenplainHTMLtagsarecomponentontheirown,andtheyareaddedbydefault.
Thenext2linesareequivalent,theydothesamething.OnewithJSX,onewithout,byinjecting <h1>HelloWorld!</h1>intoanelementwithid app.
importReactfrom'react'
importReactDOMfrom'react-dom'
ReactDOM.render(<h1>HelloWorld!</h1>,document.getElementById('app'))
ReactDOM.render(
React.DOM.h1(null,'HelloWorld!'),
document.getElementById('app')
)
See, React.DOMexposedusan h1component.WhichotherHTMLtagsareavailable?Allofthem!Youcaninspectwhat React.DOMoffersbytypingitintheBrowserConsole:
Components
74
(thelistislonger)
Thebuilt-incomponentsarenice,butyou'llquicklyoutgrowthem.WhatReactexcelsinislettinguscomposeaUIbycomposingcustomcomponents.
CustomcomponentsThereare2waystodefineacomponentinReact.
Afunctioncomponent:
constBlogPostExcerpt=()=>{
return(
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
Aclasscomponent:
importReact,{Component}from'react'
classBlogPostExcerptextendsComponent{
render(){
return(
<div>
<h1>Title</h1>
<p>Description</p>
Components
75
</div>
)
}
}
Upuntilrecently,classcomponentsweretheonlywaytodefineacomponentthathaditsownstate,andcouldaccessthelifecyclemethodssoyoucoulddothingswhenthecomponentwasfirstrendered,updatedorremoved.
ReactHookschangedthis,soourfunctioncomponentsarenowmuchmorepowerfulthaneverandIbelievewe'llseefewerandfewerclasscomponentsinthefuture,althoughitwillstillbeperfectlyvalidwaytocreatecomponents.
Thereisalsoathirdsyntaxwhichusesthe ES5syntax,withouttheclasses:
importReactfrom'react'
React.createClass({
render(){
return(
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
})
You'llrarelyseethisinmodern, >ES6codebases.
Components
76
StateHowtointeractwiththestateofyourcomponents
Settingthedefaultstate
IntheComponentconstructor,initialize this.state.ForexampletheBlogPostExcerptcomponentmighthavea clickedstate:
classBlogPostExcerptextendsComponent{
constructor(props){
super(props)
this.state={clicked:false}
}
render(){
return(
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
}
Accessingthestate
Theclickedstatecanbeaccessedbyreferencing this.state.clicked:
classBlogPostExcerptextendsComponent{
constructor(props){
super(props)
this.state={clicked:false}
}
render(){
return(
<div>
<h1>Title</h1>
<p>Description</p>
<p>Clicked:{this.state.clicked}</p>
</div>
)
}
}
State
77
Mutatingthestate
Astateshouldneverbemutatedbyusing
this.state.clicked=true
Instead,youshouldalwaysuse setState()instead,passingitanobject:
this.setState({clicked:true})
Theobjectcancontainasubset,orasuperset,ofthestate.Onlythepropertiesyoupasswillbemutated,theonesomittedwillbeleftintheircurrentstate.
WhyyoushouldalwaysusesetState()
Thereasonisthatusingthismethod,Reactknowsthatthestatehaschanged.ItwillthenstarttheseriesofeventsthatwillleadtotheComponentbeingre-rendered,alongwithanyDOMupdate.
UnidirectionalDataFlow
AstateisalwaysownedbyoneComponent.Anydatathat'saffectedbythisstatecanonlyaffectComponentsbelowit:itschildren.
ChangingthestateonaComponentwillneveraffectitsparent,oritssiblings,oranyotherComponentintheapplication:justitschildren.
ThisisthereasonthestateisoftenmovedupintheComponenttree.
MovingtheStateUpintheTree
BecauseoftheUnidirectionalDataFlowrule,iftwocomponentsneedtosharestate,thestateneedstobemoveduptoacommonancestor.
Manytimestheclosestancestoristhebestplacetomanagethestate,butit'snotamandatoryrule.
Thestateispasseddowntothecomponentsthatneedthatvalueviaprops:
classConverterextendsReact.Component{
constructor(props){
super(props)
this.state={currency:'€'}
}
State
78
render(){
return(
<div>
<Displaycurrency={this.state.currency}/>
<CurrencySwitchercurrency={this.state.currency}/>
</div>
)
}
}
Thestatecanbemutatedbyachildcomponentbypassingamutatingfunctiondownasaprop:
classConverterextendsReact.Component{
constructor(props){
super(props)
this.state={currency:'€'}
}
handleChangeCurrency=event=>{
this.setState({currency:this.state.currency==='€'?'$':'€'})
}
render(){
return(
<div>
<Displaycurrency={this.state.currency}/>
<CurrencySwitcher
currency={this.state.currency}
handleChangeCurrency={this.handleChangeCurrency}
/>
</div>
)
}
}
constCurrencySwitcher=props=>{
return(
<buttononClick={props.handleChangeCurrency}>
Currentcurrencyis{props.currency}.Changeit!
</button>
)
}
constDisplay=props=>{
return<p>Currentcurrencyis{props.currency}.</p>
}
State
79
State
80
PropsHowtousepropstopassdataaroundyourReactcomponents
PropsishowComponentsgettheirproperties.Startingfromthetopcomponent,everychildcomponentgetsitspropsfromtheparent.Inafunctioncomponent,propsisallitgetspassed,andtheyareavailablebyadding propsasthefunctionargument:
constBlogPostExcerpt=props=>{
return(
<div>
<h1>{props.title}</h1>
<p>{props.description}</p>
</div>
)
}
Inaclasscomponent,propsarepassedbydefault.Thereisnoneedtoaddanythingspecial,andtheyareaccessibleas this.propsinaComponentinstance.
importReact,{Component}from'react'
classBlogPostExcerptextendsComponent{
render(){
return(
<div>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</div>
)
}
}
Passingpropsdowntochildcomponentsisagreatwaytopassvaluesaroundinyourapplication.Acomponenteitherholdsdata(hasstate)orreceivesdatathroughitsprops.
Itgetscomplicatedwhen:
youneedtoaccessthestateofacomponentfromachildthat'sseverallevelsdown(allthepreviouschildrenneedtoactasapass-through,eveniftheydonotneedtoknowthestate,complicatingthings)youneedtoaccessthestateofacomponentfromacompletelyunrelatedcomponent.
Defaultvaluesforprops
Props
81
Ifanyvalueisnotrequiredweneedtospecifyadefaultvalueforitifit'smissingwhentheComponentisinitialized.
BlogPostExcerpt.propTypes={
title:PropTypes.string,
description:PropTypes.string
}
BlogPostExcerpt.defaultProps={
title:'',
description:''
}
SometoolinglikeESLinthavetheabilitytoenforcedefiningthedefaultPropsforaComponentwithsomepropTypesnotexplicitlyrequired.
HowpropsarepassedWheninitializingacomponent,passthepropsinawaysimilartoHTMLattributes:
constdesc='Adescription'
//...
<BlogPostExcerpttitle="Ablogpost"description={desc}/>
Wepassedthetitleasaplainstring(somethingwecanonlydowithstrings!),anddescriptionasavariable.
ChildrenAspecialpropis children.Thatcontainsthevalueofanythingthatispassedinthe bodyofthecomponent,forexample:
<BlogPostExcerpttitle="Ablogpost"description="{desc}">
Something
</BlogPostExcerpt>
Inthiscase,inside BlogPostExcerptwecouldaccess"Something"bylookingupthis.props.children.
WhilePropsallowaComponenttoreceivepropertiesfromitsparent,tobe"instructed"toprintsomedataforexample,stateallowsacomponenttotakeonlifeitself,andbeindependentofthesurroundingenvironment.
Props
82
Props
83
PresentationalvscontainercomponentsThedifferencebetweenPresentationalandContainerComponentsinReact
InReactcomponentsareoftendividedinto2bigbuckets:presentationalcomponentsandcontainercomponents.
Eachofthosehavetheiruniquecharacteristics.
Presentationalcomponentsaremostlyconcernedwithgeneratingsomemarkuptobeoutputted.
Theydon'tmanageanykindofstate,exceptforstaterelatedthethepresentation
Containercomponentsaremostlyconcernedwiththe"backend"operations.
Theymighthandlethestateofvarioussub-components.Theymightwrapseveralpresentationalcomponents.TheymightinterfacewithRedux.
Asawaytosimplifythedistinction,wecansaypresentationalcomponentsareconcernedwiththelook,containercomponentsareconcernedwithmakingthingswork.
Forexample,thisisapresentationalcomponent.Itgetsdatafromitsprops,andjustfocusesonshowinganelement:
constUsers=props=>(
<ul>
{props.users.map(user=>(
<li>{user}</li>
))}
</ul>
)
Ontheotherhandthisisacontainercomponent.Itmanagesandstoresitsowndata,andusesthepresentationalcomponenttodisplayit.
classUsersContainerextendsReact.Component{
constructor(){
this.state={
users:[]
}
}
componentDidMount(){
axios.get('/users').then(users=>
this.setState({users:users}))
)
}
Presentationalvscontainercomponents
84
render(){
return<Usersusers={this.state.users}/>
}
}
Presentationalvscontainercomponents
85
StatevsPropsWhat'sthedifferencebetweenstateandpropsinReact?
InaReactcomponent,propsarevariablespassedtoitbyitsparentcomponent.Stateontheotherhandisstillvariables,butdirectlyinitializedandmanagedbythecomponent.
Thestatecanbeinitializedbyprops.
Forexample,aparentcomponentmightincludeachildcomponentbycalling
<ChildComponent/>
Theparentcanpassapropbyusingthissyntax:
<ChildComponentcolor=green/>
InsidetheChildComponentconstructorwecouldaccesstheprop:
classChildComponentextendsReact.Component{
constructor(props){
super(props)
console.log(props.color)
}
}
andanyothermethodinthisclasscanreferencethepropsusing this.props.
Propscanbeusedtosettheinternalstatebasedonapropvalueintheconstructor,likethis:
classChildComponentextendsReact.Component{
constructor(props){
super(props)
this.state.colorName=props.color
}
}
Ofcourseacomponentcanalsoinitializethestatewithoutlookingatprops.
Inthiscasethere'snothingusefulgoingon,butimaginedoingsomethingdifferentbasedonthepropvalue,probablysettingastatevalueisbest.
Propsshouldneverbechangedinachildcomponent,soifthere'ssomethinggoingonthatalterssomevariable,thatvariableshouldbelongtothecomponentstate.
StatevsProps
86
Propsarealsousedtoallowchildcomponentstoaccessmethodsdefinedintheparentcomponent.Thisisagoodwaytocentralizemanagingthestateintheparentcomponent,andavoidchildrentohavetheneedtohavetheirownstate.
Mostofyourcomponentswilljustdisplaysomekindofinformationbasedonthepropstheyreceived,andstaystateless.
StatevsProps
87
PropTypesHowtousePropTypestosettherequiredtypeofaprop
SinceJavaScriptisadynamicallytypedlanguage,wedon'treallyhaveawaytoenforcethetypeofavariableatcompiletime,andifwepassinvalidtypes,theywillfailatruntimeorgiveweirdresultsifthetypesarecompatiblebutnotwhatweexpect.
FlowandTypeScripthelpalot,butReacthasawaytodirectlyhelpwithpropstypes,andevenbeforerunningthecode,ourtools(editors,linters)candetectwhenwearepassingthewrongvalues:
importPropTypesfrom'prop-types'
importReactfrom'react'
classBlogPostExcerptextendsComponent{
render(){
return(
<div>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</div>
)
}
}
BlogPostExcerpt.propTypes={
title:PropTypes.string,
description:PropTypes.string
}
exportdefaultBlogPostExcerpt
Whichtypescanweuse
Thesearethefundamentaltypeswecanaccept:
PropTypes.arrayPropTypes.boolPropTypes.funcPropTypes.numberPropTypes.objectPropTypes.stringPropTypes.symbol
Wecanacceptoneoftwotypes:
PropTypes
88
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
Wecanacceptoneofmanyvalues:
PropTypes.oneOf(['Test1','Test2']),
Wecanacceptaninstanceofaclass:
PropTypes.instanceOf(Something)
WecanacceptanyReactnode:
PropTypes.node
orevenanytypeatall:
PropTypes.any
Arrayshaveaspecialsyntaxthatwecanusetoacceptanarrayofaparticulartype:
PropTypes.arrayOf(PropTypes.string)
Objects,wecancomposeanobjectpropertiesbyusing
PropTypes.shape({
color:PropTypes.string,
fontSize:PropTypes.number
})
Requiringproperties
Appending isRequiredtoanyPropTypesoptionwillcauseReacttoreturnanerrorifthatpropertyismissing:
PropTypes.arrayOf(PropTypes.string).isRequired,
PropTypes.string.isRequired,
PropTypes
89
PropTypes
90
FragmentHowtouseReact.FragmenttocreateinvisibleHTMLtags
NoticehowIwrapreturnvaluesina div.Thisisbecauseacomponentcanonlyreturnonesingleelement,andifyouwantmorethanone,youneedtowrapitwithanothercontainertag.
Thishowevercausesanunnecessary divintheoutput.YoucanavoidthisbyusingReact.Fragment:
importReact,{Component}from'react'
classBlogPostExcerptextendsComponent{
render(){
return(
<React.Fragment>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</React.Fragment>
)
}
}
whichalsohasaveryniceshorthandsyntax <></>thatissupportedonlyinrecentreleases(andBabel7+):
importReact,{Component}from'react'
classBlogPostExcerptextendsComponent{
render(){
return(
<>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</>
)
}
}
Fragment
91
EventsLearnhowtointeractwitheventsinaReactapplication
Reactprovidesaneasywaytomanageevents.Preparetosaygoodbyeto addEventListener.
InthepreviousarticleabouttheStateyousawthisexample:
constCurrencySwitcher=props=>{
return(
<buttononClick={props.handleChangeCurrency}>
Currentcurrencyis{props.currency}.Changeit!
</button>
)
}
Ifyou'vebeenusingJavaScriptforawhile,thisisjustlikeplainoldJavaScripteventhandlers,exceptthatthistimeyou'redefiningeverythinginJavaScript,notinyourHTML,andyou'repassingafunction,notastring.
TheactualeventnamesarealittlebitdifferentbecauseinReactyouusecamelCaseforeverything,so onclickbecomes onClick, onsubmitbecomes onSubmit.
Forreference,thisisoldschoolHTMLwithJavaScripteventsmixedin:
<buttononclick="handleChangeCurrency()">...</button>
Eventhandlers
It'saconventiontohaveeventhandlersdefinedasmethodsontheComponentclass:
classConverterextendsReact.Component{
handleChangeCurrency=event=>{
this.setState({currency:this.state.currency==='€'?'$':'€'})
}
}
Allhandlersreceiveaneventobjectthatadheres,cross-browser,totheW3CUIEventsspec.
Bind thisinmethods
Don'tforgettobindmethods.ThemethodsofES6classesbydefaultarenotbound.Whatthismeansisthat thisisnotdefinedunlessyoudefinemethodsasarrowfunctions:
Events
92
classConverterextendsReact.Component{
handleClick=e=>{
/*...*/
}
//...
}
whenusingthethepropertyinitializersyntaxwithBabel(enabledbydefaultin create-react-app),otherwiseyouneedtobinditmanuallyintheconstructor:
classConverterextendsReact.Component{
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
handleClick(e){}
}
Theeventsreference
Therearelotsofeventssupported,here'sasummarylist.
Clipboard
onCopyonCutonPaste
Composition
onCompositionEndonCompositionStartonCompositionUpdate
Keyboard
onKeyDownonKeyPressonKeyUp
Focus
onFocus
Events
93
onBlur
Form
onChangeonInputonSubmit
Mouse
onClickonContextMenuonDoubleClickonDragonDragEndonDragEnteronDragExitonDragLeaveonDragOveronDragStartonDroponMouseDownonMouseEnteronMouseLeaveonMouseMoveonMouseOutonMouseOveronMouseUp
Selection
onSelect
Touch
onTouchCancelonTouchEndonTouchMoveonTouchStart
UI
Events
94
onScroll
MouseWheel
onWheel
Media
onAbortonCanPlayonCanPlayThroughonDurationChangeonEmptiedonEncryptedonEndedonErroronLoadedDataonLoadedMetadataonLoadStartonPauseonPlayonPlayingonProgressonRateChangeonSeekedonSeekingonStalledonSuspendonTimeUpdateonVolumeChangeonWaiting
Image
onLoadonError
Animation
onAnimationStartonAnimationEnd
Events
95
onAnimationIteration
Transition
onTransitionEnd
Events
96
LifecycleeventsFindouttheReactLifecycleeventsandhowyoucanusethem
Reactclasscomponentscanhavehooksforseverallifecycleevents.
Hooksallowfunctioncomponentstoaccessthemtoo,inadifferentway.
Duringthelifetimeofacomponent,there'saseriesofeventsthatgetscalled,andtoeacheventyoucanhookandprovidecustomfunctionality.
Whathookisbestforwhatfunctionalityissomethingwe'regoingtoseehere.
First,thereare3phasesinaReactcomponentlifecycle:
MountingUpdatingUnmounting
Let'sseethose3phasesindetailandthemethodsthatgetcalledforeach.
MountingWhenmountingyouhave4lifecyclemethodsbeforethecomponentismountedintheDOM:the constructor, getDerivedStateFromProps, renderand componentDidMount.
Constructor
Theconstructoristhefirstmethodthatiscalledwhenmountingacomponent.
Youusuallyusetheconstructortosetuptheinitialstateusing this.state=....
getDerivedStateFromProps()
Whenthestatedependsonprops, getDerivedStateFromPropscanbeusedtoupdatethestatebasedonthepropsvalue.
ItwasaddedinReact16.3,aimingtoreplacethe componentWillReceivePropsdeprecatedmethod.
Inthismethodyouhaven'taccessto thisasit'sastaticmethod.
It'sapuremethod,soitshouldnotcausesideeffectsandshouldreturnthesameoutputwhencalledmultipletimeswiththesameinput.
Lifecycleevents
97
Returnsanobjectwiththeupdatedelementsofthestate(ornullifthestatedoesnotchange)
render()
Fromtherender()methodyoureturntheJSXthatbuildsthecomponentinterface.
It'sapuremethod,soitshouldnotcausesideeffectsandshouldreturnthesameoutputwhencalledmultipletimeswiththesameinput.
componentDidMount()
ThismethodistheonethatyouwillusetoperformAPIcalls,orprocessoperationsontheDOM.
UpdatingWhenupdatingyouhave5lifecyclemethodsbeforethecomponentismountedintheDOM:the getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdateandcomponentDidUpdate.
getDerivedStateFromProps()
Seetheabovedescriptionforthismethod.
shouldComponentUpdate()
Thismethodreturnsaboolean, trueor false.YouusethismethodtotellReactifitshouldgoonwiththererendering,anddefaultsto true.Youwillreturn falsewhenrerenderingisexpensiveandyouwanttohavemorecontrolonwhenthishappens.
render()
Seetheabovedescriptionforthismethod.
getSnapshotBeforeUpdate()
Inthismethodyouhaveaccesstothepropsandstateofthepreviousrender,andofthecurrentrender.
Itsusecasesareveryniche,andit'sprobablytheonethatyouwilluseless.
Lifecycleevents
98
componentDidUpdate()
ThismethodiscalledwhenthecomponenthasbeenupdatedintheDOM.Usethistorunany3rdpartyDOMAPIorcallAPIsthatmustbeupdatedwhentheDOMchanges.
Itcorrespondstothe componentDidMount()methodfromthemountingphase.
UnmountingInthisphaseweonlyhaveonemethod, componentWillUnmount.
componentWillUnmount()
ThemethodiscalledwhenthecomponentisremovedfromtheDOM.Usethistodoanysortofcleanupyouneedtoperform.
LegacyIfyouareworkingonanappthatuses componentWillMount, componentWillReceivePropsorcomponentWillUpdate,thoseweredeprecatedinReact16.3andyoushouldmigratetootherlifecyclemethods.
Lifecycleevents
99
HandlingformsHowtohandleformsinaReactapplication
FormsareoneofthefewHTMLelementsthatareinteractivebydefault.
Theyweredesignedtoallowtheusertointeractwithapage.
Commonusesofforms?
SearchContactformsShoppingcartscheckoutLoginandregistrationandmore!
UsingReactwecanmakeourformsmuchmoreinteractiveandlessstatic.
TherearetwomainwaysofhandlingformsinReact,whichdifferonafundamentallevel:howdataismanaged.
ifthedataishandledbytheDOM,wecallthemuncontrolledcomponents
Handlingforms
100
ifthedataishandledbythecomponentswecallthemcontrolledcomponents
Asyoucanimagine,controlledcomponentsiswhatyouwillusemostofthetime.Thecomponentstateisthesinglesourceoftruth,ratherthantheDOM.Someformfieldsareinherentlyuncontrolledbecauseoftheirbehavior,likethe <inputtype="file">field.
Whenanelementstatechangesinaformfieldmanagedbyacomponent,wetrackitusingtheonChangeattribute.
classFormextendsReact.Component{
constructor(props){
super(props)
this.state={username:''}
}
handleChange(event){}
render(){
return(
<form>
Username:
<input
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
</form>
)
}
}
Inordertosetthenewstate,wemustbind thistothe handleChangemethod,otherwisethisisnotaccessiblefromwithinthatmethod:
classFormextendsReact.Component{
constructor(props){
super(props)
this.state={username:''}
this.handleChange=this.handleChange.bind(this)
}
handleChange(event){
this.setState({value:event.target.value})
}
render(){
return(
<form>
<input
type="text"
value={this.state.username}
onChange={this.handleChange}
Handlingforms
101
/>
</form>
)
}
}
Similarly,weusethe onSubmitattributeontheformtocallthe handleSubmitmethodwhentheformissubmitted:
classFormextendsReact.Component{
constructor(props){
super(props)
this.state={username:''}
this.handleChange=this.handleChange.bind(this)
this.handleSubmit=this.handleSubmit.bind(this)
}
handleChange(event){
this.setState({value:event.target.value})
}
handleSubmit(event){
alert(this.state.username)
event.preventDefault()
}
render(){
return(
<formonSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
<inputtype="submit"value="Submit"/>
</form>
)
}
}
Validationinaformcanbehandledinthe handleChangemethod:youhaveaccesstotheoldvalueofthestate,andthenewone.Youcancheckthenewvalueandifnotvalidrejecttheupdatedvalue(andcommunicateitinsomewaytotheuser).
HTMLFormsareinconsistent.Theyhavealonghistory,anditshows.Reacthowevermakesthingsmoreconsistentforus,andyoucanget(andupdate)fieldsusingits valueattribute.
Here'sa textarea,forexample:
<textareavalue={this.state.address}onChange={this.handleChange}/>
Handlingforms
102
Thesamegoesforthe selecttag:
<selectvalue="{this.state.age}"onChange="{this.handleChange}">
<optionvalue="teen">Lessthan18</option>
<optionvalue="adult">18+</option>
</select>
Previouslywementionedthe <inputtype="file">field.Thatworksabitdifferently.
Inthiscaseyouneedtogetareferencetothefieldbyassigningthe refattributetoapropertydefinedintheconstructorwith React.createRef(),andusethattogetthevalueofitinthesubmithandler:
classFileInputextendsReact.Component{
constructor(props){
super(props)
this.curriculum=React.createRef()
this.handleSubmit=this.handleSubmit.bind(this)
}
handleSubmit(event){
alert(this.curriculum.current.files[0].name)
event.preventDefault()
}
render(){
return(
<formonSubmit={this.handleSubmit}>
<inputtype="file"ref={this.curriculum}/>
<inputtype="submit"value="Submit"/>
</form>
)
}
}
Thisistheuncontrolledcomponentsway.ThestateisstoredintheDOMratherthaninthecomponentstate(noticeweused this.curriculumtoaccesstheuploadedfile,andhavenottouchedthe state.
Iknowwhatyou'rethinking-beyondthosebasics,theremustbealibrarythatsimplifiesallthisformhandlingstuffandautomatesvalidation,errorhandlingandmore,right?Thereisagreatone,Formik(Formiktutorialcomingtomorrow!)
Handlingforms
103
ReferenceaDOMelementFindouthowtorefaDOMelementinReact
ReactisgreatatabstractingawaytheDOMfromyouwhenbuildingapps.
ButwhatifyouwanttoaccesstheDOMelementthataReactcomponentrepresents?
MaybeyouhavetoaddalibrarythatinteractsdirectlywiththeDOMlikeachartlibrary,maybeyouneedtocallsomeDOMAPI,oraddfocusonanelement.
Whateverthereasonis,agoodpracticeismakingsurethere'snootherwayofdoingsowithoutaccessingtheDOMdirectly.
IntheJSXofyourcomponent,youcanassignthereferenceoftheDOMelementtoacomponentpropertyusingthisattribute:
ref={el=>this.someProperty=el}
Putthisintocontext,forexamplewitha buttonelement:
<buttonref={el=>(this.button=el)}/>
buttonreferstoapropertyofthecomponent,whichcanthenbeusedbythecomponent'slifecyclemethods(orothermethods)tointeractwiththeDOM:
classSomeComponentextendsComponent{
render(){
return<buttonref={el=>(this.button=el)}/>
}
}
Inafunctioncomponentthemechanismisthesame,youjustavoidusing this(sinceitdoesnotpointtothecomponentinstance)anduseapropertyinstead:
functionSomeComponent(){
letbutton
return<buttonref={el=>(button=el)}/>
}
ReferenceaDOMelement
104
ReferenceaDOMelement
105
ServersiderenderingWhatisServerSideRendering?HowtodoitwithReact?
ServerSideRendering,alsocalledSSR,istheabilityofaJavaScriptapplicationtorenderontheserverratherthaninthebrowser.
Whywouldweeverwanttodoso?
itallowsyoursitetohaveafasterfirstpageloadtime,whichisthekeytoagooduserexperienceitisessentialforSEO:searchenginescannot(yet?)efficientlyandcorrectlyindexapplicationsthatexclusivelyrenderclient-side.DespitethelatestimprovementstoindexinginGoogle,thereareothersearchenginestoo,andGoogleisnotperfectatitinanycase.Also,Googlefavorssiteswithfastloadtimes,andhavingtoloadclient-sideisnotgoodforspeedit'sgreatwhenpeopleshareapageofyoursiteonsocialmedia,astheycaneasilygatherthemetadataneededtonicelysharethelink(images,title,description..)
WithoutServerSideRendering,allyourservershipsisanHTMLpagewithnobody,justsomescripttagsthatarethenusedbythebrowsertorendertheapplication.
Client-renderedappsaregreatatanysubsequentuserinteractionafterthefirstpageload.ServerSideRenderingallowsustogetthesweetspotinthemiddleofclient-renderedappsandbackend-renderedapps:thepageisgeneratedserver-side,butallinteractionswiththepageonceit'sbeenloadedarehandledclient-side.
HoweverServerSideRenderinghasitsdrawbacktoo:
it'sfairtosaythatasimpleSSRproofofconceptissimple,butthecomplexityofSSRcangrowwiththecomplexityofyourapplicationrenderingabigapplicationserver-sidecanbequiteresource-intensive,andunderheavyloaditcouldevenprovideaslowerexperiencethanclient-siderendering,sinceyouhaveasinglebottleneck
AverysimplisticexampleofwhatittakestoServer-SiderenderaReactappSSRsetupscangrowvery,verycomplexandmosttutorialswillbakeinRedux,ReactRouterandmanyotherconceptsfromthestart.
TounderstandhowSSRworks,let'sstartfromthebasicstoimplementaproofofconcept.
Serversiderendering
106
FeelfreetoskipthisparagraphifyoujustwanttolookintothelibrariesthatprovideSSRandnotbotherwiththegroundwork
ToimplementbasicSSRwe'regoingtouseExpress.
IfyouarenewtoExpress,orneedsomecatch-up,checkoutmyfreeExpressHandbookhere:https://flaviocopes.com/page/ebooks/.
Warning:thecomplexityofSSRcangrowwiththecomplexityofyourapplication.ThisisthebareminimumsetuptorenderabasicReactapp.FormorecomplexneedsyoumightneedtodoabitmoreworkoralsocheckoutSSRlibrariesforReact.
IassumeyoustartedaReactappwith create-react-app.Ifyouarejusttrying,installonenowusing npxcreate-react-appssr.
Gotothemainappfolderwiththeterminal,thenrun:
npminstallexpress
Youhaveasetoffoldersinyourappdirectory.Createanewfoldercalled server,thengointoitandcreateafilenamed server.js.
Followingthe create-react-appconventions,theapplivesinthe src/App.jsfile.We'regoingtoloadthatcomponent,andrenderittoastringusingReactDOMServer.renderToString(),whichisprovidedby react-dom.
Yougetthecontentsofthe ./build/index.htmlfile,andreplacethe <divid="root"></div>placeholder,whichisthetagwheretheapplicationhooksbydefault,with ̀ <div
id="root">\${ReactDOMServer.renderToString(<App/>)}</div>.
Allthecontentinsidethe buildfolderisgoingtobeservedas-is,staticallybyExpress.
importpathfrom'path'
importfsfrom'fs'
importexpressfrom'express'
importReactfrom'react'
importReactDOMServerfrom'react-dom/server'
importAppfrom'../src/App'
constPORT=8080
constapp=express()
constrouter=express.Router()
constserverRenderer=(req,res,next)=>{
fs.readFile(path.resolve('./build/index.html'),'utf8',(err,data)=>{
if(err){
Serversiderendering
107
console.error(err)
returnres.status(500).send('Anerroroccurred')
}
returnres.send(
data.replace(
'<divid="root"></div>',
`<divid="root">${ReactDOMServer.renderToString(<App/>)}</div>`
)
)
})
}
router.use('^/$',serverRenderer)
router.use(
express.static(path.resolve(__dirname,'..','build'),{maxAge:'30d'})
)
//telltheapptousetheaboverules
app.use(router)
//app.use(express.static('./build'))
app.listen(PORT,()=>{
console.log(`SSRrunningonport${PORT}`)
})
Now,intheclientapplication,inyour src/index.js,insteadofcalling ReactDOM.render():
ReactDOM.render(<App/>,document.getElementById('root'))
call ReactDOM.hydrate(),whichisthesamebuthastheadditionalabilitytoattacheventlistenerstoexistingmarkuponceReactloads:
ReactDOM.hydrate(<App/>,document.getElementById('root'))
AlltheNode.jscodeneedstobetranspiledbyBabel,asserver-sideNode.jscodedoesnotknowanythingaboutJSX,norESModules(whichweuseforthe includestatements).
Installthese3packages:
npminstall@babel/register@babel/preset-env@babel/preset-reactignore-stylesexpress
ignore-stylesisaBabelutilitythatwilltellittoignoreCSSfilesimportedusingthe importsyntax.
Let'screateanentrypointin server/index.js:
require('ignore-styles')
Serversiderendering
108
require('@babel/register')({
ignore:[/(node_modules)/],
presets:['@babel/preset-env','@babel/preset-react']
})
require('./server')
BuildtheReactapplication,sothatthebuild/folderispopulated:
npmrunbuild
andlet'srunthis:
nodeserver/index.js
Isaidthisisasimplisticapproach,anditis:
itdoesnothandlerenderingimagescorrectlywhenusingimports,whichneedWebpackinordertowork(andwhichcomplicatestheprocessalot)itdoesnothandlepageheadermetadata,whichisessentialforSEOandsocialsharingpurposes(amongotherthings)
Sowhilethisisagoodexampleofusing ReactDOMServer.renderToString()andReactDOM.hydratetogetthisbasicserver-siderendering,it'snotenoughforrealworldusage.
ServerSideRenderingusinglibrariesSSRishardtodoright,andReacthasnode-factowaytoimplementit.
It'sstillverymuchdebatableifit'sworththetrouble,complicationandoverheadtogetthebenefits,ratherthanusingadifferenttechnologytoservethosepages.ThisdiscussiononReddithaslotsofopinionsinthatregard.
WhenServerSideRenderingisanimportantmatter,mysuggestionistorelyonpre-madelibrariesandtoolsthathavehadthisgoalinmindsincethebeginning.
Inparticular,IsuggestNext.jsandGatsby,twoprojectswe'llseelateron.
Serversiderendering
109
ContextAPITheContextAPIisaneatwaytopassstateacrosstheappwithouthavingtouseprops
TheContextAPIwasintroducedtoallowyoutopassstate(andenablethestatetoupdate)acrosstheapp,withouthavingtousepropsforit.
TheReactteamsuggeststosticktopropsifyouhavejustafewlevelsofchildrentopass,becauseit'sstillamuchlesscomplicatedAPIthantheContextAPI.
Inmanycases,itenablesustoavoidusingRedux,simplifyingourappsalot,andalsolearninghowtouseReact.
Howdoesitwork?
Youcreateacontextusing React.createContext(),whichreturnsaContextobject.:
const{Provider,Consumer}=React.createContext()
ThenyoucreateawrappercomponentthatreturnsaProvidercomponent,andyouaddaschildrenallthecomponentsfromwhichyouwanttoaccessthecontext:
classContainerextendsReact.Component{
constructor(props){
super(props)
this.state={
something:'hey'
}
}
render(){
return(
<Providervalue={{state:this.state}}>{this.props.children}</Provider>
)
}
}
classHelloWorldextendsReact.Component{
render(){
return(
<Container>
<Button/>
</Container>
)
}
}
ContextAPI
110
IusedContainerasthenameofthiscomponentbecausethiswillbeaglobalprovider.Youcanalsocreatesmallercontexts.
Insideacomponentthat'swrappedinaProvider,youuseaConsumercomponenttomakeuseofthecontext:
classButtonextendsReact.Component{
render(){
return(
<Consumer>
{context=><button>{context.state.something}</button>}
</Consumer>
)
}
}
YoucanalsopassfunctionsintoaProvidervalue,andthosefunctionswillbeusedbytheConsumertoupdatethecontextstate:
<Providervalue={{
state:this.state,
updateSomething:()=>this.setState({something:'ho!'})
{this.props.children}
</Provider>
/*...*/
<Consumer>
{(context)=>(
<buttononClick={context.updateSomething}>{context.state.something}</button>
)}
</Consumer>
YoucanseethisinactioninthisGlitch.
Youcancreatemultiplecontexts,tomakeyourstatedistributedacrosscomponents,yetexposeitandmakeitreachablebyanycomponentyouwant.
Whenusingmultiplefiles,youcreatethecontentinonefile,andimportitinalltheplacesyouuseit:
//context.js
importReactfrom'react'
exportdefaultReact.createContext()
//component1.js
importContextfrom'./context'
//...useContext.Provider
//component2.js
importContextfrom'./context'
ContextAPI
111
//...useContext.Consumer
ContextAPI
112
Higher-ordercomponentsFindoutwhatHigherOrderComponentsareandhowtheyareusefulwhenprogrammingaReactapplication
YoumightbefamiliarwithHigherOrderFunctionsinJavaScript.Thosearefunctionsthatacceptfunctionsasarguments,and/orreturnfunctions.
Twoexamplesofthosefunctionsare Array.map()or Array.filter().
InReact,weextendthisconcepttocomponents,andsowehaveaHigherOrderComponent(HOC)whenthecomponentacceptsacomponentasinputandreturnsacomponentasitsoutput.
Ingeneral,higherordercomponentsallowyoutocreatecodethat'scomposableandreusable,andalsomoreencapsulated.
WecanuseaHOCtoaddmethodsorpropertiestothestateofacomponent,oraReduxstoreforexample.
YoumightwanttouseHigherOrderComponentswhenyouwanttoenhanceanexistingcomponent,operateonthestateorprops,oritsrenderedmarkup.
ThereisaconventionofprependingaHigherOrderComponentwiththe withstring(it'saconvention,soit'snotmandatory),soifyouhavea Buttoncomponent,itsHOCcounterpartshouldbecalled withButton.
Let'screateone.
ThesimplestexampleeverofaHOCisonethatsimplyreturnsthecomponentunaltered:
constwithButton=Button=>()=><Button/>
Let'smakethisalittlebitmoreusefulandaddapropertytothatbutton,inadditiontoallthepropsitalreadycamewith,thecolor:
constwithButton=Element=>props=><Button{...props}color="red"/>
WeusethisHOCinacomponentJSX:
constButton=()=>{
return<button>test</button>
}
constWrappedButton=withButton(Button)
Higher-ordercomponents
113
andwecanfinallyrendertheWrappedButtoncomponentinourappJSX:
functionApp(){
return(
<divclassName="App">
<h1>Hello</h1>
<WrappedButton/>
</div>
)
}
ThisisaverysimpleexamplebuthopefullyyoucangetthegistofHOCsbeforeapplyingthoseconceptstomorecomplexscenarios.
Higher-ordercomponents
114
RenderPropsLearnhowRenderPropscanhelpyoubuildaReactapplication
Acommonpatternusedtosharestatebetweencomponentsistousethe childrenprop.
InsideacomponentJSXyoucanrender {this.props.children}whichautomaticallyinjectsanyJSXpassedintheparentcomponentasachildren:
classParentextendsReact.Component{
constructor(props){
super(props)
this.state={
/*...*/
}
}
render(){
return<div>{this.props.children}</div>
}
}
constChildren1=()=>{}
constChildren2=()=>{}
constApp=()=>(
<Parent>
<Children1/>
<Children2/>
</Parent>
)
However,thereisaproblemhere:thestateoftheparentcomponentcannotbeaccessedfromthechildren.
Tobeabletosharethestate,youneedtousearenderpropcomponent,andinsteadofpassingcomponentsaschildrenoftheparentcomponent,youpassafunctionwhichyouthenexecutein {this.props.children()}.Thefunctioncanacceptarguments,:
classParentextendsReact.Component{
constructor(props){
super(props)
this.state={name:'Flavio'}
}
render(){
return<div>{this.props.children(this.state.name)}</div>
}
}
RenderProps
115
constChildren1=props=>{
return<p>{props.name}</p>
}
constApp=()=><Parent>{name=><Children1name={name}/>}</Parent>
Insteadofusingthe childrenprop,whichhasaveryspecificmeaning,youcanuseanyprop,andsoyoucanusethispatternmultipletimesonthesamecomponent:
classParentextendsReact.Component{
constructor(props){
super(props)
this.state={name:'Flavio',age:35}
}
render(){
return(
<div>
<p>Test</p>
{this.props.someprop1(this.state.name)}
{this.props.someprop2(this.state.age)}
</div>
)
}
}
constChildren1=props=>{
return<p>{props.name}</p>
}
constChildren2=props=>{
return<p>{props.age}</p>
}
constApp=()=>(
<Parent
someprop1={name=><Children1name={name}/>}
someprop2={age=><Children2age={age}/>}
/>
)
ReactDOM.render(<App/>,document.getElementById('app'))
RenderProps
116
HooksLearnhowHookscanhelpyoubuildaReactapplication
HooksisafeaturethatwillbeintroducedinReact16.7,andisgoingtochangehowwewriteReactappsinthefuture.
BeforeHooksappeared,somekeythingsincomponentswereonlypossibleusingclasscomponents:havingtheirownstate,andusinglifecycleevents.Functioncomponents,lighterandmoreflexible,werelimitedinfunctionality.
Hooksallowfunctioncomponentstohavestateandtorespondtolifecycleeventstoo,andkindofmakeclasscomponentsobsolete.Theyalsoallowfunctioncomponentstohaveagoodwaytohandleevents.
AccessstateUsingthe useState()API,youcancreateanewstatevariable,andhaveawaytoalterit.useState()acceptstheinitialvalueofthestateitemandreturnsanarraycontainingthestatevariable,andthefunctionyoucalltoalterthestate.Sinceitreturnsanarrayweusearraydestructuringtoaccesseachindividualitem,likethis: const[count,setCount]=useState(0)
Here'sapracticalexample:
import{useState}from'react'
constCounter=()=>{
const[count,setCount]=useState(0)
return(
<div>
<p>Youclicked{count}times</p>
<buttononClick={()=>setCount(count+1)}>Clickme</button>
</div>
)
}
ReactDOM.render(<Counter/>,document.getElementById('app'))
Youcanaddasmany useState()callsyouwant,tocreateasmanystatevariablesasyouwant.Justmakesureyoucallitinthetoplevelofacomponent(notinan iforinanyotherblock).
ExampleonCodepen:
Hooks
117
SeethePenReactHooksexample#1counterbyFlavioCopes(@flaviocopes)onCodePen.
AccesslifecyclehooksAnotherveryimportantfeatureofHooksisallowingfunctioncomponentstohaveaccesstothelifecyclehooks.
Usingclasscomponentsyoucanregisterafunctiononthe componentDidMount,componentWillUnmountand componentDidUpdateevents,andthosewillservemanyusecases,fromvariablesinitializationtoAPIcallstocleanup.
Hooksprovidethe useEffect()API.Thecallacceptsafunctionasargument.
Thefunctionrunswhenthecomponentisfirstrendered,andoneverysubsequentre-render/update.ReactfirstupdatestheDOM,thencallsanyfunctionpassedto useEffect().AllwithoutblockingtheUIrenderingevenonblockingcode,unliketheold componentDidMountand componentDidUpdate,whichmakesourappsfeelfaster.
Example:
const{useEffect,useState}=React
constCounterWithNameAndSideEffect=()=>{
const[count,setCount]=useState(0)
const[name,setName]=useState('Flavio')
useEffect(()=>{
console.log(`Hi${name}youclicked${count}times`)
})
return(
<div>
<p>
Hi{name}youclicked{count}times
</p>
<buttononClick={()=>setCount(count+1)}>Clickme</button>
<buttononClick={()=>setName(name==='Flavio'?'Roger':'Flavio')}>
Changename
</button>
</div>
)
}
ReactDOM.render(
<CounterWithNameAndSideEffect/>,
document.getElementById('app')
)
Hooks
118
Thesame componentWillUnmountjobcanbeachievedbyoptionallyreturningafunctionfromour useEffect()parameter:
useEffect(()=>{
console.log(`Hi${name}youclicked${count}times`)
return()=>{
console.log(`Unmounted`)
}
})
useEffect()canbecalledmultipletimes,whichisnicetoseparateunrelatedlogic(somethingthatplaguestheclasscomponentlifecycleevents).
Sincethe useEffect()functionsarerunoneverysubsequentre-render/update,wecantellReacttoskiparun,forperformancepurposes,byaddingasecondparameterwhichisanarraythatcontainsalistofstatevariablestowatchfor.Reactwillonlyre-runthesideeffectifoneoftheitemsinthisarraychanges.
useEffect(
()=>{
console.log(`Hi${name}youclicked${count}times`)
},
[name,count]
)
SimilarlyyoucantellReacttoonlyexecutethesideeffectonce(atmounttime),bypassinganemptyarray:
useEffect(()=>{
console.log(`Componentmounted`)
},[])
useEffect()isgreatforaddinglogs,accessing3rdpartyAPIsandmuchmore.
ExampleonCodepen:
SeethePenReactHooksexample#3sideeffectsbyFlavioCopes(@flaviocopes)onCodePen.
HandleeventsinfunctioncomponentsBeforehooks,youeitherusedclasscomponents,oryoupassedaneventhandlerusingprops.
Nowwecanusethe useCallback()built-inAPI:
Hooks
119
constButton=()=>{
consthandleClick=useCallback(()=>{
//...dosomething
})
return<buttononClick={handleClick}/>
}
AnyparameterusedinsidethefunctionmustbepassedthroughasecondparametertouseCallback(),inanarray:
constButton=()=>{
letname=''//...addlogic
consthandleClick=useCallback(
()=>{
//...dosomething
},
[name]
)
return<buttononClick={handleClick}/>
}
Enablecross-componentcommunicationusingcustomhooksTheabilitytowriteyourownhooksisthefeaturethatisgoingtosignificantlyalterhowyouwriteReactappsinthefuture.
Usingcustomhooksyouhaveonemorewaytosharestateandlogicbetweencomponents,addingasignificantimprovementtothepatternsofrenderpropsandhigherordercomponents.Whicharestillgreat,butnowwithcustomhookshavelessrelevanceinmanyusecases.
Howdoyoucreateacustomhook?
Ahookisjustafunctionthatconventionallystartswith use.Itcanacceptanarbitrarynumberofarguments,andreturnanythingitwants.
Examples:
constuseGetData(){
//...
returndata
}
or
Hooks
120
constuseGetUser(username){
//...constuser=fetch(...)
//...constuserData=...
return[user,userData]
}
Inyourowncomponents,youcanusethehooklikethis:
constMyComponent=()=>{
constdata=useGetData()
const[user,userData]=useGetUser('flavio')
//...
}
Whenexactlytoaddhooksinsteadofregularfunctionsshouldbedeterminedonausecasebasis,andonlyexperiencewilltell.
Hooks
121
CodesplittingWhatisCodeSplittingandhowtointroduceitinaReactapp
ModernJavaScriptapplicationscanbequitehugeintermsofbundlesize.Youdon'twantyouruserstohavetodownloada1MBpackageofJavaScript(yourcodeandthelibrariesyouuse)justtoloadthefirstpage,right?ButthisiswhathappensbydefaultwhenyoushipamodernWebAppbuiltwithWebpackbundling.
Thatbundlewillcontaincodethatmightneverrunbecausetheuseronlystopsontheloginpageandneverseestherestofyourapp.
CodesplittingisthepracticeofonlyloadingtheJavaScriptyouneedthemomentwhenyouneedit.
Thisimproves:
theperformanceofyourapptheimpactonmemory,andsobatteryusageonmobiledevicesthedownloadedKiloBytes(orMegaBytes)size
React16.6.0,releasedinOctober2018,introducedawayofperformingcodesplittingthatshouldtaketheplaceofeverypreviouslyusedtoolorlibrary:React.lazyandSuspense.
React.lazyand Suspenseformtheperfectwaytolazilyloadadependencyandonlyloaditwhenneeded.
Let'sstartwith React.lazy.Youuseittoimportanycomponent:
importReactfrom'react'
constTodoList=React.lazy(()=>import('./TodoList'))
exportdefault()=>{
return(
<div>
<TodoList/>
</div>
)
}
theTodoListcomponentwillbedynamicallyaddedtotheoutputassoonasit'savailable.Webpackwillcreateaseparatebundleforit,andwilltakecareofloadingitwhennecessary.
Suspenseisacomponentthatyoucanusetowrapanylazilyloadedcomponent:
Codesplitting
122
importReactfrom'react'
constTodoList=React.lazy(()=>import('./TodoList'))
exportdefault()=>{
return(
<div>
<React.Suspense>
<TodoList/>
</React.Suspense>
</div>
)
}
Ittakescareofhandlingtheoutputwhilethelazyloadedcomponentisfetchedandrendered.
Useits fallbackproptooutputsomeJSXoracomponentoutput:
...
<React.Suspensefallback={<p>Pleasewait</p>}>
<TodoList/>
</React.Suspense>
...
AllthisplayswellwithReactRouter:
importReactfrom'react'
import{BrowserRouterasRouter,Route,Switch}from'react-router-dom'
constTodoList=React.lazy(()=>import('./routes/TodoList'))
constNewTodo=React.lazy(()=>import('./routes/NewTodo'))
constApp=()=>(
<Router>
<React.Suspensefallback={<p>Pleasewait</p>}>
<Switch>
<Routeexactpath="/"component={TodoList}/>
<Routepath="/new"component={NewTodo}/>
</Switch>
</React.Suspense>
</Router>
)
Codesplitting
123
CSSinReactHowtouseCSStostyleaReactapplication
UsingReactyouhavevariouswaystoaddstylingtoyourcomponents.
UsingclassesandCSSThefirstandmostsimpleistouseclasses,anduseanormalCSSfiletotargetthoseclasses:
constButton=()=>{
return<buttonclassName="button">Abutton</button>
}
.button{
background-color:yellow;
}
Youcanimportthestylesheetusinganimportstatement,likethis:
import'./style.css'
andWebpackwilltakecareofaddingtheCSSpropertytothebundle.
UsingthestyleattributeAsecondmethodistousethe styleattributeattachedtoaJSXelement.Usingthisapproachyoudon'tneedaseparateCSSfile.
constButton=()=>{
return<buttonstyle={{backgroundColor:'yellow'}}>Abutton</button>
}
CSSisdefinedinaslightlydifferentwaynow.First,noticethedoublecurlybrackets:it'sbecause styleacceptsanobject.WepassinaJavaScriptobject,whichisdefinedincurlybraces.Wecouldalsodothis:
constbuttonStyle={backgroundColor:'yellow'}
constButton=()=>{
return<buttonstyle={buttonStyle}>Abutton</button>
CSSinReact
124
}
Whenusing create-react-app,thosestylesareautoprefixedbydefaultthankstoitsuseofAutoprefixer.
Also,thestylenowiscamelCasedinsteadofusingdashes.EverytimeaCSSpropertyhasadash,removeitandstartthenextwordcapitalized.
Styleshavethebenefitofbeinglocaltothecomponent,andtheycannotleaktoothercomponentsinotherpartsoftheapp,somethingthatusingclassesandanexternalCSSfilecan'tprovide.
UsingCSSModulesCSSModulesseemtobeaperfectspotinthemiddle:youuseclasses,butCSSisscopedtothecomponent,whichmeansthatanystylingyouaddcannotbeappliedtoothercomponentswithoutyourpermission.AndyetyourstylesaredefinedinaseparateCSSfile,whichiseasiertomaintainthanCSSinJavaScript(andyoucanuseyourgoodoldCSSpropertynames).
StartbycreatingaCSSfilethatendswith .module.css,forexample Button.module.css.Agreatchoiceistogiveitthesamenameasthecomponentyouaregoingtostyle
AddyourCSShere,thenimportitinsidethecomponentfileyouwanttostyle:
importstylefrom'./Button.module.css'
nowyoucanuseitinyourJSX:
constButton=()=>{
return<buttonclassName={style.content}>Abutton</button>
}
That'sit!Intheresultingmarkup,Reactwillgenerateaspecific,uniqueclassforeachrenderedcomponent,andassigntheCSStothatclass,sothattheCSSisnotaffectingothermarkup.
CSSinReact
125
CSSinReact
126
SASSwithReactHowtouseSASStostyleaReactapplication
WhenyoubuildaReactapplicationusing create-react-app,youhavemanyoptionsatyourdisposalwhenitcomestostyling.
Ofcourse,ifnotusing create-react-app,youhaveallthechoicesintheworld,butwelimitthediscussiontothe create-react-app-providedoptions.
YoucanstyleusingplainclassesandCSSfiles,usingthestyleattributeorCSSModules,tostartwith.
SASS/SCSSisaverypopularoption,amuchlovedonebymanydevelopers.
Youcanuseitwithoutanyconfigurationatall,startingwith create-react-app2.
Allyouneedisa .sassor .scssfile,andyoujustimportitinacomponent:
import'./styles.scss'
Youcanseeanexampleofitworkingathttps://codesandbox.io/s/18qq31rp3.
SASSwithReact
127
StyledComponentsStyledComponentsareoneofthenewwaystouseCSSinmodernJavaScript.ItisthemeanttobeasuccessorofCSSModules,awaytowriteCSSthat'sscopedtoasinglecomponent,andnotleaktoanyotherelementinthepage
AbriefhistoryOnceuponatime,theWebwasreallysimpleandCSSdidn'tevenexist.Welaidoutpagesusingtablesandframes.Goodtimes.
ThenCSScametolife,andaftersometimeitbecameclearthatframeworkscouldgreatlyhelpespeciallyinbuildinggridsandlayouts,BootstrapandFoundationplayingabigpartinthis.
PreprocessorslikeSASSandothershelpedalottoslowdowntheadoptionofframeworks,andtobetterorganizethecode,conventionslikeBEMandSMACSSgrewinuse,especiallywithinteams.
Conventionsarenotasolutiontoeverything,andtheyarecomplextoremember,sointhelastfewyearswiththeincreasingadoptionofJavaScriptandbuildprocessesineveryfrontendproject,CSSfounditswayintoJavaScript(CSS-in-JS).
NewtoolsexplorednewwaysofdoingCSS-in-JSandafewsucceededwithincreasingpopularity:
ReactStylejsxstyleRadium
andmore.
IntroducingStyledComponentsOneofthemostpopularofthesetoolsisStyledComponents.
ItisthemeanttobeasuccessortoCSSModules,awaytowriteCSSthat'sscopedtoasinglecomponent,andnotleaktoanyotherelementinthepage.
(moreonCSSmoduleshereandhere)
StyledComponents
128
StyledComponentsallowyoutowriteplainCSSinyourcomponentswithoutworryingaboutclassnamecollisions.
InstallationSimplyinstallstyled-componentsusingnpmoryarn:
npminstallstyled-components
yarnaddstyled-components
That'sit!Nowallyouhavetodoistoaddthisimport:
importstyledfrom'styled-components'
YourfirststyledcomponentWiththe styledobjectimported,youcannowstartcreatingStyledComponents.Here'sthefirstone:
constButton=styled.button`
font-size:1.5em;
background-color:black;
color:white;
`
ButtonisnowaReactComponentinallitsgreatness.
Wecreateditusingafunctionofthestyledobject,called buttoninthiscase,andpassingsomeCSSpropertiesinatemplateliteral.
NowthiscomponentcanberenderedinourcontainerusingthenormalReactsyntax:
render(<Button/>)
StyledComponentsofferotherfunctionsyoucanusetocreateothercomponents,notjustbutton,like section, h1, inputandmanyothers.
Thesyntaxused,withthebacktick,mightbeweirdatfirst,butit'scalledTaggedTemplates,it'splainJavaScriptandit'sawaytopassanargumenttothefunction.
Usingpropstocustomizecomponents
StyledComponents
129
WhenyoupasssomepropstoaStyledComponent,itwillpassthemdowntotheDOMnodemounted.
Forexamplehere'showwepassthe placeholderand typepropstoan inputcomponent:
constInput=styled.input`
//...
`
render(
<div>
<Inputplaceholder="..."type="text"/>
</div>
)
Thiswilldojustwhatyouthink,insertingthosepropsasHTMLattributes.
PropsinsteadofjustbeingblindlypasseddowntotheDOMcanalsobeusedtocustomizeacomponentbasedonthepropvalue.Here'sanexample:
constButton=styled.button`
background:${props=>(props.primary?'black':'white')};
color:${props=>(props.primary?'white':'black')};
`
render(
<div>
<Button>Anormalbutton</Button>
<Button>Anormalbutton</Button>
<Buttonprimary>Theprimarybutton</Button>
</div>
)
Settingthe primarypropchangesthecolorofthebutton.
ExtendinganexistingStyledComponentIfyouhaveonecomponentandyouwanttocreateasimilarone,juststyledslightlydifferently,youcanuse extend:
constButton=styled.button`
color:black;
//...
`
constWhiteButton=Button.extend`
color:white;
`
StyledComponents
130
render(
<div>
<Button>Ablackbutton,likeallbuttons</Button>
<WhiteButton>Awhitebutton</WhiteButton>
</div>
)
It'sRegularCSSInStyledComponents,youcanusetheCSSyoualreadyknowandlove.It'sjustplainCSS.ItisnotpseudoCSSnorinlineCSSwithitslimitations.
Youcanusemediaqueries,nestingandanythingelseyoumightneed.
UsingVendorPrefixesStyledComponentsautomaticallyaddallthevendorprefixesneeded,soyoudon'tneedtoworryaboutthisproblem.
ConclusionThat'sitforthisStyledComponentsintroduction!TheseconceptswillhelpyougetanunderstandingoftheconceptandhelpyougetupandrunningwiththiswayofusingCSSinJavaScript.
StyledComponents
131
BabelBabelisanawesomeentryintheWebDevelopertoolset.It'sanawesometool,andit’sbeenaroundforquitesometime,butnowadaysalmosteveryJavaScriptdeveloperreliesonit,andthiswillcontinuegoingon,becauseBabelisnowindispensableandhassolvedabigproblemforeveryone.
Babelisanawesometool,andit’sbeenaroundforquitesometime,butnowadaysalmosteveryJavaScriptdeveloperreliesonit,andthiswillcontinue,becauseBabelisnowindispensableandhassolvedabigproblemforeveryone.
Whichproblem?
TheproblemthateveryWebDeveloperhassurelyhad:afeatureofJavaScriptisavailableinthelatestreleaseofabrowser,butnotintheolderversions.OrmaybeChromeorFirefoximplementit,butSafariiOSandEdgedonot.
Forexample,ES6introducedthearrowfunction:
[1,2,3].map((n)=>n+1)
Whichisnowsupportedbyallmodernbrowsers.IE11doesnotsupportit,norOperaMini(HowdoIknow?BycheckingtheES6CompatibilityTable).
Sohowshouldyoudealwiththisproblem?Shouldyoumoveonandleavethosecustomerswitholder/incompatiblebrowsersbehind,orshouldyouwriteolderJavaScriptcodetomakeallyourusershappy?
EnterBabel.Babelisacompiler:ittakescodewritteninonestandard,andittranspilesittocodewrittenintoanotherstandard.
YoucanconfigureBabeltotranspilemodernES2017JavaScriptintoJavaScriptES5syntax:
[1,2,3].map(function(n){
returnn+1
})
Thismusthappenatbuildtime,soyoumustsetupaworkflowthathandlesthisforyou.Webpackisacommonsolution.
(P.S.ifallthisESthingsoundsconfusingtoyou,seemoreaboutESversionsintheECMAScriptguide)
Babel
132
InstallingBabelBabeliseasilyinstalledusingnpm,locallyinaproject:
npminstall--save-dev@babel/core@babel/cli
InthepastIrecommendedinstalling babel-cliglobally,butthisisnowdiscouragedbytheBabelmaintainers,becausebyusingitlocallyyoucanhavedifferentversionsofBabelineachproject,andalsocheckinginbabelinyourrepositoryisbetterforteamwork
Sincenpmnowcomeswith npx,locallyinstalledCLIpackagescanrunbytypingthecommandintheprojectfolder:
SowecanrunBabelbyjustrunning
npxbabelscript.js
AnexampleBabelconfigurationBabeloutoftheboxdoesnotdoanythinguseful,youneedtoconfigureitandaddplugins.
HereisalistofBabelplugins
Tosolvetheproblemwetalkedaboutintheintroduction(usingarrowfunctionsineverybrowser),wecanrun
npminstall--save-dev\
@babel/plugin-transform-es2015-arrow-functions
todownloadthepackageinthe node_modulesfolderofourapp,thenweneedtoadd
{
"plugins":["transform-es2015-arrow-functions"]
}
tothe .babelrcfilepresentintheapplicationrootfolder.Ifyoudon'thavethatfilealready,youjustcreateablankfile,andputthatcontentintoit.
TIP:Ifyouhaveneverseenadotfile(afilestartingwithadot)itmightbeoddatfirstbecausethatfilemightnotappearinyourfilemanager,asit'sahiddenfile.
Nowifwehavea script.jsfilewiththiscontent:
Babel
133
vara=()=>{};
vara=(b)=>b;
constdouble=[1,2,3].map((num)=>num*2);
console.log(double);//[2,4,6]
varbob={
_name:"Bob",
_friends:["Sally","Tom"],
printFriends(){
this._friends.forEach(f=>
console.log(this._name+"knows"+f));
}
};
console.log(bob.printFriends());
running babelscript.jswilloutputthefollowingcode:
vara=function(){};vara=function(b){
returnb;
};
constdouble=[1,2,3].map(function(num){
returnnum*2;
});console.log(double);//[2,4,6]
varbob={
_name:"Bob",
_friends:["Sally","Tom"],
printFriends(){
var_this=this;
this._friends.forEach(function(f){
returnconsole.log(_this._name+"knows"+f);
});
}
};
console.log(bob.printFriends());
AsyoucanseearrowfunctionshaveallbeenconvertedtoJavaScriptES5functions.
BabelpresetsWejustsawinthepreviousarticlehowBabelcanbeconfiguredtotranspilespecificJavaScriptfeatures.
Youcanaddmuchmoreplugins,butyoucan'taddtotheconfigurationfeaturesonebyone,it'snotpractical.
Babel
134
ThisiswhyBabelofferspresets.
Themostpopularpresetsare envand react.
Tip:Babel7deprecated(andremoved)yearlypresetslike preset-es2017,andstagepresets.Use @babel/preset-envinstead.
envpreset
The envpresetisverynice:youtellitwhichenvironmentsyouwanttosupport,anditdoeseverythingforyou,supportingallmodernJavaScriptfeatures.
E.g."supportthelast2versionsofeverybrowser,butforSafarilet'ssupportallversionssinceSafari7`
{
"presets":[
["env",{
"targets":{
"browsers":["last2versions","safari>=7"]
}
}]
]
}
or"Idon'tneedbrowsersupport,justletmeworkwithNode.js6.10"
{
"presets":[
["env",{
"targets":{
"node":"6.10"
}
}]
]
}
reactpreset
The reactpresetisveryconvenientwhenwritingReactapps:adding preset-flow, syntax-jsx, transform-react-jsx, transform-react-display-name.
Byincludingit,youareallreadytogodevelopingReactapps,withJSXtransformsandFlowsupport.
Moreinfoonpresets
Babel
135
https://babeljs.io/docs/plugins/
UsingBabelwithwebpackIfyouwanttorunmodernJavaScriptinthebrowser,Babelonitsownisnotenough,youalsoneedtobundlethecode.Webpackistheperfecttoolforthis.
TIP:readthewebpackguideifyou'renotfamiliarwithwebpack
ModernJSneedstwodifferentstages:acompilestage,andaruntimestage.ThisisbecausesomeES6+featuresneedapolyfilloraruntimehelper.
ToinstalltheBabelpolyfillruntimefunctionality,run
npminstall@babel/polyfill\
@babel/runtime\
@babel/plugin-transform-runtime
Nowinyour webpack.config.jsfileadd:
entry:[
'babel-polyfill',
//yourappscriptsshouldbehere
],
module:{
loaders:[
//BabelloadercompilesES2015intoES5for
//completecross-browsersupport
{
loader:'babel-loader',
test:/\.js$/,
//onlyincludefilespresentinthe`src`subdirectory
include:[path.resolve(__dirname,"src")],
//excludenode_modules,equivalenttotheaboveline
exclude:/node_modules/,
query:{
//UsethedefaultES2015preset
//toincludeallES2015features
presets:['es2015'],
plugins:['transform-runtime']
}
}
]
}
Bykeepingthepresetsandpluginsinformationinsidethe webpack.config.jsfile,wecanavoidhavinga .babelrcfile.
Babel
136
Babel
137
WebpackWebpackisatoolthathasgotalotofattentioninthelastfewyears,anditisnowseenusedinalmosteveryproject.Learnaboutit.
Whatiswebpack?WebpackisatoolthatletsyoucompileJavaScriptmodules,alsoknownasmodulebundler.
Givenalargenumberoffiles,itgeneratesasinglefile(orafewfiles)thatrunyourapp.
Itcanperformmanyoperations:
helpsyoubundleyourresources.watchesforchangesandre-runsthetasks.canrunBabeltranspilationtoES5,allowingyoutousethelatestJavaScriptfeatureswithoutworryingaboutbrowsersupport.cantranspileCoffeeScripttoJavaScriptcanconvertinlineimagestodataURIs.allowsyoutouserequire()forCSSfiles.
Webpack
138
canrunadevelopmentwebserver.canhandlehotmodulereplacement.cansplittheoutputfilesintomultiplefiles,toavoidhavingahugejsfiletoloadinthefirstpagehit.canperformtreeshaking.
Webpackisnotlimitedtobeuseonthefrontend,it'salsousefulinbackendNode.jsdevelopmentaswell.
Predecessorsofwebpack,andstillwidelyusedtools,include:
GruntBroccoliGulp
TherearelotsofsimilaritiesinwhatthoseandWebpackcando,butthemaindifferenceisthatthoseareknownastaskrunners,whilewebpackwasbornasamodulebundler.
It'samorefocusedtool:youspecifyanentrypointtoyourapp(itcouldevenbeanHTMLfilewithscripttags)andwebpackanalyzesthefilesandbundlesallyouneedtoruntheappinasingleJavaScriptoutputfile(orinmorefilesifyouusecodesplitting).
InstallingwebpackWebpackcanbeinstalledgloballyorlocallyforeachproject.
Globalinstall
Here'showtoinstallitgloballywithYarn:
yarnglobaladdwebpackwebpack-cli
withnpm:
npmi-gwebpackwebpack-cli
oncethisisdone,youshouldbeabletorun
webpack-cli
Webpack
139
Localinstall
Webpackcanbeinstalledlocallyaswell.It'stherecommendedsetup,becausewebpackcanbeupdatedper-project,andyouhavelessresistancetousingthelatestfeaturesjustforasmallprojectratherthanupdatingalltheprojectsyouhavethatusewebpack.
WithYarn:
yarnaddwebpackwebpack-cli-D
withnpm:
npmiwebpackwebpack-cli--save-dev
Oncethisisdone,addthistoyour package.jsonfile:
{
//...
"scripts":{
"build":"webpack"
}
}
oncethisisdone,youcanrunwebpackbytyping
Webpack
140
yarnbuild
intheprojectroot.
WebpackconfigurationBydefault,webpack(startingfromversion4)doesnotrequireanyconfigifyourespecttheseconventions:
theentrypointofyourappis ./src/index.jstheoutputisputin ./dist/main.js.Webpackworksinproductionmode
Youcancustomizeeverylittlebitofwebpackofcourse,whenyouneed.Thewebpackconfigurationisstoredinthe webpack.config.jsfile,intheprojectrootfolder.
TheentrypointBydefaulttheentrypointis ./src/index.jsThissimpleexampleusesthe ./index.jsfileasastartingpoint:
module.exports={
/*...*/
entry:'./index.js'
/*...*/
}
TheoutputBydefaulttheoutputisgeneratedin ./dist/main.js.Thisexampleputstheoutputbundleinto app.js:
module.exports={
/*...*/
output:{
path:path.resolve(__dirname,'dist'),
filename:'app.js'
}
/*...*/
}
Webpack
141
LoadersUsingwebpackallowsyoutouse importor requirestatementsinyourJavaScriptcodetonotjustincludeotherJavaScript,butanykindoffile,forexampleCSS.
Webpackaimstohandleallourdependencies,notjustJavaScript,andloadersareonewaytodothat.
Forexample,inyourcodeyoucanuse:
import'style.css'
byusingthisloaderconfiguration:
module.exports={
/*...*/
module:{
rules:[
{test:/\.css$/,use:'css-loader'},
}]
}
/*...*/
}
TheregularexpressiontargetsanyCSSfile.
Aloadercanhaveoptions:
module.exports={
/*...*/
module:{
rules:[
{
test:/\.css$/,
use:[
{
loader:'css-loader',
options:{
modules:true
}
}
]
}
]
}
/*...*/
}
Youcanrequiremultipleloadersforeachrule:
Webpack
142
module.exports={
/*...*/
module:{
rules:[
{
test:/\.css$/,
use:
[
'style-loader',
'css-loader',
]
}
]
}
/*...*/
}
Inthisexample, css-loaderinterpretsthe import'style.css'directiveintheCSS. style-loaderisthenresponsibleforinjectingthatCSSintheDOM,usinga <style>tag.
Theordermatters,andit'sreversed(thelastisexecutedfirst).
Whatkindofloadersarethere?Many!Youcanfindthefulllisthere.
AcommonlyusedloaderisBabel,whichisusedtotranspilemodernJavaScripttoES5code:
module.exports={
/*...*/
module:{
rules:[
{
test:/\.js$/,
exclude:/(node_modules|bower_components)/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
}
}
]
}
/*...*/
}
ThisexamplemakesBabelpreprocessallourReact/JSXfiles:
module.exports={
/*...*/
module:{
rules:[
{
Webpack
143
test:/\.(js|jsx)$/,
exclude:/node_modules/,
use:'babel-loader'
}
]
},
resolve:{
extensions:[
'.js',
'.jsx'
]
}
/*...*/
}
Seethe babel-loaderoptionshere.
PluginsPluginsarelikeloaders,butonsteroids.Theycandothingsthatloaderscan'tdo,andtheyarethemainbuildingblockofwebpack.
Takethisexample:
module.exports={
/*...*/
plugins:[
newHTMLWebpackPlugin()
]
/*...*/
}
The HTMLWebpackPluginpluginhasthejobofautomaticallycreatinganHTMLfile,addingtheoutputJSbundlepath,sotheJavaScriptisreadytobeserved.
Therearelotsofpluginsavailable.
Oneusefulplugin, CleanWebpackPlugin,canbeusedtoclearthe dist/folderbeforecreatinganyoutput,soyoudon'tleavefilesaroundwhenyouchangethenameoftheoutputfile:
module.exports={
/*...*/
plugins:[
newCleanWebpackPlugin(['dist']),
]
/*...*/
}
Webpack
144
ThewebpackmodeThismode(introducedinwebpack4)setstheenvironmentonwhichwebpackworks.Itcanbesetto developmentor production(defaultstoproduction,soyouonlysetitwhenmovingtodevelopment)
module.exports={
entry:'./index.js',
mode:'development',
output:{
path:path.resolve(__dirname,'dist'),
filename:'app.js'
}
}
Developmentmode:
buildsveryfastislessoptimizedthanproductiondoesnotremovecommentsprovidesmoredetailederrormessagesandsuggestionsprovidesabetterdebuggingexperience
Productionmodeisslowertobuild,sinceitneedstogenerateamoreoptimizedbundle.TheresultingJavaScriptfileissmallerinsize,asitremovesmanythingsthatarenotneededinproduction.
Imadeasampleappthatjustprintsa console.logstatement.
Here'stheproductionbundle:
Webpack
145
Here'sthedevelopmentbundle:
Webpack
146
RunningwebpackWebpackcanberunfromthecommandlinemanuallyifinstalledglobally,butgenerallyyouwriteascriptinsidethe package.jsonfile,whichisthenrunusing npmor yarn.
Forexamplethis package.jsonscriptsdefinitionweusedbefore:
"scripts":{
"build":"webpack"
}
allowsustorun webpackbyrunning
npmrunbuild
or
yarnrunbuild
Webpack
147
orsimply
yarnbuild
WatchingchangesWebpackcanautomaticallyrebuildthebundlewhenachangeinyourapphappens,andkeeplisteningforthenextchange.
Justaddthisscript:
"scripts":{
"watch":"webpack--watch"
}
andrun
npmrunwatch
or
yarnrunwatch
orsimply
yarnwatch
Onenicefeatureofthewatchmodeisthatthebundleisonlychangedifthebuildhasnoerrors.Ifthereareerrors, watchwillkeeplisteningforchanges,andtrytorebuildthebundle,butthecurrent,workingbundleisnotaffectedbythoseproblematicbuilds.
HandlingimagesWebpackallowsustouseimagesinaveryconvenientway,usingthe file-loaderloader.
Thissimpleconfiguration:
module.exports={
/*...*/
module:{
rules:[
Webpack
148
{
test:/\.(png|svg|jpg|gif)$/,
use:[
'file-loader'
]
}
]
}
/*...*/
}
AllowsyoutoimportimagesinyourJavaScript:
importIconfrom'./icon.png'
constimg=newImage()
img.src=Icon
element.appendChild(img)
( imgisanHTMLImageElement.ChecktheImagedocs)
file-loadercanhandleotherassettypesaswell,likefonts,CSVfiles,xml,andmore.
Anothernicetooltoworkwithimagesisthe url-loaderloader.
ThisexampleloadsanyPNGfilesmallerthan8KBasadataURL.
module.exports={
/*...*/
module:{
rules:[
{
test:/\.png$/,
use:[
{
loader:'url-loader',
options:{
limit:8192
}
}
]
}
]
}
/*...*/
}
ProcessyourSASScodeandtransformittoCSS
Webpack
149
Using sass-loader, css-loaderand style-loader:
module.exports={
/*...*/
module:{
rules:[
{
test:/\.scss$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
/*...*/
}
GenerateSourceMapsSincewebpackbundlesthecode,SourceMapsaremandatorytogetareferencetotheoriginalfilethatraisedanerror,forexample.
Youtellwebpacktogeneratesourcemapsusingthe devtoolpropertyoftheconfiguration:
module.exports={
/*...*/
devtool:'inline-source-map',
/*...*/
}
devtoolhasmanypossiblevalues,themostusedprobablyare:
none:addsnosourcemapssource-map:idealforproduction,providesaseparatesourcemapthatcanbeminimized,andaddsareferenceintothebundle,sodevelopmenttoolsknowthatthesourcemapisavailable.Ofcourseyoushouldconfiguretheservertoavoidshippingthis,andjustuseitfordebuggingpurposesinline-source-map:idealfordevelopment,inlinesthesourcemapasaDataURL
Webpack
150
PrettierPrettierisanopinionatedcodeformatter.Itisagreatwaytokeepcodeformattedconsistentlyforyouandyourteam,andsupportsalotofdifferentlanguagesoutofthebox
Prettierisanopinionatedcodeformatter.
Itsupportsalotofdifferentsyntaxoutofthebox,including:
JavaScript
Prettier
151
Flow,TypeScriptCSS,SCSS,LessJSXGraphQLJSONMarkdown
andwithpluginsyoucanuseitforPython,PHP,Swift,Ruby,Javaandmore.
Itintegrateswiththemostpopularcodeeditors,includingVSCode,SublimeText,Atomandmore.
Prettierishugelypopular,asinFebruary2018ithasbeendownloadedover3.5milliontimes.
ThemostimportantlinksyouneedtoknowmoreaboutPrettierare
https://prettier.io/https://github.com/prettier/prettierhttps://www.npmjs.com/package/prettier
LessoptionsIlearnedGorecentlyandoneofthebestthingsaboutGoisgofmt,anofficialtoolthatautomaticallyformatsyourcodeaccordingtocommonstandards.
95%(madeupstat)oftheGocodearoundlooksexactlythesame,becausethistoolcanbeeasilyenforcedandsincethestyleisdefinedforyoubytheGomaintainers,youaremuchmorelikelytoadapttothatstandardinsteadofinsistingonyourownstyle.Liketabsvsspaces,orwheretoputanopeningbracket.
Thismightsoundlikealimitation,butit'sactuallyverypowerful.AllGocodelooksthesame.
Prettieristhe gofmtfortherestoftheworld.
Ithasveryfewoptions,andmostofthedecisionsarealreadytakenforyousoyoucanstoparguingaboutstyleandlittlethings,andfocusonyourcode.
DifferencewithESLintESLintisalinter,itdoesnotjustformat,butitalsohighlightssomeerrorsthankstoitsstaticanalysisofthecode.
ItisaninvaluabletoolanditcanbeusedalongsidePrettier.
Prettier
152
ESLintalsohighlightsformattingissues,butsinceit'salotmoreconfigurable,everyonecouldhaveadifferentsetofformattingrules.Prettierprovidesacommongroundforall.
Now,thereareafewthingsyoucancustomize,like:
thetabwidththeuseofsinglequotesvsdoublequotesthelinecolumnsnumbertheuseoftrailingcommas
andsomeothers,butPrettiertriestokeepthenumberofthosecustomizationsundercontrol,toavoidbecomingtoocustomizable.
InstallationPrettiercanrunfromthecommandline,andyoucaninstallitusingYarnornpm.
AnothergreatusecaseforPrettieristorunitonPRsforyourGitrepositories,forexampleonGitHub.
IfyouuseasupportededitorthebestthingistousePrettierdirectlyfromtheeditor,andthePrettierformattingwillberuneverytimeyousave.
ForexamplehereisthePrettierextensionforVSCode:https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
PrettierforbeginnersIfyouthinkPrettierisjustforteams,orforprousers,youaremissingagoodvaluepropositionofthistool.
Agoodstyleenforcesgoodhabits.
Formattingisatopicthat'smostlyoverlookedbybeginners,buthavingcleanandconsistentformattingiskeytoyoursuccessasanewdeveloper.
Also,evenifyoustartedusingJavaScript2weeksago,withPrettieryourcode-stylewise-willlookjustlikecodewrittenfromaJavaScriptGuruwritingJSsince1998.
Prettier
153
IntroductiontoJestJestisalibraryfortestingJavaScriptcode.It'sanopensourceprojectmaintainedbyFacebook,andit'sespeciallywellsuitedforReactcodetesting,althoughnotlimitedtothat:itcantestanyJavaScriptcode.Jestisveryfastandeasytouse
JestisalibraryfortestingJavaScriptcode.
It'sanopensourceprojectmaintainedbyFacebook,andit'sespeciallywellsuitedforReactcodetesting,althoughnotlimitedtothat:itcantestanyJavaScriptcode.Itsstrengthsare:
it'sfastitcanperformsnapshottestingit'sopinionated,andprovideseverythingoutoftheboxwithoutrequiringyoutomakechoices
JestisatoolverysimilartoMocha,althoughtheyhavedifferences:
Mochaislessopinionated,whileJesthasacertainsetofconventionsMocharequiresmoreconfiguration,whileJestworksusuallyoutofthebox,thankstobeingopinionated
IntroductiontoJest
154
Mochaisolderandmoreestablished,withmoretoolingintegrations
InmyopinionthebiggestfeatureofJestisit'sanoutoftheboxsolutionthatworkswithouthavingtointeractwithothertestinglibrariestoperformitsjob.
InstallationJestisautomaticallyinstalledin create-react-app,soifyouusethat,youdon'tneedtoinstallJest.
JestcanbeinstalledinanyotherprojectusingYarn:
yarnadd--devjest
ornpm:
npminstall--save-devjest
noticehowweinstructbothtoputJestinthe devDependenciespartofthe package.jsonfile,sothatitwillonlybeinstalledinthedevelopmentenvironmentandnotinproduction.
Addthislinetothescriptspartofyour package.jsonfile:
{
"scripts":{
"test":"jest"
}
}
sothattestscanberunusing yarntestor npmruntest.
Alternatively,youcaninstallJestglobally:
yarnglobaladdjest
andrunallyourtestsusingthe jestcommandlinetool.
CreatethefirstJesttestProjectscreatedwith create-react-apphaveJestinstalledandpreconfiguredoutofthebox,butaddingJesttoanyprojectisaseasyastyping
IntroductiontoJest
155
yarnadd--devjest
Addtoyour package.jsonthisline:
{
"scripts":{
"test":"jest"
}
}
andrunyourtestsbyexecuting yarntestinyourshell.
Now,youdon'thaveanytestshere,sonothingisgoingtobeexecuted:
Let'screatethefirsttest.Opena math.jsfileandtypeacouplefunctionsthatwe'lllatertest:
constsum=(a,b)=>a+b
constmul=(a,b)=>a*b
constsub=(a,b)=>a-b
constdiv=(a,b)=>a/b
exportdefault{sum,mul,sub,div}
Nowcreatea math.test.jsfile,inthesamefolder,andtherewe'lluseJesttotestthefunctionsdefinedin math.js:
const{sum,mul,sub,div}=require('./math')
test('Adding1+1equals2',()=>{
expect(sum(1,1)).toBe(2)
})
IntroductiontoJest
156
test('Multiplying1*1equals1',()=>{
expect(mul(1,1)).toBe(1)
})
test('Subtracting1-1equals0',()=>{
expect(sub(1,1)).toBe(0)
})
test('Dividing1/1equals1',()=>{
expect(div(1,1)).toBe(1)
})
Running yarntestresultsinJestbeingrunonallthetestfilesitfinds,andreturningustheendresult:
RunJestwithVSCodeVisualStudioCodeisagreateditorforJavaScriptdevelopment.TheJestextensionoffersatopnotchintegrationforourtests.
Onceyouinstallit,itwillautomaticallydetectifyouhaveinstalledJestinyourdevDependenciesandrunthetests.YoucanalsoinvokethetestsmanuallybyselectingtheJest:StartRunnercommand.Itwillrunthetestsandstayinwatchmodetore-runthemwheneveryouchangeoneofthefilesthathaveatest(oratestfile):
IntroductiontoJest
157
MatchersInthepreviousarticleIused toBe()astheonlymatcher:
test('Adding1+1equals2',()=>{
expect(sum(1,1)).toBe(2)
})
Amatcherisamethodthatletsyoutestvalues.
Mostcommonlyusedmatchers,comparingthevalueoftheresultof expect()withthevaluepassedinasargument,are:
toBecomparesstrictequality,using ===toEqualcomparesthevaluesoftwovariables.Ifit'sanobjectorarray,itcheckstheequalityofallthepropertiesorelementstoBeNullistruewhenpassinganullvaluetoBeDefinedistruewhenpassingadefinedvalue(oppositetotheabove)toBeUndefinedistruewhenpassinganundefinedvaluetoBeCloseToisusedtocomparefloatingvalues,avoidingroundingerrors
IntroductiontoJest
158
toBeTruthytrueifthevalueisconsideredtrue(likean ifdoes)toBeFalsytrueifthevalueisconsideredfalse(likean ifdoes)toBeGreaterThantrueiftheresultofexpect()ishigherthantheargumenttoBeGreaterThanOrEqualtrueiftheresultofexpect()isequaltotheargument,orhigherthantheargumenttoBeLessThantrueiftheresultofexpect()islowerthantheargumenttoBeLessThanOrEqualtrueiftheresultofexpect()isequaltotheargument,orlowerthantheargumenttoMatchisusedtocomparestringswithregularexpressionpatternmatchingtoContainisusedinarrays,trueiftheexpectedarraycontainstheargumentinitselementssettoHaveLength(number):checksthelengthofanarraytoHaveProperty(key,value):checksifanobjecthasaproperty,andoptionallychecksitsvaluetoThrowchecksifafunctionyoupassthrowsanexception(ingeneral)oraspecificexceptiontoBeInstanceOf():checksifanobjectisaninstanceofaclass
Allthosematcherscanbenegatedusing .not.insidethestatement,forexample:
test('Adding1+1doesnotequal3',()=>{
expect(sum(1,1)).not.toBe(3)
})
Forusewithpromises,youcanuse .resolvesand .rejects:
expect(Promise.resolve('lemon')).resolves.toBe('lemon')
expect(Promise.reject(newError('octopus'))).rejects.toThrow('octopus')
SetupBeforerunningyourtestsyouwillwanttoperformsomeinitialization.
Todosomethingoncebeforeallthetestsrun,usethe beforeAll()function:
beforeAll(()=>{
//dosomething
})
Toperformsomethingbeforeeachtestruns,use beforeEach():
IntroductiontoJest
159
beforeEach(()=>{
//dosomething
})
TeardownJustasyoucandowithsetup,youcanalsoperformsomethingaftereachtestruns:
afterEach(()=>{
//dosomething
})
andafteralltestsend:
afterAll(()=>{
//dosomething
})
Grouptestsusingdescribe()Youcancreategroupsoftests,inasinglefile,thatisolatethesetupandteardownfunctions:
describe('firstset',()=>{
beforeEach(()=>{
//dosomething
})
afterAll(()=>{
//dosomething
})
test(/*...*/)
test(/*...*/)
})
describe('secondset',()=>{
beforeEach(()=>{
//dosomething
})
beforeAll(()=>{
//dosomething
})
test(/*...*/)
test(/*...*/)
})
IntroductiontoJest
160
TestingasynchronouscodeAsynchronouscodeinmodernJavaScriptcanhavebasically2forms:callbacksandpromises.Ontopofpromiseswecanuseasync/await.
Callbacks
Youcan'thaveatestinacallback,becauseJestwon'texecuteit-theexecutionofthetestfileendsbeforethecallbackiscalled.Tofixthis,passaparametertothetestfunction,whichyoucanconvenientlycall done.Jestwillwaituntilyoucall done()beforeendingthattest:
//uppercase.js
functionuppercase(str,callback){
callback(str.toUpperCase())
}
module.exports=uppercase
//uppercase.test.js
constuppercase=require('./src/uppercase')
test(`uppercase'test'toequal'TEST'`,(done)=>{
uppercase('test',(str)=>{
expect(str).toBe('TEST')
done()
}
})
IntroductiontoJest
161
Promises
Withfunctionsthatreturnpromises,wesimplyreturnapromisefromthetest:
//uppercase.js
constuppercase=str=>{
returnnewPromise((resolve,reject)=>{
if(!str){
reject('Emptystring')
return
}
resolve(str.toUpperCase())
})
}
module.exports=uppercase
//uppercase.test.js
constuppercase=require('./uppercase')
test(`uppercase'test'toequal'TEST'`,()=>{
returnuppercase('test').then(str=>{
expect(str).toBe('TEST')
})
})
IntroductiontoJest
162
Promisesthatarerejectedcanbetestedusing .catch():
//uppercase.js
constuppercase=str=>{
returnnewPromise((resolve,reject)=>{
if(!str){
reject('Emptystring')
return
}
resolve(str.toUpperCase())
})
}
module.exports=uppercase
//uppercase.test.js
constuppercase=require('./uppercase')
IntroductiontoJest
163
test(`uppercase'test'toequal'TEST'`,()=>{
returnuppercase('').catch(e=>{
expect(e).toMatch('Emptystring')
})
})
Async/await
Totestfunctionsthatreturnpromiseswecanalsouseasync/await,whichmakesthesyntaxverystraightforwardandsimple:
//uppercase.test.js
constuppercase=require('./uppercase')
IntroductiontoJest
164
test(`uppercase'test'toequal'TEST'`,async()=>{
conststr=awaituppercase('test')
expect(str).toBe('TEST')
})
MockingIntesting,mockingallowsyoutotestfunctionalitythatdependson:
DatabaseNetworkrequestsaccesstoFilesanyExternalsystem
sothat:
1. yourtestsrunfaster,givingaquickturnaroundtimeduringdevelopment
IntroductiontoJest
165
2. yourtestsareindependentofnetworkconditions,orthestateofthedatabase3. yourtestsdonotpolluteanydatastoragebecausetheydonottouchthedatabase4. anychangedoneinatestdoesnotchangethestateforsubsequenttests,andre-running
thetestsuiteshouldstartfromaknownandreproduciblestartingpoint5. youdon'thavetoworryaboutratelimitingonAPIcallsandnetworkrequests
Mockingisusefulwhenyouwanttoavoidsideeffects(e.g.writingtoadatabase)oryouwanttoskipslowportionsofcode(likenetworkaccess),andalsoavoidsimplicationswithrunningyourtestsmultipletimes(e.g.imagineafunctionthatsendsanemailorcallsarate-limitedAPI).
Evenmoreimportant,ifyouarewritingaUnitTest,youshouldtestthefunctionalityofafunctioninisolation,notwithallitsbaggageofthingsittouches.
Usingmocks,youcaninspectifamodulefunctionhasbeencalledandwhichparameterswereused,with:
expect().toHaveBeenCalled():checkifaspiedfunctionhasbeencalledexpect().toHaveBeenCalledTimes():counthowmanytimesaspiedfunctionhasbeencalledexpect().toHaveBeenCalledWith():checkifthefunctionhasbeencalledwithaspecificsetofparametersexpect().toHaveBeenLastCalledWith():checktheparametersofthelasttimethefunctionhasbeeninvoked
Spypackageswithoutaffectingthefunctionscode
Whenyouimportapackage,youcantellJestto"spy"ontheexecutionofaparticularfunction,using spyOn(),withoutaffectinghowthatmethodworks.
Example:
constmathjs=require('mathjs')
test(`Themathjslogfunction`,()=>{
constspy=jest.spyOn(mathjs,'log')
constresult=mathjs.log(10000,10)
expect(mathjs.log).toHaveBeenCalled()
expect(mathjs.log).toHaveBeenCalledWith(10000,10)
})
Mockanentirepackage
IntroductiontoJest
166
Jestprovidesaconvenientwaytomockanentirepackage.Createa __mocks__folderintheprojectroot,andinthisfoldercreateoneJavaScriptfileforeachofyourpackages.
Sayyouimport mathjs.Createa __mocks__/mathjs.jsfileinyourprojectroot,andaddthiscontent:
module.exports={
log:jest.fn(()=>'test')
}
Thiswillmockthelog()functionofthepackage.Addasmanyfunctionsasyouwanttomock:
constmathjs=require('mathjs')
test(`Themathjslogfunction`,()=>{
constresult=mathjs.log(10000,10)
expect(result).toBe('test')
expect(mathjs.log).toHaveBeenCalled()
expect(mathjs.log).toHaveBeenCalledWith(10000,10)
})
Mockasinglefunction
Moresimply,youcanmockasinglefunctionusing jest.fn():
constmathjs=require('mathjs')
mathjs.log=jest.fn(()=>'test')
test(`Themathjslogfunction`,()=>{
constresult=mathjs.log(10000,10)
expect(result).toBe('test')
expect(mathjs.log).toHaveBeenCalled()
expect(mathjs.log).toHaveBeenCalledWith(10000,10)
})
Youcanalsouse jest.fn().mockReturnValue('test')tocreateasimplemockthatdoesnothingexceptreturningavalue.
Pre-builtmocks
Youcanfindpre-mademocksforpopularlibraries.Forexamplethispackagehttps://github.com/jefflau/jest-fetch-mockallowsyoutomock fetch()calls,andprovidesamplereturnvalueswithoutinteractingwiththeactualserverinyourtests.
Snapshottesting
IntroductiontoJest
167
SnapshottestingisaprettycoolfeatureofferedbyJest.ItcanmemorizehowyourUIcomponentsarerendered,andcompareittothecurrenttest,raisinganerrorifthere'samismatch.
ThisisasimpletestontheAppcomponentofasimple create-react-appapplication(makesureyouinstall react-test-renderer):
importReactfrom'react'
importAppfrom'./App'
importrendererfrom'react-test-renderer'
it('renderscorrectly',()=>{
consttree=renderer.create(<App/>).toJSON()
expect(tree).toMatchSnapshot()
})
thefirsttimeyourunthistest,Jestsavesthesnapshottothe __snapshots__folder.Here'swhatApp.test.js.snapcontains:
//JestSnapshotv1,https://goo.gl/fbAQLP
exports[`renderscorrectly1`]=`
<div
className="App"
>
<header
className="App-header"
>
<img
alt="logo"
className="App-logo"
src="logo.svg"
/>
<h1
className="App-title"
>
WelcometoReact
</h1>
</header>
<p
className="App-intro"
>
Togetstarted,edit
<code>
src/App.js
</code>
andsavetoreload.
</p>
</div>
`
IntroductiontoJest
168
Asyouseeit'sthecodethattheAppcomponentrenders,nothingmore.
Thenexttimethetestcomparestheoutputof <App/>tothis.IfAppchanges,yougetanerror:
Whenusing yarntestin create-react-appyouareinwatchmode,andfromthereyoucanpress wandshowmoreoptions:
WatchUsage
›Pressutoupdatefailingsnapshots.
›Pressptofilterbyafilenameregexpattern.
›Pressttofilterbyatestnameregexpattern.
›Pressqtoquitwatchmode.
›PressEntertotriggeratestrun.
IntroductiontoJest
169
Ifyourchangeisintended,pressing uwillupdatethefailingsnapshots,andmakethetestpass.
Youcanalsoupdatethesnapshotbyrunning jest-u(or jest--updateSnapshot)outsideofwatchmode.
IntroductiontoJest
170
TestingReactComponentsTestyourfirstReactcomponentusingJestand`react-testing-library`
TheeasiestwaytostartwithtestingReactcomponentsisdoingsnapshottesting,atestingtechniquethatletsyoutestcomponentsinisolation.
Ifyouarefamiliarwithtestingsoftware,it'sjustlikeunittestingyoudoforclasses:youtesteachcomponentfunctionality.
IassumeyoucreatedaReactappwith create-react-app,whichalreadycomeswithJestinstalled,thetestingpackagewe'llneed.
Let'sstartwithasimpletest.CodeSandboxisagreatenvironmenttotrythisout.StartwithaReactsandbox,andcreatean App.jscomponentina componentsfolder,andaddanApp.test.jsfile.
importReactfrom'react'
exportdefaultfunctionApp(){
return(
<divclassName="App">
<h1>HelloCodeSandbox</h1>
<h2>Starteditingtoseesomemagichappen!</h2>
</div>
)
}
Ourfirsttestisdumb:
test('Firsttest',()=>{
expect(true).toBeTruthy()
})
WhenCodeSandboxdetectstestfiles,itautomaticallyrunsthemforyou,andyoucanclicktheTestsbuttoninthebottomoftheviewtoshowyourtestresults:
TestingReactComponents
171
Atestfilecancontainmultipletests:
Let'sdosomethingabitmoreusefulnow,toactuallytestaReactcomponent.WeonlyhaveAppnow,whichisnotdoinganythingreallyuseful,solet'sfirstsetuptheenvironmentwithalittleapplicationwithmorefunctionality:thecounterappwebuiltpreviously.Ifyouskippedit,youcangobackandreadhowwebuiltit,butforeasierreferenceIaddithereagain.
TestingReactComponents
172
It'sjust2components:AppandButton.Createthe App.jsfile:
importReact,{useState}from'react'
importButtonfrom'./Button'
constApp=()=>{
const[count,setCount]=useState(0)
constincrementCount=increment=>{
setCount(count+increment)
}
return(
<div>
<Buttonincrement={1}onClickFunction={incrementCount}/>
<Buttonincrement={10}onClickFunction={incrementCount}/>
<Buttonincrement={100}onClickFunction={incrementCount}/>
<Buttonincrement={1000}onClickFunction={incrementCount}/>
<span>{count}</span>
</div>
)
}
exportdefaultApp
andthe Button.jsfile:
importReactfrom'react'
constButton=({increment,onClickFunction})=>{
consthandleClick=()=>{
onClickFunction(increment)
}
return<buttononClick={handleClick}>+{increment}</button>
}
exportdefaultButton
Wearegoingtousethe react-testing-library,whichisagreathelpasitallowsustoinspecttheoutputofeverycomponentandtoapplyeventsonthem.Youcanreadmoreaboutitonhttps://github.com/kentcdodds/react-testing-libraryorbywatchingthisvideo.
Let'stesttheButtoncomponentfirst.
Westartbyimporting renderand fireEventfrom react-testing-library,twohelpers.ThefirstletsusrenderJSX.Thesecondletsusemiteventsonacomponent.
Createa Button.test.jsandputitinthesamefolderas Button.js.
importReactfrom'react'
import{render,fireEvent}from'react-testing-library'
TestingReactComponents
173
importButtonfrom'./Button'
ButtonsareusedintheapptoacceptaclickeventandthentheycallafunctionpassedtotheonClickFunctionprop.Weadda countvariableandwecreateafunctionthatincrementsit:
letcount
constincrementCount=increment=>{
count+=increment
}
Nowofftotheactualtests.Wefirstinitializecountto0,andwerendera +1 Buttoncomponentpassinga 1to incrementandour incrementCountfunctionto onClickFunction.
Thenwegetthecontentofthefirstchildofthecomponent,andwecheckitoutputs +1.
Wethenproceedtoclickingthebutton,andwecheckthatthecountgotfrom0to1:
test('+1Buttonworks',()=>{
count=0
const{container}=render(
<Buttonincrement={1}onClickFunction={incrementCount}/>
)
constbutton=container.firstChild
expect(button.textContent).toBe('+1')
expect(count).toBe(0)
fireEvent.click(button)
expect(count).toBe(1)
})
Similarlywetesta+100button,thistimecheckingtheoutputis +100andthebuttonclickincrementsthecountof100.
test('+100Buttonworks',()=>{
count=0
const{container}=render(
<Buttonincrement={100}onClickFunction={incrementCount}/>
)
constbutton=container.firstChild
expect(button.textContent).toBe('+100')
expect(count).toBe(0)
fireEvent.click(button)
expect(count).toBe(100)
})
Let'stesttheAppcomponentnow.Itshows4buttonsandtheresultinthepage.Wecaninspecteachbuttonandseeiftheresultincreaseswhenweclickthem,clickingmultipletimesaswell:
TestingReactComponents
174
importReactfrom'react'
import{render,fireEvent}from'react-testing-library'
importAppfrom'./App'
test('Appworks',()=>{
const{container}=render(<App/>)
console.log(container)
constbuttons=container.querySelectorAll('button')
expect(buttons[0].textContent).toBe('+1')
expect(buttons[1].textContent).toBe('+10')
expect(buttons[2].textContent).toBe('+100')
expect(buttons[3].textContent).toBe('+1000')
constresult=container.querySelector('span')
expect(result.textContent).toBe('0')
fireEvent.click(buttons[0])
expect(result.textContent).toBe('1')
fireEvent.click(buttons[1])
expect(result.textContent).toBe('11')
fireEvent.click(buttons[2])
expect(result.textContent).toBe('111')
fireEvent.click(buttons[3])
expect(result.textContent).toBe('1111')
fireEvent.click(buttons[2])
expect(result.textContent).toBe('1211')
fireEvent.click(buttons[1])
expect(result.textContent).toBe('1221')
fireEvent.click(buttons[0])
expect(result.textContent).toBe('1222')
})
CheckthecodeworkingonthisCodeSandbox:https://codesandbox.io/s/pprl4y0wq
TestingReactComponents
175
ReactRouterReactRouter4istheperfecttooltolinktogethertheURLandyourReactapp.ReactRouteristhede-factoReactroutinglibrary,andit'soneofthemostpopularprojectsbuiltontopofReact.
ThistutorialintroducesReactRouter4,thelaststableversion
ReactRouteristhede-factoReactroutinglibrary,andit'soneofthemostpopularprojectsbuiltontopofReact.
Reactatitscoreisaverysimplelibrary,anditdoesnotdictateanythingaboutrouting.
RoutinginaSinglePageApplicationisthewaytointroducesomefeaturestonavigatingtheappthroughlinks,whichareexpectedinnormalwebapplications:
1. ThebrowsershouldchangetheURLwhenyounavigatetoadifferentscreen2. Deeplinkingshouldwork:ifyoupointthebrowsertoaURL,theapplicationshould
reconstructthesameviewthatwaspresentedwhentheURLwasgenerated.3. Thebrowserback(andforward)buttonshouldworklikeexpected.
Routinglinkstogetheryourapplicationnavigationwiththenavigationfeaturesofferedbythebrowser:theaddressbarandthenavigationbuttons.
ReactRouteroffersawaytowriteyourcodesothatitwillshowcertaincomponentsofyourapponlyiftheroutematcheswhatyoudefine.
Installation
ReactRouter
176
Withnpm:
npminstallreact-router-dom
WithYarn:
yarnaddreact-router-dom
TypesofroutesReactRouterprovidestwodifferentkindofroutes:
BrowserRouter
HashRouter
OnebuildsclassicURLs,theotherbuildsURLswiththehash:
https://application.com/dashboard/*BrowserRouter*/
https://application.com/#/dashboard/*HashRouter*/
Whichonetouseismainlydictatedbythebrowsersyouneedtosupport. BrowserRouterusestheHistoryAPI,whichisrelativelyrecent,andnotsupportedinIE9andbelow.Ifyoudon'thavetoworryaboutolderbrowsers,it'stherecommendedchoice.
ComponentsThe3componentsyouwillinteractthemostwhenworkingwithReactRouterare:
BrowserRouter,usuallyaliasedas RouterLink
Route
BrowserRouterwrapsallyourRoutecomponents.
Linkcomponentsare-asyoucanimagine-usedtogeneratelinkstoyourroutes
Routecomponentsareresponsibleforshowing-orhiding-thecomponentstheycontain.
BrowserRouter
ReactRouter
177
Here'sasimpleexampleoftheBrowserRoutercomponent.Youimportitfromreact-router-dom,andyouuseittowrapallyourapp:
importReactfrom'react'
importReactDOMfrom'react-dom'
import{BrowserRouterasRouter}from'react-router-dom'
ReactDOM.render(
<Router>
<div>
<!---->
</div>
</Router>,
document.getElementById('app')
)
ABrowserRoutercomponentcanonlyhaveonechildelement,sowewrapallwe'regoingtoaddina divelement.
LinkTheLinkcomponentisusedtotriggernewroutes.Youimportitfrom react-router-dom,andyoucanaddtheLinkcomponentstopointatdifferentroutes,withthe toattribute:
importReactfrom'react'
importReactDOMfrom'react-dom'
import{BrowserRouterasRouter,Link}from'react-router-dom'
ReactDOM.render(
<Router>
<div>
<aside>
<Linkto={`/dashboard`}>Dashboard</Link>
<Linkto={`/about`}>About</Link>
</aside>
<!---->
</div>
</Router>,
document.getElementById('app')
)
RouteNowlet'saddtheRoutecomponentintheabovesnippettomakethingsactuallyworkaswewant:
ReactRouter
178
importReactfrom'react'
importReactDOMfrom'react-dom'
import{BrowserRouterasRouter,Link,Route}from'react-router-dom'
constDashboard=()=>(
<div>
<h2>Dashboard</h2>
...
</div>
)
constAbout=()=>(
<div>
<h2>About</h2>
...
</div>
)
ReactDOM.render(
<Router>
<div>
<aside>
<Linkto={`/`}>Dashboard</Link>
<Linkto={`/about`}>About</Link>
</aside>
<main>
<Routeexactpath="/"component={Dashboard}/>
<Routepath="/about"component={About}/>
</main>
</div>
</Router>,
document.getElementById('app')
)
CheckthisexampleonGlitch:https://flaviocopes-react-router-v4.glitch.me/
Whentheroutematches /,theapplicationshowstheDashboardcomponent.
Whentherouteischangedbyclickingthe"About"linkto /about,theDashboardcomponentisremovedandtheAboutcomponentisinsertedintheDOM.
Noticethe exactattribute.Withoutthis, path="/"wouldalsomatch /about,since /iscontainedintheroute.
MatchmultiplepathsYoucanhavearouterespondtomultiplepathssimplyusingaregex,because pathcanbearegularexpressionsstring:
ReactRouter
179
<Routepath="/(about|who)/"component={Dashboard}/>
InlinerenderingInsteadofspecifyinga componentpropertyon Route,youcanseta renderprop:
<Route
path="/(about|who)/"
render={()=>(
<div>
<h2>About</h2>
...
</div>
)}
/>
MatchdynamicrouteparameterYoualreadysawhowtousestaticrouteslike
constPosts=()=>(
<div>
<h2>Posts</h2>
...
</div>
)
//...
<Routeexactpath="/posts"component={Posts}/>
Here'showtohandledynamicroutes:
constPost=({match})=>(
<div>
<h2>Post#{match.params.id}</h2>
...
</div>
)
//...
<Routeexactpath="/post/:id"component={Post}/>
InyourRoutecomponentyoucanlookupthedynamicparametersin match.params.
ReactRouter
180
matchisalsoavailableininlinerenderedroutes,andthisisespeciallyusefulinthiscase,becausewecanusethe idparametertolookupthepostdatainourdatasourcebeforerenderingPost:
constposts=[
{id:1,title:'First',content:'Helloworld!'},
{id:2,title:'Second',content:'Helloagain!'}
]
constPost=({post})=>(
<div>
<h2>{post.title}</h2>
{post.content}
</div>
)
//...
<Routeexactpath="/post/:id"render={({match})=>(
<Postpost={posts.find(p=>p.id===match.params.id)}/>
)}/>
ReactRouter
181
ReduxReduxisastatemanagerthat'susuallyusedalongwithReact,butit'snottiedtothatlibrary.LearnReduxbyreadingthissimpleandeasytofollowguide
WhyyouneedReduxReduxisastatemanagerthat'susuallyusedalongwithReact,butit'snottiedtothatlibrary-itcanbeusedwithothertechnologiesaswell,butwe'llsticktoReactforthesakeoftheexplanation.
Reacthasitsownwaytomanagestate,asyoucanreadontheReactBeginner'sGuide,whereIintroducehowyoucanmanageStateinReact.
Movingthestateupinthetreeworksinsimplecases,butinacomplexappyoumightfindyouaremovingalmostallthestateup,andthendownusingprops.
Reactinversion16.3.0introducedtheContextAPI,whichmakesReduxredundantfortheusecaseofaccessingthestatefromdifferentpartsofyourapp,soconsiderusingtheContextAPIinsteadofRedux,unlessyouneedaspecificfeaturethatReduxprovides.
Reduxisawaytomanageanapplicationstate,andmoveittoanexternalglobalstore.
Thereareafewconceptstograsp,butonceyoudo,Reduxisaverysimpleapproachtotheproblem.
ReduxisverypopularwithReactapplications,butit'sinnowayuniquetoReact:therearebindingsfornearlyanypopularframework.Thatsaid,I'llmakesomeexamplesusingReactasitisitsprimaryusecase.
WhenshouldyouuseRedux?Reduxisidealformediumtobigapps,andyoushouldonlyuseitwhenyouhavetroublemanagingthestatewiththedefaultstatemanagementofReact,ortheotherlibraryyouuse.
Simpleappsshouldnotneeditatall(andthere'snothingwrongwithsimpleapps).
ImmutableStateTree
Redux
182
InRedux,thewholestateoftheapplicationisrepresentedbyoneJavaScriptobject,calledStateorStateTree.
WecallitImmutableStateTreebecauseitisreadonly:itcan'tbechangeddirectly.
ItcanonlybechangedbydispatchinganAction.
ActionsAnActionisaJavaScriptobjectthatdescribesachangeinaminimalway(withjusttheinformationneeded):
{
type:'CLICKED_SIDEBAR'
}
//e.g.withmoredata
{
type:'SELECTED_USER',
userId:232
}
Theonlyrequirementofanactionobjectishavinga typeproperty,whosevalueisusuallyastring.
Actionstypesshouldbeconstants
Inasimpleappanactiontypecanbedefinedasastring,asIdidintheexampleinthepreviouslesson.
Whentheappgrowsisbesttouseconstants:
constADD_ITEM='ADD_ITEM'
constaction={type:ADD_ITEM,title:'Thirditem'}
andtoseparateactionsintheirownfiles,andimportthem
import{ADD_ITEM,REMOVE_ITEM}from'./actions'
Actioncreators
ActionsCreatorsarefunctionsthatcreateactions.
functionaddItem(t){
Redux
183
return{
type:ADD_ITEM,
title:t
}
}
Youusuallyrunactioncreatorsincombinationwithtriggeringthedispatcher:
dispatch(addItem('Milk'))
orbydefininganactiondispatcherfunction:
constdispatchAddItem=i=>dispatch(addItem(i))
dispatchAddItem('Milk')
ReducersWhenanactionisfired,somethingmusthappen,thestateoftheapplicationmustchange.
Thisisthejobofreducers.
Whatisareducer
AreducerisapurefunctionthatcalculatesthenextStateTreebasedonthepreviousStateTree,andtheactiondispatched.
;(currentState,action)=>newState
Apurefunctiontakesaninputandreturnsanoutputwithoutchangingtheinputoranythingelse.Thus,areducerreturnsacompletelynewstatetreeobjectthatsubstitutesthepreviousone.
Whatareducershouldnotdo
Areducershouldbeapurefunction,soitshould:
nevermutateitsargumentsnevermutatethestate,butinsteadcreateanewonewith Object.assign({},...)nevergenerateside-effects(noAPIcallschanginganything)nevercallnon-purefunctions,functionsthatchangetheiroutputbasedonfactorsotherthantheirinput(e.g. Date.now()or Math.random())
Redux
184
Thereisnoreinforcement,butyoushouldsticktotherules.
Multiplereducers
Sincethestateofacomplexappcouldbereallywide,thereisnotasinglereducer,butmanyreducersforanykindofaction.
Asimulationofareducer
Atitscore,Reduxcanbesimplifiedwiththissimplemodel:
Thestate
{
list:[
{title:"Firstitem"},
{title:"Seconditem"},
],
title:'Grocerieslist'
}
Alistofactions
{type:'ADD_ITEM',title:'Thirditem'}
{type:'REMOVE_ITEM',index:1}
{type:'CHANGE_LIST_TITLE',title:'Roadtriplist'}
Areducerforeverypartofthestate
consttitle=(state='',action)=>{
if(action.type==='CHANGE_LIST_TITLE'){
returnaction.title
}else{
returnstate
}
}
constlist=(state=[],action)=>{
switch(action.type){
case'ADD_ITEM':
returnstate.concat([{title:action.title}])
case'REMOVE_ITEM':
returnstate.map((item,index)=>
action.index===index
?{title:item.title}
:item
Redux
185
default:
returnstate
}
}
Areducerforthewholestate
constlistManager=(state={},action)=>{
return{
title:title(state.title,action),
list:list(state.list,action)
}
}
TheStoreTheStoreisanobjectthat:
holdsthestateoftheappexposesthestatevia getState()allowsustoupdatethestatevia dispatch()allowsusto(un)registerastatechangelistenerusing subscribe()
Astoreisuniqueintheapp.
HereishowastoreforthelistManagerappiscreated:
import{createStore}from'redux'
importlistManagerfrom'./reducers'
letstore=createStore(listManager)
CanIinitializethestorewithserver-sidedata?
Sure,justpassastartingstate:
letstore=createStore(listManager,preexistingState)
Gettingthestate
store.getState()
Updatethestate
Redux
186
store.dispatch(addItem('Something'))
Listentostatechanges
constunsubscribe=store.subscribe(()=>
constnewState=store.getState()
)
unsubscribe()
DataFlowDataflowinReduxisalwaysunidirectional.
Youcall dispatch()ontheStore,passinganAction.
TheStoretakescareofpassingtheActiontotheReducer,generatingthenextState.
TheStoreupdatestheStateandalertsalltheListeners.
Redux
187
Next.jsNext.jsisaverypopularNode.jsframeworkwhichenableseasyserver-sideReactrendering,andprovidesmanyotheramazingfeatures
WorkingonamodernJavaScriptapplicationpoweredbyReactisawesomeuntilyourealizethatthereareacoupleproblemsrelatedtorenderingallthecontentontheclient-side.
First,thepagetakeslongertothebecomevisibletotheuser,becausebeforethecontentloads,alltheJavaScriptmustload,andyourapplicationneedstoruntodeterminewhattoshowonthepage.
Second,ifyouarebuildingapubliclyavailablewebsite,youhaveacontentSEOissue.SearchenginesaregettingbetteratrunningandindexingJavaScriptapps,butit'smuchbetterifwecansendthemcontentinsteadoflettingthemfigureitout.
Thesolutiontobothofthoseproblemsisserverrendering,alsocalledstaticpre-rendering.
Next.jsisoneReactframeworktodoallofthisinaverysimpleway,butit'snotlimitedtothis.It'sadvertisedbyitscreatorsasazero-configuration,single-commandtoolchainforReactapps.
ItprovidesacommonstructurethatallowsyoutoeasilybuildafrontendReactapplication,andtransparentlyhandleserver-siderenderingforyou.
Next.js
188
MainfeaturesHereisanon-exhaustivelistofthemainNext.jsfeatures:
HotCodeReloading:Next.jsreloadsthepagewhenitdetectsanychangesavedtodisk.AutomaticRouting:anyURLismappedtothefilesystem,tofilesputinthe pagesfolder,andyoudon'tneedanyconfiguration(youhavecustomizationoptionsofcourse).SingleFileComponents:usingstyled-jsx,completelyintegratedasbuiltbythesameteam,it'strivialtoaddstylesscopedtothecomponent.ServerRendering:youcan(optionally)renderReactcomponentsontheserverside,beforesendingtheHTMLtotheclient.EcosystemCompatibility:Next.jsplayswellwiththerestoftheJavaScript,NodeandReactecosystem.AutomaticCodeSplitting:pagesarerenderedwithjustthelibrariesandJavaScriptthattheyneed,nomore.Prefetching:the Linkcomponent,usedtolinktogetherdifferentpages,supportsaprefetchpropwhichautomaticallyprefetchespageresources(includingcodemissingduetocodesplitting)inthebackground.DynamicComponents:youcanimportJavaScriptmodulesandReactComponentsdynamically(https://github.com/zeit/next.js#dynamic-import).StaticExports:usingthe nextexportcommand,Next.jsallowsyoutoexportafullystaticsitefromyourapp.
InstallationNext.jssupportsallthemajorplatforms:Linux,macOS,Windows.
ANext.jsprojectisstartedeasilywithnpm:
npminstallnextreactreact-dom
orwithYarn:
yarnaddnextreactreact-dom
GettingstartedCreatea package.jsonfilewiththiscontent:
Next.js
189
{
"scripts":{
"dev":"next"
}
}
Ifyourunthiscommandnow:
npmrundev
thescriptwillraiseanerrorcomplainingaboutnotfindingthe pagesfolder.ThisistheonlythingthatNext.jsrequirestorun.
Createanempty pagesfolder,andrunthecommandagain,andNext.jswillstartupaserveron localhost:3000.
IfyougotothatURLnow,you'llbegreetedbyafriendly404page,withanicecleandesign.
Next.jshandlesothererrortypesaswell,like500errorsforexample.
CreateapageInthe pagesfoldercreatean index.jsfilewithasimpleReactfunctionalcomponent:
exportdefault()=>(
<div>
<p>HelloWorld!</p>
</div>
)
Ifyouvisit localhost:3000,thiscomponentwillautomaticallyberendered.
Next.js
190
Whyisthissosimple?
Next.jsusesadeclarativepagesstructure,whichisbasedonthefilesystemstructure.
Simplyput,pagesareinsidea pagesfolder,andthepageURLisdeterminedbythepagefilename.ThefilesystemisthepagesAPI.
Server-siderenderingOpenthepagesource, View->Developer->ViewSourcewithChrome.
Asyoucansee,theHTMLgeneratedbythecomponentissentdirectlyinthepagesource.It'snotrenderedclient-side,butinsteadit'srenderedontheserver.
TheNext.jsteamwantedtocreateadeveloperexperienceforserverrenderedpagessimilartotheoneyougetwhencreatingabasicPHPproject,whereyousimplydropPHPfilesandyoucallthem,andtheyshowupaspages.Internallyofcourseit'sallverydifferent,buttheapparenteaseofuseisclear.
AddasecondpageLet'screateanotherpage,in pages/contact.js
exportdefault()=>(
<div>
<p>
<ahref="mailto:[email protected]">Contactus!</a>
</p>
</div>
)
Ifyoupointyourbrowserto localhost:3000/contactthispagewillberendered.Asyoucansee,alsothispageisserverrendered.
HotreloadingNotehowyoudidnothavetorestartthe npmprocesstoloadthesecondpage.Next.jsdoesthisforyouunderthehood.
Clientrendering
Next.js
191
Serverrenderingisveryconvenientinyourfirstpageload,forallthereasonswesawabove,butwhenitcomestonavigatinginsidethewebsite,client-siderenderingiskeytospeedingupthepageloadandimprovingtheuserexperience.
Next.jsprovidesa Linkcomponentyoucanusetobuildlinks.Trylinkingthetwopagesabove.
Change index.jstothiscode:
importLinkfrom'next/link'
exportdefault()=>(
<div>
<p>HelloWorld!</p>
<Linkhref="/contact">
<a>Contactme!</a>
</Link>
</div>
)
Nowgobacktothebrowserandtrythislink.Asyoucansee,theContactpageloadsimmediately,withoutapagerefresh.
Thisisclient-sidenavigationworkingcorrectly,withcompletesupportfortheHistoryAPI,whichmeansyourusersbackbuttonwon'tbreak.
Ifyounow cmd-clickthelink,thesameContactpagewillopeninanewtab,nowserverrendered.
DynamicpagesAgoodusecaseforNext.jsisablog,asit'ssomethingthatalldevelopersknowhowitworks,andit'sagoodfitforasimpleexampleofhowtohandledynamicpages.
Adynamicpageisapagethathasnofixedcontent,butinsteaddisplaysomedatabasedonsomeparameters.
Change index.jsto
importLinkfrom'next/link'
constPost=props=>(
<li>
<Linkhref={`/post?title=${props.title}`}>
<a>{props.title}</a>
</Link>
</li>
)
Next.js
192
exportdefault()=>(
<div>
<h2>Myblog</h2>
<ul>
<li>
<Posttitle="Yetanotherpost"/>
<Posttitle="Secondpost"/>
<Posttitle="Hello,world!"/>
</li>
</ul>
</div>
)
Thiswillcreateaseriesofpostsandwillfillthetitlequeryparameterwiththeposttitle:
Nowcreatea post.jsfileinthe pagesfolder,andadd:
exportdefaultprops=><h1>{props.url.query.title}</h1>
Nowclickingasinglepostwillrendertheposttitleina h1tag:
YoucanusecleanURLswithoutqueryparameters.TheNext.jsLinkcomponenthelpsusbyacceptingan asattribute,whichyoucanusetopassaslug:
Next.js
193
importLinkfrom'next/link'
constPost=props=>(
<li>
<Linkas={`/${props.slug}`}href={`/post?title=${props.title}`}>
<a>{props.title}</a>
</Link>
</li>
)
exportdefault()=>(
<div>
<h2>Myblog</h2>
<ul>
<li>
<Postslug="yet-another-post"title="Yetanotherpost"/>
<Postslug="second-post"title="Secondpost"/>
<Postslug="hello-world"title="Hello,world!"/>
</li>
</ul>
</div>
)
CSS-in-JSNext.jsbydefaultprovidessupportforstyled-jsx,whichisaCSS-in-JSsolutionprovidedbythesamedevelopmentteam,butyoucanusewhateverlibraryyouprefer,likeStyledComponents.
Example:
exportdefault()=>(
<div>
<p>
<ahref="mailto:[email protected]">Contactus!</a>
</p>
<stylejsx>{`
p{
font-family:'CourierNew';
}
a{
text-decoration:none;
color:black;
}
a:hover{
opacity:0.8;
}
`}</style>
</div>
)
Next.js
194
Stylesarescopedtothecomponent,butyoucanalsoeditglobalstylesadding globaltothestyleelement:
exportdefault()=>(
<div>
<p>
<ahref="mailto:[email protected]">Contactus!</a>
</p>
<stylejsxglobal>{`
body{
font-family:'BentonSans','HelveticaNeue';
margin:2em;
}
h2{
font-style:italic;
color:#373fff;
}
`}</style>
</div>
)
ExportingastaticsiteANext.jsapplicationcanbeeasilyexportedasastaticsite,whichcanbedeployedononeofthesuperfaststaticsitehosts,likeNetlifyorFirebaseHosting,withouttheneedtosetupaNodeenvironment.
TheprocessrequiresyoutodeclaretheURLsthatcomposethesite,butit'sastraightforwardprocess.
DeployingCreatingaproduction-readycopyoftheapplication,withoutsourcemapsorotherdevelopmenttoolingthataren'tneededinthefinalbuild,iseasy.
Atthebeginningofthistutorialyoucreateda package.jsonfilewiththiscontent:
{
"scripts":{
"dev":"next"
}
}
whichwasthewaytostartupadevelopmentserverusing npmrundev.
Nowjustaddthefollowingcontentto package.jsoninstead:
Next.js
195
{
"scripts":{
"dev":"next",
"build":"nextbuild",
"start":"nextstart"
}
}
andprepareyourappbyrunning npmrunbuildand npmrunstart.
NowThecompanybehindNext.jsprovidesanawesomehostingserviceforNode.jsapplications,calledNow.
OfcoursetheyintegrateboththeirproductssoyoucandeployNext.jsappsseamlessly,onceyouhaveNowinstalled,byrunningthe nowcommandintheapplicationfolder.
BehindthescenesNowsetsupaserverforyou,andyoudon'tneedtoworryaboutanything,justwaitforyourapplicationURLtobeready.
ZonesYoucansetupmultipleNext.jsinstancestolistentodifferentURLs,yettheapplicationtoanoutsideuserwillsimplylooklikeit'sbeingpoweredbyasingleserver:https://github.com/zeit/next.js/#multi-zones
PluginsNext.jshasalistofpluginsathttps://github.com/zeit/next-plugins
StarterkitonGlitchIfyou'relookingtoexperiment,IrecommendGlitch.CheckoutmyNext.jsGlitchStarterKit.
ReadmoreonNext.jsIcan'tpossiblydescribeeveryfeatureofthisgreatframework,andthemainplacetoreadmoreaboutNext.jsistheprojectreadmeonGitHub.
Next.js
196
Next.js
197
GatsbyGatsbyisaplatformforbuildingappsandwebsitesusingReact
GatsbyisaplatformforbuildingappsandwebsitesusingReact.
ItisoneofthetoolsthatallowyoutobuildonasetoftechnologiesandpracticescollectivelyknownasJAMstack.
GatsbyisoneofthecoolkidsintheFrontendDevelopmentspacerightnow.Why?Ithinkthereasonsare:
theexplosionoftheJAMstackapproachtobuildingWebAppsandWebSitestherapidadoptionoftheProgressiveWebAppstechnologyintheindustry,whichisoneofthekeyfeaturesofGatsbyit'sbuiltinReactandGraphQL,whicharetwoverypopularandrisingtechnologiesit'sreallypowerfulit'sfastthedocumentationisgreatthenetworkeffect(peopleuseit,createsites,maketutorials,peopleknowmoreaboutit,creatingacycle)everythingisJavaScript(noneedtolearnanewtemplatinglanguage)ithidesthecomplexity,inthebeginning,butallowsusaccessintoeverysteptocustomize
Allthosearegreatpoints,andGatsbyisdefinitelyworthalook.
Howdoesitwork?WithGatsby,yourapplicationsarebuiltusingReactcomponents.
Gatsby
198
Thecontentyou'llrenderinasiteisgenerallywrittenusingMarkdown,butyoucanuseanykindofdatasource,likeanheadlessCMSorawebservicelikeContentful.
Gatsbybuildsthesite,andit'scompiledtostaticHTMLwhichcanbedeployedonanyWebServeryouwant,likeNetlify,AWSS3,GitHubPages,regularhostingproviders,PAASandmore.AllyouneedisaplacethatservesplainHTTPpagesandyourassetstotheclient.
ImentionedProgressiveWebAppsinthelist.GatsbyautomaticallygeneratesyoursiteasaPWA,withaserviceworkerthatspeedsuppageloadingandresourcecaching.
YoucanenhancethefunctionalityofGatsbyviaplugins.
InstallationYoucaninstallGatsbybysimplyrunningthisinyourterminal:
npminstall-ggatsby-cli
Thisinstallsthe gatsbyCLIutility.
(whenanewversionisout,updateitbycallingthiscommandagain)
Youcreateanew"HelloWorld"sitebyrunning
gatsbynewmysitehttps://github.com/gatsbyjs/gatsby-starter-hello-world
ThiscommandcreatesabrandnewGatsbysiteinthe mysitefolder,usingthestarteravailableathttps://github.com/gatsbyjs/gatsby-starter-hello-world.
Gatsby
199
Astarterisasamplesitethatyoucanbuildupon.Anothercommonstarteris default,availableathttps://github.com/gatsbyjs/gatsby-starter-default.
Hereyoucanfindalistofallthestartersyoucanuse
RunningtheGatsbysiteAftertheterminalhasfinishedinstallingthestarter,youcanrunthewebsitebycalling
cdmysite
gatsbydevelop
whichwillstartupanewWebServerandservethesiteonport8000onlocalhost.
Gatsby
200
AndhereisourHelloWorldstarterinaction:
Gatsby
201
InspectingthesiteIfyouopenthesiteyoucreatedwithyourfavoritecodeeditor(IuseVSCode),you'llfindthereare3folders:
.cache,anhiddenfolderthatcontainstheGatsbyinternals,nothingyoushouldchangerightnowpublic,whichcontainstheresultingwebsiteonceyoubuilditsrccontainstheReactcomponents,inthiscasejustthe indexcomponentstaticwhichwillcontainthestaticresourceslikeCSSandimages
Gatsby
202
Now,makingasimplechangetothedefaultpageiseasy,justopen src/pages/index.jsandchange"Helloworld!"tosomethingelse,andsave.Thebrowsershouldinstantlyhotreloadthecomponent(whichmeansthepagedoesnotactuallyrefresh,butthecontentchanges-atrickmadepossiblebytheunderlyingtechnology).
Toaddasecondpage,justcreateanother.jsfileinthisfolder,withthesamecontentofindex.js(tweakthecontent)andsaveit.
ForexampleIcreateda second.jsfilewiththiscontent:
importReactfrom'react'
exportdefault()=><div>Secondpage!</div>
andIopenedthebrowsertohttp://localhost:8000/second:
Gatsby
203
LinkingpagesYoucanlinkthosepagesbyimportingaGatsby-providedReactcomponentcalled Link:
import{Link}from"gatsby"
andusingitinyourcomponentJSX:
<Linkto="/second/">Second</Link>
AddingCSSYoucanimportanyCSSfileusingaJavaScriptimport:
import'./index.css'
YoucanuseReactstyling:
<pstyle={{
margin:'0auto',
padding:'20px'
Gatsby
204
}}>Helloworld</p>
UsingpluginsGatsbyprovideslotsofthingsoutofthebox,butmanyotherfunctionalitiesareprovidedbyplugins.
Thereare3kindofplugins:
sourcepluginsfetchdatafromasource.CreatenodesthatcanbethenfilteredbytransformerpluginstransformerpluginstransformthedataprovidedbysourcepluginsintosomethingGatsbycanusefunctionalpluginsimplementsomekindoffunctionality,likeaddingsitemapsupportormore
Somecommonlyusedpluginsare:
gatsby-plugin-react-helmetwhichallowstoeditthe headtagcontentgatsby-plugin-catch-linkswhichusestheHistoryAPItopreventthebrowserreloadingthepagewhenalinkisclicked,loadingthenewcontentusingAJAXinstead
AGatsbypluginisinstalledin2steps.Firstyouinstallitusing npm,thenyouaddittotheGatsbyconfigurationin gatsby-config.js.
ForexampleyoucaninstalltheCatchLinksplugin:
npminstallgatsby-plugin-catch-links
In gatsby-config.js(createitifyoudon'thaveit,inthewebsiterootfolder),addtheplugintothe pluginsexportedarray:
module.exports={
plugins:['gatsby-plugin-catch-links']
}
That'sit,thepluginwillnowdoitsjob.
BuildingthestaticwebsiteOnceyouaredonetweakingthesiteandyouwanttogeneratetheproductionstaticsite,youwillcall
Gatsby
205
gatsbybuild
AtthispointyoucancheckthatitallworksasyouexpectbystartingalocalWebServerusing
gatsbyserve
whichwillrenderthesiteascloseaspossibletohowyouwillseeitinproduction.
DeploymentOnceyoubuildthesiteusing gatsbybuild,allyouneedtodoistodeploytheresultcontainedinthe publicfolder.
Dependingonthesolutionyouchoose,you'llneeddifferentstepshere,butgenerallyyou'llpushtoaGitrepositoryandlettheGitpost-commithooksdothejobofdeploying.
Herearesomegreatguidesforsomepopularhostingplatforms.
Gatsby
206
WrappingupIhopethebookhelpedyougetstartedwithReact,andmaybeitgaveyouaheadstartinexploringsomeofthemostadvancedaspectsofReactprogramming.
That'smyhope,atleast.
Iwillsoonreleaseapracticalonlinecoursetoapplythoseconcepts.Staytuned.
Wrappingup
207