Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (15.53 MB, 432 trang )
<span class='text_page_counter'>(1)</span><div class='page_container' data-page=1></div>
<span class='text_page_counter'>(2)</span><div class='page_container' data-page=2></div>
<span class='text_page_counter'>(3)</span><div class='page_container' data-page=3>
BuildingMobileApplicationswithJavaScript
<b>LearningReactNative</b>
byBonnieEisenman
Copyright©2016BonnieEisenman.Allrightsreserved.
PrintedintheUnitedStatesofAmerica.
PublishedbyO’ReillyMedia,Inc.,1005GravensteinHighwayNorth,Sebastopol,CA
95472.
O’Reillybooksmaybepurchasedforeducational,business,orsalespromotionaluse.
Onlineeditionsarealsoavailableformosttitles().Formore
information,contactourcorporate/institutionalsalesdepartment:800-998-9938or
<i></i>
Editor:MegFoley
ProductionEditor:NicholasAdams
Copyeditor:JasmineKwityn
Proofreader:ChristinaEdwards
Indexer:EllenTroutman-Zaig
InteriorDesigner:DavidFutato
CoverDesigner:RandyComer
Illustrator:RebeccaDemarest
<b>RevisionHistoryfortheFirstEdition</b>
2015-12-01:FirstRelease
Seeforreleasedetails.
<i>TheO’ReillylogoisaregisteredtrademarkofO’ReillyMedia,Inc.LearningReact</i>
<i>Native,thecoverimage,andrelatedtradedressaretrademarksofO’ReillyMedia,Inc.</i>
Whilethepublisherandtheauthorhaveusedgoodfaitheffortstoensurethatthe
informationandinstructionscontainedinthisworkareaccurate,thepublisherandthe
authordisclaimallresponsibilityforerrorsoromissions,includingwithoutlimitation
responsibilityfordamagesresultingfromtheuseoforrelianceonthiswork.Useofthe
informationandinstructionscontainedinthisworkisatyourownrisk.Ifanycode
samplesorothertechnologythisworkcontainsordescribesissubjecttoopensource
licensesortheintellectualpropertyrightsofothers,itisyourresponsibilitytoensurethat
yourusethereofcomplieswithsuchlicensesand/orrights.
978-1-491-92900-1
ThisbookisanintroductiontoReactNative,Facebook’sJavaScriptframeworkfor
traditionalmeansofmobiledevelopment,andwedon’tneedtosacrificethenativelook
andfeel.
We’llstartwiththebasics,andworkourwayuptodeployingafull-fledgedapplicationto
boththeiOSAppStoreandtheGooglePlayStore,with100%codereusebetweenthetwo
platforms.Inadditiontotheessentialsoftheframework,we’lldiscusshowtowork
beyondit,includinghowtomakeuseofthird-partylibrariesandevenhowtowriteyour
ownJavaorObjective-ClibrariestoextendReactNative.
ThisbookisnotanintroductiontoReact,ingeneral.We’llassumethatyouhavesome
workingknowledgeofReact.Ifyou’rebrandnewtoReact,Isuggestreadingthrougha
tutorialortwobeforecomingbacktotaketheplungeintomobiledevelopment.
Specifically,youshouldbefamiliarwiththeroleofpropsandstate,thecomponent
lifecycle,andhowtocreateReactcomponents.
We’llalsobeusingsomeES6syntax,aswellasJSX.Ifyouaren’tfamiliarwiththese,
don’tworry;we’llcoverJSXinChapter2,andES6syntaxinAppendixA.Thesefeatures
areessentially1:1translationsoftheJavaScriptcodeyou’realreadyaccustomedto
writing.
Thefollowingtypographicalconventionsareusedinthisbook:
<i>Italic</i>
Indicatesnewterms,URLs,emailaddresses,filenames,andfileextensions.
<i>Constantwidth</i>
Usedforprogramlistings,aswellaswithinparagraphstorefertoprogramelements
suchasvariableorfunctionnames,databases,datatypes,environmentvariables,
statements,andkeywords.
<i><b>Constantwidthbold</b></i>
Showscommandsorothertextthatshouldbetypedliterallybytheuser.
<i>Constantwidthitalic</i>
Showstextthatshouldbereplacedwithuser-suppliedvaluesorbyvaluesdetermined
bycontext.
<b>TIP</b>
Thiselementsignifiesatiporsuggestion.
<b>NOTE</b>
Thiselementsignifiesageneralnote.
<b>WARNING</b>
Supplementalmaterial(codeexamples,exercises,etc.)isavailablefordownloadat:
<i> />
Thisbookisheretohelpyougetyourjobdone.Ingeneral,ifexamplecodeisoffered
withthisbook,youmayuseitinyourprogramsanddocumentation.Youdonotneedto
contactusforpermissionunlessyou’rereproducingasignificantportionofthecode.For
example,writingaprogramthatusesseveralchunksofcodefromthisbookdoesnot
requirepermission.SellingordistributingaCD-ROMofexamplesfromO’Reillybooks
doesrequirepermission.Answeringaquestionbycitingthisbookandquotingexample
codedoesnotrequirepermission.Incorporatingasignificantamountofexamplecode
fromthisbookintoyourproduct’sdocumentationdoesrequirepermission.
Weappreciate,butdonotrequire,attribution.Anattributionusuallyincludesthetitle,
<i>author,publisher,andISBN.Forexample:“LearningReactNativebyBonnieEisenman</i>
(O’Reilly).Copyright2016BonnieEisenman,978-1-491-92900-1.”
<i>SafariBooksOnlineisanon-demanddigitallibrarythatdeliversexpert</i>contentinboth
bookandvideoformfromtheworld’sleadingauthorsintechnologyandbusiness.
Technologyprofessionals,softwaredevelopers,webdesigners,andbusinessandcreative
professionalsuseSafariBooksOnlineastheirprimaryresourceforresearch,problem
solving,learning,andcertificationtraining.
SafariBooksOnlineoffersarangeofplansandpricingforenterprise,government,
education,andindividuals.
Membershaveaccesstothousandsofbooks,trainingvideos,andprepublication
manuscriptsinonefullysearchabledatabasefrompublisherslikeO’ReillyMedia,
PrenticeHallProfessional,Addison-WesleyProfessional,MicrosoftPress,Sams,Que,
PeachpitPress,FocalPress,CiscoPress,JohnWiley&Sons,Syngress,Morgan
Kaufmann,IBMRedbooks,Packt,AdobePress,FTPress,Apress,Manning,NewRiders,
McGraw-Hill,Jones&Bartlett,CourseTechnology,andhundredsmore.Formore
Pleaseaddresscommentsandquestionsconcerningthisbooktothepublisher:
O’ReillyMedia,Inc.
1005GravensteinHighwayNorth
Sebastopol,CA95472
800-998-9938(intheUnitedStatesorCanada)
707-829-0515(internationalorlocal)
707-829-0104(fax)
Wehaveawebpageforthisbook,wherewelisterrata,examples,andanyadditional
Tocommentorasktechnicalquestionsaboutthisbook,sendemailto
<i></i>
Formoreinformationaboutourbooks,courses,conferences,andnews,seeourwebsiteat
<i>.</i>
FindusonFacebook: />
FollowusonTwitter: />
It’sdangeroustogoalone!Well,notreally,butthatdoesn’tmeanyouhaveto.Hereare
someresourcesyoumayfindusefulasyouworkthroughthebook:
TheGitHubrepositoryforthisbookcontainsallofthecodesampleswe’llbe
discussing.Ifyougetstumped,orwantmorecontext,trylookingherefirst.
JointhemailinglistatLearningReactNative.comforfollow-uparticles,suggestions,
andhelpfulresources.
Theofficialdocumentationhasalotofgoodreferencematerial.
Additionally,theReactNativecommunityisausefulresource:
BrentVatne’sReactNativenewsletter
Thereact-nativetagonStackOverflow
Asistraditional:thisbookwouldnothavebeenpossiblewithoutthehelpandsupportof
manyothers.Thankyoutomyeditor,MegFoley,andtherestoftheO’Reillyteam,for
bringingthisprojectintotheworld.Thankyoualsotomytechnicalreviewers,foryour
timeandinsightfulfeedback:DavidBieber,JasonBrown,EricaPortnoy,andJonathan
Stark.IwouldalsoliketothanktheReactNativeteam,withoutwhosestellarworkthis
bookwouldnaturallybeimpossible.ThanksalsotoZacharyElliotforhishelpwiththe
ZebretoapplicationandAndroidingeneral.
ReactNativeisaJavaScriptframeworkforwritingreal,nativelyrenderingmobile
applicationsforiOSandAndroid.It’sbasedonReact,Facebook’sJavaScriptlibraryfor
buildinguserinterfaces,butinsteadoftargetingthebrowser,ittargetsmobileplatforms.
Inotherwords:webdeveloperscannowwritemobileapplicationsthatlookandfeeltruly
“native,”allfromthecomfortofaJavaScriptlibrarythatwealreadyknowandlove.Plus,
becausemostofthecodeyouwritecanbesharedbetweenplatforms,ReactNativemakes
iteasytosimultaneouslydevelopforbothAndroidandiOS.
SimilartoReactfortheWeb,ReactNativeapplicationsarewrittenusingamixtureof
JavaScriptandXML-esquemarkup,knownasJSX.Then,underthehood,theReact
Native“bridge”invokesthenativerenderingAPIsinObjective-C(foriOS)orJava(for
<i>Android).Thus,yourapplicationwillrenderusingrealmobileUIcomponents,not</i>
webviews,andwilllookandfeellikeanyothermobileapplication.ReactNativealso
exposesJavaScriptinterfacesforplatformAPIs,soyourReactNativeappscanaccess
platformfeatureslikethephonecamera,ortheuser’slocation.
ReactNativecurrentlysupportsbothiOSandAndroid,andhasthepotentialtoexpandto
futureplatformsaswell.Inthisbook,we’llcoverbothiOSandAndroid.Thevast
ThefactthatReactNativeactuallyrendersusingitshostplatform’sstandardrendering
APIsenablesittostandoutfrommostexistingmethodsofcross-platformapplication
development,likeCordovaorIonic.Existingmethodsofwritingmobileapplications
usingcombinationsofJavaScript,HTML,andCSStypicallyrenderusingwebviews.
Whilethisapproachcanwork,italsocomeswithdrawbacks,especiallyaround
performance.Additionally,theydonotusuallyhaveaccesstothehostplatform’ssetof
nativeUIelements.WhentheseframeworksdotrytomimicnativeUIelements,the
resultsusually“feel”justalittleoff;reverse-engineeringallthefinedetailsofthingslike
animationstakesanenormousamountofeffort,andtheycanquicklybecomeoutofdate.
Incontrast,ReactNativeactuallytranslatesyourmarkuptoreal,nativeUIelements,
leveragingexistingmeansofrenderingviewsonwhateverplatformyouareworkingwith.
Additionally,ReactworksseparatelyfromthemainUIthread,soyourapplicationcan
maintainhighperformancewithoutsacrificingcapability.TheupdatecycleinReact
NativeisthesameasinReact:whenpropsorstatechange,ReactNativere-rendersthe
views.ThemajordifferencebetweenReactNativeandReactinthebrowseristhatReact
NativedoesthisbyleveragingtheUIlibrariesofitshostplatform,ratherthanusing
HTMLandCSSmarkup.
FordevelopersaccustomedtoworkingontheWebwithReact,thismeansyoucanwrite
mobileappswiththeperformanceandlookandfeelofanativeapplication,whileusing
familiartools.ReactNativealsorepresentsanimprovementovernormalmobile
Ifyou’veeverdevelopedformobilebefore,youmightbesurprisedbyhoweasyReact
Nativeistoworkwith.TheReactNativeteamhasbakedstrongdevelopertoolsand
meaningfulerrormessagesintotheframework,soworkingwithrobusttoolsisanatural
Forinstance,becauseReactNativeis“just”JavaScript,youdon’tneedtorebuildyour
applicationinordertoseeyourchangesreflected;instead,youcanhitCommand+Rto
refreshyourapplicationjustasyouwouldanyotherwebpage.Allofthoseminutesspent
waitingforyourapplicationtobuildcanreallyaddup,andincontrastReactNative’s
quickiterationcyclefeelslikeagodsend.
Additionally,ReactNativeletsyoutakeadvantageofintelligentdebuggingtoolsanderror
reporting.IfyouarecomfortablewithChromeorSafari’sdevelopertools(Figure1-1),
youwillbehappytoknowthatyoucanusethemformobiledevelopment,aswell.
<i>Figure1-1.UsingtheChromeDebugger</i>
Besidestheday-to-dayimprovementstoyourdevelopmentexperience,ReactNativealso
hasthepotentialtopositivelyimpactyourproductreleasecycle.Forinstance,Apple
permitsJavaScript-basedchangestoanapp’sbehaviortobeloadedovertheairwithno
additionalreviewcyclenecessary.
WorkingwithReactNativecandramaticallyshrinktheresourcesrequiredtobuildmobile
applications.AnydeveloperwhoknowshowtowriteReactcodecannowtargettheWeb,
iOS,andAndroid,allwiththesameskillset.Byremovingtheneedto“silo”developers
basedontheirtargetplatform,ReactNativeletsyourteamiteratemorequickly,andshare
knowledgeandresourcesmoreeffectively.
Aswithanything,usingReactNativeisnotwithoutitsdownsides,andwhetherornot
ThelargestriskisprobablyReactNative’smaturity,astheprojectisstillrelativelyyoung.
iOSsupportwasreleasedinMarch2015,andAndroidsupportwasreleasedinSeptember
2015.Thedocumentationcertainlyhasroomforimprovement,andcontinuestoevolve.
SomefeaturesoniOSandAndroidstillaren’tsupported,andthecommunityisstill
discoveringbestpractices.Thegoodnewsisthatinthevastmajorityofcases,youcan
implementsupportformissingAPIsyourself,whichwe’llcoverinChapter7.
BecauseReactNativeintroducesanotherlayertoyourproject,itcanalsomakedebugging
hairier,especiallyattheintersectionofReactandthehostplatform.We’llcover
debuggingforReactNativeinmoredepthinChapter8,andtrytoaddresssomeofthe
mostcommonissues.
ReactNativeisanexcitingframeworkthatenableswebdeveloperstocreaterobustmobile
applicationsusingtheirexistingJavaScriptknowledge.Itoffersfastermobile
development,andmoreefficientcodesharingacrossiOS,Android,andtheWeb,without
sacrificingtheenduser’sexperienceorapplicationquality.Thetradeoffisthatit’snew,
andstillaworkinprogress.Ifyourteamcanhandletheuncertaintythatcomeswith
workingwithanewtechnology,andwantstodevelopmobileapplicationsformorethan
justoneplatform,youshouldbelookingatReactNative.
Inthenextchapter,we’llgooversomeofthemainwaysinwhichReactNativediffers
fromReactfortheWeb,andcoversomekeyconcepts.Ifyou’dliketoskipstraightto
developing,feelfreetojumptoChapter3,inwhichwe’llhandlesettingupour
Inthischapter,we’llcoverthe“bridge,”andreviewhowReactNativeworksunderthe
hood.Then,we’lllookathowReactNativecomponentsdifferfromtheirweb
counterparts,andcoverwhatyou’llneedtoknowinordertocreateandstylecomponents
formobile.
<b>NOTE</b>
TheideaofwritingmobileapplicationsinJavaScriptfeelsalittleodd.Howisitpossible
touseReactinamobileenvironment?Inordertounderstandthetechnicalunderpinnings
ofReactNative,firstwe’llneedtorecalloneofReact’sfeatures,theVirtualDOM.
InReact,theVirtualDOMactsasalayerbetweenthedeveloper’sdescriptionofhow
thingsoughttolook,andtheworkdonetoactuallyrenderyourapplicationontothepage.
Torenderinteractiveuserinterfacesinabrowser,developersmusteditthebrowser’s
DOM,orDocumentObjectModel.Thisisanexpensivestep,andexcessivewritestothe
DOMhaveasignificantimpactonperformance.Ratherthandirectlyrenderchangeson
thepage,Reactcomputesthenecessarychangesbyusinganin-memoryversionofthe
DOM,andrerenderstheminimalamountnecessary.Figure2-1showshowthisworks.
<i>Figure2-1.PerformingcalculationsintheVirtualDOMlimitsrerenderingintheBrowserDOM</i>
InthecontextofReactontheWeb,mostdevelopersthinkoftheVirtualDOMprimarily
asaperformanceoptimization.TheVirtualDOMcertainlyhasperformancebenefits,but
itsrealpotentialliesinthepowerofitsabstraction.Placingacleanabstractionlayer
betweenthedeveloper’scodeandtheactualrenderingopensupalotofinteresting
possibilities.WhatifReactcouldrendertoatargetotherthanthebrowser’sDOM?After
<i>all,Reactalready“understands”whatyourapplicationissupposedtolooklike.</i>
components,orJavaAPIstorendertoAndroidcomponents.ThissetsReactNativeapart
fromothercross-platformappdevelopmentoptions,whichoftenenduprenderingweb-basedviews.
<i>Figure2-2.Reactcanrendertodifferenttargets</i>
Thisisallpossiblebecauseofthe“bridge,”whichprovidesReactwithaninterfaceinto
thehostplatform’snativeUIelements.Reactcomponentsreturnmarkupfromtheir
renderfunction,whichdescribeshowtheyshouldlook.WithReactfortheWeb,this
translatesdirectlytothebrowser’sDOM.ForReactNative,thismarkupistranslatedto
suitthehostplatform,soa<View>mightbecomeaniOS-specificUIView.
ReactNativecurrentlysupportsiOSandAndroid.Becauseoftheabstractionlayer
IfyouareaccustomedtoworkinginReact,theReactlifecycleshouldbefamiliartoyou.
WhenReactrunsinthebrowser,therenderlifecyclebeginsbymountingyourReact
components(Figure2-3).
<i>Figure2-3.MountingcomponentsinReact</i>
Afterthat,Reacthandlestherenderingandrerenderingofyourcomponentasnecessary
(Figure2-4).
<i>Figure2-4.RerenderingcomponentsinReact</i>
Fortherenderstage,thedeveloperreturnsHTMLmarkupfromaReactcomponent’s
rendermethod,whichReactthenrendersdirectlyintothepageasnecessary.
ForReactNative,thelifecycleisthesame,buttherenderingprocessisslightlydifferent,
becauseReactNativedependsonthebridge.Welookedatthebridgebrieflyearlierin
Figure2-2.ThebridgetranslatesJavaScriptcallsandinvokesthehostplatform’s
WhenwritinginReactfortheWeb,yourendernormalHTMLelements(<div>,<p>,
<span>,<a>
,etc.).WithReactNative,alloftheseelementsarereplacedbyplatform-specificReactcomponents(seeTable2-1).Themostbasicisthecross-platform<View>,a
simpleandflexibleUIelementthatcanbethoughtofasanalogoustothe<div>.OniOS,
forinstance,the<View>componentrenderstoaUIView,whileonAndroiditrenderstoa
View.
<i>Table2-1.Basic</i>
<i>elementsforthe</i>
<i>Web,comparedwith</i>
<i>ReactNative</i>
<b>React</b> <b>ReactNative</b>
<div> <View>
<span> <Text>
<li>,<ul> <ListView>
<img> <Image>
Othercomponentsareplatform-specific.Forinstance,the<DatePickerIOS>component
(predictably)renderstheiOSstandarddatepicker.HereisanexcerptfromtheUIExplorer
sampleapp,demonstratinganiOSdatepicker.Theusageisstraightforward,asyouwould
expect:
<DatePickerIOS
date={this.state.date}
mode="date"
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours*60}
/>
<i>Figure2-5.TheDatePickerIOSis,asthenamewouldsuggest,iOS-specific</i>
BecauseallofourUIelementsarenowReactcomponents,ratherthanbasicHTML
elementslikethe<div>,youwillneedtoexplicitlyimporteachcomponentyouwishto
use.Forinstance,weneededtoimportthe<DatePickerIOS>componentlikeso:
<b>var</b>React=require('react-native');
<b>var{</b>
DatePickerIOS
}=React;
TheUIExplorerapplication,whichisbundledintothestandardReactNativeexamples,
allowsyoutoviewallofthesupportedUIelements.Iencourageyoutoexaminethe
variouselementsincludedintheUIExplorerapp.Italsodemonstratesmanystyling
optionsandinteractions.
<b>TIP</b>
Platform-specificcomponentsandAPIshavespecialtagsinthedocumentation,andtypicallyusethe
platformnameasasuffix—forexample,<SwitchAndroid>and<SwitchIOS>.
Becausethesecomponentsvaryfromplatformtoplatform,howyoustructureyourReact
componentsbecomesevenmoreimportantwhenworkinginReactNative.InReactfor
theWeb,weoftenhaveamixofReactcomponents:somemanagelogicandtheirchild
components,whileothercomponentsrenderrawmarkup.Ifyouwanttoreusecodewhen
workinginReactNative,maintainingseparationbetweenthesetypesofcomponents
becomescritical.AReactcomponentthatrendersa<DatePickerIOS>elementobviously
cannotbereusedforAndroid.However,acomponentthatencapsulatestheassociated
platform.Youcanalsodesignateplatform-specificversionsofcomponents,ifyouwant,
<i>soyoucanhaveapicker.ios.jsandapicker.android.jsfile,forinstance.We’llcoverthisin</i>
<b>var</b>HelloMessage=React.createClass({
displayName:"HelloMessage",
render:functionrender(){
returnReact.createElement(
"div",
null,
"Hello",
this.props.name
);
}
});
React.render(React.createElement(HelloMessage,{name:"Bonnie"}),mountNode);
WecanrenderthismoresuccinctlybyusingJSX.Insteadofcalling
React.createElementandpassinginalistofHTMLattributes,weuseXML-like
markup:
<b>var</b>HelloMessage=React.createClass({
render:function(){
<i>//InsteadofcallingcreateElement,wereturnmarkup</i>
return<div>Hello{this.props.name}</div>;
}
});
<i>//WenolongerneedacreateElementcallhere</i>
React.render(<HelloMessagename="Bonnie"/>,mountNode);
BothofthesewillrenderthefollowingHTMLontothepage:
examplesthatshipswithReactNative.
ReactNativealsoinsistsontheuseofinlinestyles,whichexistasJavaScriptobjects.The
ReactteamhasadvocatedforthisapproachbeforeinReactforwebapplications.Ifyou
havepreviouslyexperimentedwithinlinestylesinReact,thesyntaxwilllookfamiliarto
you:
<i>//Defineastyle…</i>
<b>var</b>style={
backgroundColor:'white',
fontSize:'16px'
};
<i>//...andthenapplyit.</i>
<b>var</b>tv=(
<Textstyle={style}>
AstyledText
</Text>);
ReactNativealsoprovidesuswithsomeutilitiesforcreatingandextendingstyleobjects
thatmakedealingwithinlinestylesamoremanageableprocess.Wewillexplorethose
later,inChapter5.
Settingupyourdevelopmentenvironmentwillenableyoutofollowalongwiththe
examplesinthebook,andwillletyouwriteyourownapplications!
InstructionsforinstallingReactNativecanbefoundintheofficialReactNative
documentation.Theofficialsitewillbeyourmostup-to-datereferencepointforspecific
installationsteps,butwe’llwalkthroughthemhereaswell.
YouwillneedtouseHomebrew,acommonpackagemanagerforOSX,inordertoinstall
ReactNative’sdependencies.Throughoutthisbook,wewillassumethatyouare
developingonOSX,whichallowsyoutowritebothiOSandAndroidapplications.
OnceyouhaveHomebrewinstalled,runthefollowingfromthecommandline:
<b>brewinstallnode</b>
<b>brewinstallwatchman</b>
<b>brewinstallflow</b>
TheReactNativepackagerusesbothnodeandwatchman,soifthepackagergivesyou
troubleinthefutureit’sworthupdatingthesedependencies.flow
isFacebook’stype-checkinglibrary,andisalsousedbyReactNative.(Ifyouwanttoenabletype-checkingin
yourReactNativeprojects,youcanuseflow!)
Ifyouencounterdifficulties,youmayneedtoupdatebrewandupgradeanypackages
(notethatthesecommandsmaytakealittlewhiletorun):
<b>brewupdate</b>
<b>brewupgrade</b>
Nowthatyouhavenodeinstalled,youcanusenpm(theNodePackageManager)toinstall
theReactNativecommand-linetools:
<b>npminstall-greact-native-cli</b>
ThisinstallstheReactNativecommand-linetoolsgloballyonyoursystem.Afterthisis
done,congrats;ReactNativeisinstalled!
InordertodevelopandreleaseappsforiOS,youwillneedtoacquireaniOSdeveloper’s
account.Theaccountisfree,andissufficientfordevelopment.Fordeployingapplications
totheiOSAppStore,you’lleventuallyneedalicense,whichispricedat$99/year.
Ifyouhaven’tdonesoalready,you’llwanttodownloadandinstallXcode,whichincludes
theXcodeIDE,theiOSsimulators,andtheiOSSDK.YoucandownloadXcodefromthe
AppStoreorfromtheXcodewebsite.
Androidsetupisamultistepprocess.Youshouldchecktheofficialdocumentationforthe
mostup-to-dateinstructions.Notethattheseinstructionsassumeyoudon’talreadyhave
yourenvironmentsetupforAndroiddevelopment.Ingeneralterms,therearethreemain
phases:installingtheSDKs,installingthesimulatortools,andcreatingsimulatorsforuse.
First,you’llneedtoinstalltheJDK(JavaDevelopmentKit)andAndroidSDKs:
1. InstallthelatestJDK.
2. InstalltheAndroidSDK,usingbrewinstallandroid-sdk.
3. ExportyourANDROID_HOME<i>variableappropriatelyinyourshellconfigfile(~/.bashrc,</i>
<i>~/.zshrc,orwhicheveryourshelluses):</i>
exportANDROID_HOME=/usr/local/opt/android-sdk
ThisenvironmentvariableisusedformanyAndroid-relateddevelopmenttasks;makesure
tosourceyourshellconfigafteraddingit.
<i>Figure3-1.TheAndroidSDKManagerallowsyoutochoosewhichpackagestoinstall</i>
WaitfortheSDKManagertoupdateanddownloadthepackagelisting.Somepackages
willalreadybecheckedbydefault.Alsomakesuretochecktheboxesfor:
AndroidSDKBuild-toolsversion23.0.1
Android6.0(API23)
AndroidSupportRepository
Then,clickInstallPackagesandacceptanyapplicablelicenses.It’lltakealittlewhilefor
everythingtoinstall.
Next,you’llwanttoinstallthesimulatorandrelatedtools.
StartanewshellandrunandroidagaintolaunchtheAndroidSDKManager.We’re
goingtoinstallafewmorepackages:
Intelx86EmulatorAccelerator(HAXMinstaller)
Onceagain,clickInstallPackagesandacceptanyapplicablelicenses.
ThesepackagesgiveustheabilitytocreateAndroidVirtualDevices(AVDs),or
emulators,butwedon’tactuallyhaveanyemulatorscreatedyet.Let’scorrectthat.Launch
theAVDManager(showninFigure3-2)byrunning:
<b>androidavd</b>
<i>Figure3-2.TheAVDmanagerletsyoucreateandlaunchemulators</i>
<i>Figure3-3.Youcancreatewhicheveremulatorsyoulike(inthisexample,I’vecreatedaGalaxyNexusemulator)</i>
<i>Figure3-4.BesuretocheckUseHostGPU—otherwiseyouremulatorwillbeveryslow!</i>
YoucanusetheReactNativecommand-linetoolstocreateanewapplication.Thiswill
generateafreshprojectwithalloftheReactNative,iOS,andAndroidboilerplateforyou:
<b>react-nativeinitFirstProject</b>
TheresultingdirectoryshouldhavethestructureshowninFigure3-5.
<i>Figure3-5.Filestructureinthedefaultproject</i>
<i>Theios/andandroid/directoriescontainboilerplaterelevanttothoseplatforms.Your</i>
<i>Reactcodeislocatedintheindex.ios.jsandandroid.ios.jsfiles,whicharetherespective</i>
entrypointsforyourReactapplication.Dependenciesinstalledvianpmcan,asusual,be
Forstarters,we’lltryrunningtheiOSversionofourReactNativeapplication,bothinthe
simulatorandonaphysicaldevice.
<i>OpentheFirstProject.xcodeprojfile,locatedintheios/directory,inXcode.Inthetopleft,</i>
you’llnoticeaRunbutton,asshowninFigure3-6.Pressingthiswillbuildandrunyour
application.YoucanalsochangethedeploytargetheretoadifferentiOSsimulator.
<i>Figure3-6.TheRunbutton,withdeploytargetselector</i>
WhenyoupressRun,theReactpackagershouldautomaticallylaunchinanewterminal
window.Ifitfailstolaunch,orprintsanerror,tryrunningnpminstallandnpmstart
<i>fromtheFirstProject/directory.</i>
ItshouldlooklikethescreenshotshowninFigure3-7.
<i>Figure3-7.TheReactpackager</i>
<i>Figure3-8.Screenshotofthedefaultapp</i>
TouploadyourReactNativeapplicationtoaphysicaliOSdevice,youwillneedaniOS
developeraccountwithApple.Youwillthenneedtogenerateacertificateandregister
yourdevice.AfterregisteringwithApple,openXcode’spreferencesandaddyour
account,asshowninFigure3-9.
<i>Figure3-9.AddyouraccountinXcode’sPreferencespane</i>
<i>Figure3-10.Screenshotofthedefaultapp</i>
Havingobtainedacertificate,you’renearlydone.ThefinalstepistologontoApple
Developerandregisteryourdevice(seeFigure3-11).
<i>Figure3-11.RegisteringyourdeviceintheiOSdevelopermembercenter</i>
Obtainingyourdevice’sUDIDissimple.OpeniTunes,andselectyourdevice.Then,click
ontheserialnumber;itshouldnowdisplaytheUDIDinstead,andtheUDIDwillbe
copiedovertoyourclipboard.
OnceyouhaveregisteredyourdevicewithApple,itshouldappearinyourlistof
approveddevices.
Thisregistrationprocesscanalsobeusedlateron,ifyouwishtodistributeanearly
releasetoothertestdevices.Forindividualdevelopers,Applegivesyouanallotmentof
100devicesperyearthroughthedeveloperprogram.
Lastly,weneedtomakeaquickchangetoourcodebeforewecandeploy.Youwillneed
<i>toalteryourAppDelegate.mfiletoincludeyourMac’sIPaddressinsteadoflocalhost.If</i>
youdonotknowhowtofindyourcomputer’sIPaddress,youcanrunifconfigandthen
usetheinetvalueunderen0.
Forexample,ifyourIPaddresswas10.10.12.345,youshouldeditthejsCodeLocationto
looklikethis:
[NSURLURLWithString:@"http://10.10.12.345:8081/index.ios.bundle"];
Phew!Withallofthatoutoftheway,wecanselectaphysicaldeviceasthedeploytarget
inXcode(seeFigure3-12).
<i>Figure3-12.SelectyouriOSdeviceasthedeploytarget</i>
TorunaReactNativeapplicationforAndroid,you’llneedtodotwothings:startthe
emulator,andthenrunyourapplication.
Earlier,inFigure3-2,wesawthatwecanlaunchtheAVDManagerbyrunning:
<b>androidavd</b>
SelecttheemulatoryouwouldliketorunandthenclicktheStart…button
Alternatively,youcanlaunchyouremulatorsfromthecommandline.Toviewavailable
emulators,type:
<b>emulator-list-avds</b>
Thenlaunchthembyname,prefixedbythe@symbol.Forinstance,IhaveanAVDnamed
“galaxy,”soIcanrunthefollowing:
<b>emulator@galaxy</b>
Regardlessofhowyouchosetostartyouremulator,onceit’srunning,launchyourReact
Nativeapplicationbyrunningthefollowingfromyourproject’srootdirectory:
Wecoveredalotofgroundhere!BecauseweneededtoinstalldependenciesforReact
Native,iOS,andAndroiddevelopment,thatmighthavefeltlikealotofwork.
RCTRootView*rootView=
[[RCTRootViewalloc]initWithBundleURL:jsCodeLocation
moduleName:@"FirstProject"
launchOptions:launchOptions];
TheReactNativelibraryprefixesallofitsclasseswithRCT,meaningthatRCTRootViewis
aReactNativeclass.Inthiscase,theRCTRootViewrepresentstherootReactview.The
<i>remainderoftheboilerplatecodeinAppDelegate.mhandlesattachingthisviewtoa</i>
UIViewControllerandrenderingtheviewtothescreen.Thesestepsareanalogousto
mountingaReactcomponenttoaDOMnodewithacalltoReact.render.
<i>Fornow,theAppDelegate.mfilecontainstwothingsthatyououghttoknowhowto</i>
modify.
ThefirstisthejsCodeLocationline,whichweeditedearlierinordertodeploytoa
physicaldevice.Asthecommentsinthegeneratedfileexplain,thefirstoptionisusedfor
development,whilethesecondoptionisusedfordeployingwithaprebundledfileondisk.
Fornow,wewillleavethefirstoptionuncommented.Later,oncewepreparetodeploy
applicationstotheAppStore,wewilldiscussthesetwoapproachesinmoredetail.
You’llalsoneedtomodifythemoduleName,whichispassedtotheRCTRootViewand
determineswhichcomponentwillbemountedintheview.Thisiswhereyoucanchoose
whichcomponentshouldberenderedbyyourapplication.
InordertousetheFirstProjectcomponenthere,youneedtoregisteraReactcomponent
<i>withthesamename.Ifyouopenupindex.ios.js,you’llseethatthisisaccomplishedonthe</i>
lastline(Example3-2).
<i>Example3-2.Registeringthetop-levelcomponent</i>
AppRegistry.registerComponent('FirstProject',()=>FirstProject);
<i>ThisexposestheFirstProjectcomponentsothatwecanuseitinAppDelegate.m.Forthe</i>
mostpart,youwillnotneedtomodifythisboilerplate,butit’sgoodtoknowthatit’sthere.
<i>WhataboutonAndroid?Thestoryisprettysimilar.IfyoulookatMainActivity.java,</i>
you’llnoticethelineshowninExample3-3.
<i>Example3-3.ReactentrypointforAndroidisinMainActivity.java</i>
mReactRootView.startReactApplication(mReactInstanceManager,"FirstProject",null);
<i>Let’stakeacloserlookattheindex.ios.jsfile.Asyoucanseein</i>Example3-4,therequire
statementsusedareabitdifferentthannormal.
<i>Example3-4.requirestatementsinReactNative,andimportingUIelements</i>
varReact=require('react-native');
var{
AppRegistry,
StyleSheet,
Text,
View,
}=React;
There’ssomeinterestingsyntaxgoingonhere.Reactisrequiredasusual,butwhatis
happeningonthenextline?
OnequirkofworkingwithReactNativeisthatyouneedtoexplicitlyrequireevery
Native-providedmoduleyouworkwith.Thingslike<div>don’tsimplyexist;instead,
youneedtoexplicitlyimportcomponentssuchas<View>and<Text>.Libraryfunctions
suchasStylesheetandAppRegistryarealsoexplicitlyimportedusingthissyntax.Once
westartbuildingourownapplications,wewillexploretheotherReactNativefunctions
thatyoumayneedtoimport.
Let’stakealookatthe<FirstProject>component(Example3-5),whichisduplicated
<i>betweenindex.ios.jsandindex.android.js(inotherwords,youcanexamineeither,as</i>
they’reidentical).
Thisshouldalllookcomfortablyfamiliar,because<FirstProject>iswrittenjustlikean
ordinaryReactcomponent.Themaindifferenceisitsuseof<Text>and<View>
componentsinsteadof<div>and<span>,andtheuseofstyleobjects.
<i>Example3-5.FirstProjectcomponent,withstyles</i>
<b>var</b>FirstProject=React.createClass({
render:function(){
return(
<Viewstyle={styles.container}>
<Textstyle={styles.welcome}>
WelcometoReactNative!
</Text>
<Textstyle={styles.instructions}>
Togetstarted,editindex.ios.js
</Text>
<Textstyle={styles.instructions}>
PressCmd+Rtoreload,{'\n'}
Cmd+Dorshakefordevmenu
</Text>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#F5FCFF',
},
welcome:{
fontSize:20,
textAlign:'center',
margin:10,
},
instructions:{
textAlign:'center',
color:'#333333',
});
AsImentionedearlier,allstylinginReactNativeisdonewithstyleobjectsratherthan
stylesheets.ThestandardmethodofdoingthisisbyutilizingtheStyleSheetlibrary.You
canseehowthestyleobjectsaredefinedtowardthebottomofthefile.Notethatonly
<Text>componentscantaketext-specificstyleslikefontSize,andthatalllayoutlogicis
handledbyflexbox.Wewilldiscusshowtobuildlayoutswithflexboxatgreaterlength
lateroninChapter5.
Thesampleapplicationisagooddemonstrationofthebasicfunctionsyouwillneedto
createReactNativeapplications.ItmountsaReactcomponentforrendering,and
Wewillbebuildingoffofthesampleapplicationtocreateaweatherapp(youcancreatea
newoneforthisexamplewithreact-nativeinitWeatherProject).Thiswillgiveusa
chancetoexplorehowtoutilizeandcombinestylesheets,flexbox,network
communication,userinput,andimagesintoausefulappwecanthendeploytoan
AndroidoriOSdevice.
Thissectionmayfeellikeabitofablur,aswe’llbefocusingonanoverviewofthese
featuresratherthandeepexplanationsofthem.TheWeatherAppwillserveasauseful
referenceinfuturesectionsaswediscussthesefeaturesinmoredetail.Don’tworryifit
feelslikewe’removingquickly!
<i>Figure3-13.Thefinishedweatherapp</i>
Thefirstthingwe’lldoisreplacethedefaultcode.Movetheinitialcomponentoutintoits
<i>ownfile,WeatherProject.js,andreplacethecontentsofindex.ios.jsandindex.android.js.</i>
<i>Example3-6.Simplifiedcontentsofindex.ios.jsandindex.android.js(theyshouldbe</i>
<i>identical)</i>
<b>var</b>React=require('react-native');
<b>var{</b>AppRegistry}=React;
<b>var</b>WeatherProject=require('./WeatherProject');
Wewanttheusertobeabletoinputazipcodeandgettheforecastforthatarea,sowe
needtoaddatextfieldforuserinput.Wecanstartbyaddingzipcodeinformationtoour
component’sinitialstate(seeExample3-7).
<i>Example3-7.Addthistoyourcomponent,beforetherenderfunction</i>
getInitialState:function(){
return{
zip:''
};
}
RememberthatgetInitialStateishowwesetuptheinitialstatevaluesforReact
components.IfyouneedareviewoftheReactcomponentlifecycle,seetheReactdocs.
Then,weshouldalsochangeoneofthe<Text>componentstodisplaythis.state.zip:
<Textstyle={styles.welcome}>
Youinput{this.state.zip}.
</Text>
Withthatoutoftheway,let’sadda<TextInput>component(thisisabasiccomponent
thatallowstheusertoentertext):
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
The<TextInput>componentisdocumentedontheReactNativesite,alongwithits
properties.Youcanalsopassthe<TextInput>additionalcallbacksinordertolistento
otherevents,suchasonChangeoronFocus,butwedonotneedthematthemoment.
Notethatwe’veaddedasimplestyletothe<TextInput>.Addtheinputstyletoyour
stylesheet:
varstyles=StyleSheet.create({
...
input:{
fontSize:20,
borderWidth:2,
height:40
}
ThecallbackwepassedastheonSubmitEditingproplookslikethis,andshouldbeadded
asafunctiononthecomponent:
_handleTextChange(event){
console.log(event.nativeEvent.text);
this.setState({zip:event.nativeEvent.text})
}
yousodesire.
Youwillalsoneedtoupdateyourimportstatements:
<b>var</b>React=require('react-native');
<b>var{</b>
...
TextInput
...
}=React;
Now,tryrunningyourapplicationusingtheiOSsimulator.Itwon’tbepretty,butyou
shouldbeabletosuccessfullysubmitazipcodeandhaveitbereflectedinthe<Text>
component.
Ifwewanted,wecouldaddsomesimpleinputvalidationheretoensurethattheusertyped
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
TextInput,
Image
}=React;
<b>var</b>WeatherProject=React.createClass({
<i>//Ifyouwanttohaveadefaultzipcode,youcouldaddonehere</i>
getInitialState(){
return({
zip:''
});
},
<i>//We'llpassthiscallbacktothe<TextInput></i>
_handleTextChange(event){
<i>//logstatementsareviewableinXcode,</i>
<i>//ortheChromedebugtools</i>
console.log(event.nativeEvent.text);
this.setState({
zip:event.nativeEvent.text
});
},
render(){
return(
<Viewstyle={styles.container}>
<Textstyle={styles.welcome}>
Youinput{this.state.zip}.
</Text>
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
backgroundColor:'#F5FCFF',
},
welcome:{
fontSize:20,
textAlign:'center',
margin:10,
},
input:{
fontSize:20,
borderWidth:2,
height:40
}
});
Nowlet’sworkondisplayingtheforecastforthatzipcode.Wewillstartbyaddingsome
mockdatatogetInitialStateinWeatherProject.js:
getInitialState(){
return{
zip:'',
forecast:{
main:'Clouds',
description:'fewclouds',
temp:45.7
}
}
}
Forsanity’ssake,let’salsopulltheforecastrenderingintoitsowncomponent.Makea
<i>newfilecalledForecast.js(see</i>Example3-9).
<i>Example3-9.ForecastcomponentinForecast.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View
}=React;
<b>var</b>Forecast=React.createClass({
render:function(){
return(
<View>
<Textstyle={styles.bigText}>
{this.props.main}
</Text>
<Textstyle={styles.mainText}>
Currentconditions:{this.props.description}
</Text>
<Textstyle={styles.bigText}>
{this.props.temp}°F
</Text>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
bigText:{
flex:2,
fontSize:20,
textAlign:'center',
margin:10,
color:'#FFFFFF'
},
mainText:{
flex:1,
fontSize:16,
textAlign:'center',
color:'#FFFFFF'
}
})
module.exports=Forecast;
The<Forecast>componentjustrenderssome<Text>basedonitsprops.We’vealso
includedsomesimplestylesatthebottomofthefile,tocontrolthingsliketextcolor.
itpropsbasedonthethis.state.forecast(seeExample3-10).We’lladdressissueswith
layoutandstylinglater.Youcanseehowthe<Forecast>componentappearsinthe
resultingapplicationinFigure3-14.
<i>Example3-10.WeatherProject.jsshouldbeupdatedwithnewstateandtheForecast</i>
<i>component</i>
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
TextInput,
Image
}=React;
<b>var</b>Forecast=require('./Forecast');
<b>var</b>WeatherProject=React.createClass({
getInitialState(){
return{
zip:'',
forecast:{
main:'Clouds',
description:'fewclouds',
temp:45.7
}
}
},
_handleTextChange(event){
console.log(event.nativeEvent.text);
zip:event.nativeEvent.text
});
},
render(){
return(
<Viewstyle={styles.container}>
<Textstyle={styles.welcome}>
Youinput{this.state.zip}.
</Text>
<Forecast
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>
<TextInput
style={styles.input}
returnKeyType='go'
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#4D4D4D',
},
welcome:{
fontSize:20,
textAlign:'center',
margin:10,
},
input:{
fontSize:20,
borderWidth:2,
height:40
<b>ASSETINCLUSIONISPLATFORM-SPECIFIC</b>
AndroidandiOShavedifferentrequirementsforaddingassetstoyourprojects.We’llcoverbothhere.
Assetssuchasimagesneedtobeaddedtoyourprojectbasedonwhichplatformyou’re
buildingfor.We’llstartwithXcode.
<i>SelecttheImages.xcassets/folder,andthenselecttheNewImageSetoption,asshownin</i>
Figure3-15.Then,youcandraganddropanimageintotheset.Figure3-16showsthe
resultingImageSet.Makesuretheimageset’snamematchesthefilename,otherwise
ReactNativewillhavedifficultyimportingit.
<i>Figure3-15.Addanewimageset</i>
<i>Figure3-16.Dragyourimagefilesintotheimagesettoaddthem</i>
The@2xand@3xdecoratorsindicateanimagewitharesolutionoftwiceandthricethe
baseresolution,respectively.BecausetheWeatherAppisdesignatedasauniversal
application(meaningonethatcanrunoniPhoneoriPad),Xcodegivesustheoptionof
uploadingimagesatthevariousappropriateresolutions.
ForAndroid,wehavetoaddourfilesasbitmapdrawableresourcestotheappropriate
<i>foldersinWeatherProject/android/app/src/main/res.You’llwanttocopythe.pngfileinto</i>
thefollowingresolution-specificdirectories(seeFigure3-17):
<i>Figure3-17.AddingimagefilestoAndroid</i>
Afterthat,theimagewillbeavailabletoyourAndroidapplication.
Ifthisworkflowfeelssuboptimal,that’sbecauseitis.Itwillprobablychangeinfuture
versionsofReactNative.
propertyona<div>likewecandoontheWeb.Instead,weusean<Image>componentas
acontainer:
<Imagesource={require('image!flowers')}
resizeMode='cover'
style={styles.backdrop}>
<i>//Yourcontenthere</i>
</Image>
The<Image>componentexpectsasourceprop,whichwegetbyusingrequire.Thecall
torequire(image!flowers)<i>willcauseReactNativetosearchforafilenamedflowers.</i>
Don’tforgettostyleitwithflexDirectionsothatitschildrenrenderaswe’dlikethem
to:
backdrop:{
flex:1,
flexDirection:'column'
}
Nowlet’sgivethe<Image>somechildren.Updatetherendermethodofthe
<WeatherProject>componenttoreturnthefollowing:
<Imagesource={require('image!flowers')}
resizeMode='cover'
style={styles.backdrop}>
<Viewstyle={styles.overlay}>
<Viewstyle={styles.row}>
Currentweatherfor
</Text>
<Viewstyle={styles.zipContainer}>
<TextInput
style={[styles.zipCode,styles.mainText]}
returnKeyType='go'
onSubmitEditing={this._handleTextChange}/>
</View>
</View>
<Forecast
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>
</View>
</Image>
Next,let’sexploreusingthenetworkingAPIsavailableinReactNative.Youwon’tbe
usingjQuerytosendAJAXrequestsfrommobiledevices!Instead,ReactNative
implementstheFetchAPI.ThePromise-basedsyntaxisfairlysimple:
fetch('')
.then((response)=>response.text())
.then((responseText)=>{
console.log(responseText);
});
WewillbeusingtheOpenWeatherMapAPI,whichprovidesuswithasimpleendpoint
thatreturnsthecurrentweatherforagivenzipcode.
TointegratethisAPI,wecanchangethecallbackonthe<TextInput>componenttoquery
theOpenWeatherMapAPI:
_handleTextChange:function(event){
varzip=event.nativeEvent.text;
this.setState({zip:zip});
fetch('+
zip+'&units=imperial')
.then((response)=>response.json())
.then((responseJSON)=>{
<i>//Takealookattheformat,ifyouwant.</i>
console.log(responseJSON);
this.setState({
forecast:{
main:responseJSON.weather[0].main,
description:responseJSON.weather[0].description,
temp:responseJSON.main.temp
}
});
})
.catch((error)=>{
console.warn(error);
});
}
NotethatwewanttheJSONfromtheresponse.TheFetchAPIisprettystraightforwardto
workwith,sothisisallwewillneedtodo.
Theotherthingthatwecandoistoremovetheplaceholderdata,andmakesurethatthe
forecastdoesnotrenderifwedonothavedatayet.
First,clearthemockdatafromgetInitialState:
getInitialState:function(){
return{
zip:'',
}
Then,intherenderfunction,updatetherenderinglogic:
<b>var</b>content=null;
<b>if(this.</b>state.forecast!==null){
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>;
}
Forthefinalversionoftheapplication,I’vereorganizedthe<WeatherProject>
component’srenderfunctionandtweakedthestyles.Themainchangeistothelayout
logic,diagrammedinFigure3-18.
<i>Figure3-18.Layoutofthefinishedweatherapplication</i>
OK.Readytoseeitallinoneplace?Example3-11showsthefinishedcodeforthe
<WeatherProject>componentinfull,includingthestylesheets.The<Forecast>
componentwillbethesameasaboveinExample3-9.
<i>Example3-11.FinishedcodeforWeatherProject.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
TextInput,
Image
}=React;
<b>var</b>Forecast=require('./Forecast');
<b>var</b>WeatherProject=React.createClass({
getInitialState:function(){
return{
zip:'',
forecast:null
};
},
fetch(' />
+zip+'&units=imperial')
.then((response)=>response.json())
.then((responseJSON)=>{
this.setState({
forecast:{
main:responseJSON.weather[0].main,
description:responseJSON.weather[0].description,
temp:responseJSON.main.temp
}
});
})
.catch((error)=>{
console.warn(error);
});
},
render:function(){
varcontent=null;
if(this.state.forecast!==null){
content=<Forecast
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>;
}
return(
<Viewstyle={styles.container}>
<Imagesource={require('image!flowers')}
resizeMode='cover'
style={styles.backdrop}>
<Viewstyle={styles.overlay}>
<Viewstyle={styles.row}>
<Textstyle={styles.mainText}>
Currentweatherfor
</Text>
<Viewstyle={styles.zipContainer}>
<TextInput
style={[styles.zipCode,styles.mainText]}
returnKeyType='go'
onSubmitEditing={this._handleTextChange}/>
</View>
</View>
{content}
</View>
}
});
<b>var</b>baseFontSize=16;
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
alignItems:'center',
paddingTop:30
},
backdrop:{
flex:1,
flexDirection:'column'
},
overlay:{
paddingTop:5,
backgroundColor:'#000000',
flexDirection:'column',
alignItems:'center'
},
row:{
flex:1,
flexDirection:'row',
flexWrap:'nowrap',
alignItems:'flex-start',
padding:30
zipContainer:{
flex:1,
borderBottomColor:'#DDDDDD',
borderBottomWidth:1,
marginLeft:5,
marginTop:3
},
zipCode:{
width:50,
height:baseFontSize,
},
mainText:{
flex:1,
fontSize:baseFontSize,
color:'#FFFFFF'
}
});
module.exports=WeatherProject;
Nowthatwe’redone,trylaunchingtheapplication.ItshouldworkonbothAndroidand
iOS,inanemulatororonyourphysicaldevice.Whatwouldyouliketochangeor
improve?
Forourfirstrealapplication,we’vealreadycoveredalotofground.Weintroducedanew
UIcomponent,<TextInput>,andlearnedhowtouseittogetinformationfromtheuser.
WedemonstratedhowtoimplementbasicstylinginReactNative,aswellashowtouse
imagesandincludeassetsinourapplication.Finally,welearnedhowtousetheReact
NativenetworkingAPItorequestdatafromexternalwebsources.Notbadforafirst
application!
Hopefully,thishasdemonstratedhowquicklyyoucanbuildReactNativeapplications
withusefulfeaturesthatfeelathomeonamobiledevice.
Ifyouwanttoextendyourapplicationfurther,herearesomethingstotry:
Addmoreimages,andchangethembasedontheforecast
Addvalidationtothezipcodefield
Switchtousingamoreappropriatekeypadforthezipcodeinput
Displaythefive-dayweatherforecast
Oncewecovermoretopics,suchasgeolocation,youwillbeabletoextendtheweather
applicationinevenmoreways.
InChapter3,webuiltasimpleweatherapp.Indoingso,wetoucheduponthebasicsof
buildinginterfaceswithReactNative.Inthischapter,wewilltakeacloserlookatthe
mobile-basedcomponentsusedforReactNative,andhowtheycomparetobasicHTML
elements.MobileinterfacesarebasedondifferentprimitiveUIelementsthanwebpages,
andthusweneedtousedifferentcomponents.
WhendevelopingfortheWeb,wemakeuseofavarietyofbasicHTMLelements.These
include<div>,<span>,and<img>,aswellasorganizationalelementssuchas<ol>,<ul>,
and<table>.(Wecouldincludeaconsiderationofelementssuchas<audio>,<svg>,
<canvas>,andsoon,butwe’llignorethemfornow.)
WhendealingwithReactNative,wedon’tusetheseHTMLelements,butweuseavariety
<i>Table4-1.Analogous</i>
<i>HTMLandNative</i>
<i>components</i>
<b>HTML</b> <b>ReactNative</b>
div View
img Image
span,p Text
ul/ol,li ListView,childitems
Althoughtheseelementsserveroughlythesamepurposes,theyarenotinterchangeable.
Let’stakealookathowthesecomponentsworkonmobilewithReactNative,andhow
theydifferfromtheirbrowser-basedcounterparts.
<b>CANISHARECODEBETWEENREACTNATIVEANDMYWEBAPP?</b>
InReactNative,only<Text>componentsmayhaveplaintextnodesaschildren.Inother
words,thisisnotvalid:
<b><View></b>
Textdoesn'tgohere!
<b></View></b>
Instead,wrapyourtextina<Text>component:
<b><View></b>
<Text>ThisisOK!</Text>
<b></View></b>
Whendealingwith<Text>componentsinReactNative,younolongerhaveaccessto
subtagssuchas<strong>and<em>,thoughyoucanapplystylestoachievesimilareffects
throughuseofattributessuchasfontWeightandfontStyle.Here’showyoumight
achieveasimilareffectbymakinguseofinlinestyles:
<Text>
Thequick<Textstyle={{fontStyle:"italic"}}>brown</Text>fox
jumpedoverthelazy<Textstyle={{fontWeight:"bold"}}>dog</Text>.
</Text>
Thisapproachcouldquicklybecomeverbose.You’lllikelywanttocreatestyled
componentsasasortofshorthandwhendealingwithtext,asshowninExample4-1.
<i>Example4-1.Creatingreusablecomponentsforstylingtext</i>
<b>var</b>styles=StyleSheet.create({
bold:{
fontWeight:"bold"
},
italic:{
fontStyle:"italic"
}
});
<b>var</b>Strong=React.createClass({
render:function(){
return(
<Textstyle={styles.bold}>
{this.props.children}
</Text>);
<b>var</b>Em=React.createClass({
render:function(){
return(
<Textstyle={styles.italic}>
{this.props.children}
</Text>);
}
});
Onceyouhavedeclaredthesestyledcomponents,youcanfreelymakeuseofstyled
nesting.NowtheReactNativeversionlooksquitesimilartotheHTMLversion(see
<Text>
Thequick<Em>brown</Em>foxjumped
overthelazy<Strong>dog</Strong>.
</Text>
Similarly,ReactNativedoesnotinherentlyhaveanyconceptofheaderelements(h1,h2,
etc.),butit’seasytodeclareyourownstyled<Text>elementsandusethemasneeded.
<i>Iftextisthemostbasicelementinanapplication,imagesareaclosecontender,forboth</i>
mobileandfortheWeb.WhenwritingHTMLandCSSfortheWeb,weincludeimagesin
avarietyofways:sometimesweusethe<img>tag,whileatothertimesweapplyimages
viaCSS,suchaswhenweusethebackground-imageproperty.InReactNative,wehavea
similar<Image>component,butitbehavesalittledifferently.
Thebasicusageofthe<Image>componentisstraightforward;justsetthesourceprop:
<Imagesource={require('image!puppies')}/>
Howdoesthatrequirecallwork?Wheredoesthisresourcelive?Here’sonepartofReact
Nativethatyou’llhavetoadjustbasedonwhichplatformyou’retargeting.OniOS,this
meansthatyou’llneedtoimportitintotheassetsfolderwithinyourXcodeproject.By
providingtheappropriate@2xand@3xresolutionfiles,youwillenableXcodetoserve
thecorrectassetfileforthecorrectplatform.Thisisanicechangefromwebdevelopment:
ForReactNativeonotherplatforms,wecanexpectthattheimage!requiresyntaxwill
pointtoasimilarassetsdirectory.
It’sworthmentioningthatitisalsopossibletoincludeweb-basedimagesourcesinstead
ofbundlingyourassetswithyourapplication.Facebookdoesthisasoneoftheexamples
intheUIExplorerapplication:
<Imagesource={{uri:' />style={{width:400,height:400}}/>
Whenutilizingnetworkresources,youwillneedtospecifydimensionsmanually.
Downloadingimagesviathenetworkratherthanincludingthemasassetshassome
advantages.Duringdevelopment,forinstance,itmaybeeasiertousethisapproachwhile
prototyping,ratherthancarefullyimportingallofyourassetsaheadoftime.Italso
reducesthesizeofyourbundledmobileapplication,sothatusersneedn’tdownloadallof
yourassets.However,itmeansthatinsteadyou’llberelyingontheuser’sdataplan
whenevertheyaccessyourapplicationinthefuture.Formostcases,you’llwanttoavoid
usingtheURI-basedmethod.
Ifyou’rewonderingaboutworkingwiththeuser’sownimages,we’llcoverthecamera
rollinChapter6.
<i>BecauseReactNativeemphasizesacomponent-basedapproach,imagesmustbeincluded</i>
asan<Image>componentinsteadofbeingreferencedviastyles.Forinstance,in
Chapter3,wewantedtouseanimageasabackgroundforourweatherapplication.
applyabackgroundimage,inReactNativeyouinsteadusethe<Image>asacontainer
component,likeso:
<Imagesource={require('image!puppies')}>
{<i>/*Yourcontenthere…*/</i>}
</Image>
Stylingtheimagesthemselvesisfairlystraightforward.Inadditiontoapplyingstyles,
certainpropscontrolhowtheimagewillberendered.You’lloftenmakeuseofthe
resizeModeprop,forinstance,whichcanbesettoresize,cover,orcontain.The
UIExplorerappdemonstratesthiswell(Figure4-1).
<i>Figure4-1.Thedifferencebetweenresize,cover,andcontain</i>
Web-basedinterfacesareusuallydesignedformouse-basedcontrollers.Weusethingslike
hoverstatetoindicateinteractivityandrespondtouserinteraction.Formobile,it’stouch
thatmatters.Mobileplatformshavetheirownnormsaroundinteractionsthatyou’llwant
todesignfor.Thisvariessomewhatfromplatformtoplatform:iOSbehavesdifferently
fromAndroid,whichbehavesdifferentlyyetagainfromWindowsPhone.
shoulduse<TouchableHighlight>anywheretherewouldbeabuttonoralinkonthe
Web.
Atitsmostbasicusage,youjustneedtowrapyourcomponentina
<TouchableHighlight>,whichwilladdasimpleoverlaywhenpressed.The
<TouchableHighlight>componentalsogivesyouhooksforeventssuchasonPressIn,
onPressOut,onLongPress,andthelike,soyoucanusetheseeventsinyourReact
applications.
Example4-3showshowyoucanwrapacomponentina<TouchableHighlight>inorder
togivetheuserfeedback.
<i>Example4-3.Usingthe<TouchableHighlight>component</i>
<TouchableHighlight
onPressIn={this._onPressIn}
onPressOut={this._onPressOut}
style={styles.touchable}>
<Viewstyle={styles.button}>
<Textstyle={styles.welcome}>
{this.state.pressing?'EEK!':'PUSHME'}
</Text>
</View>
</TouchableHighlight>
<i>Figure4-2.Using<TouchableHighlight>togivetheuservisualfeedback—theunpressedstate(left)andthepressed</i>
<i>state,withhighlight(right)</i>
Thisisacontrivedexample,butitillustratesthebasicinteractionsthatmakeabutton
“feel”touchableonmobile.Theoverlayisakeypieceoffeedbackthatinformstheuser
thatanelementcanbepressed.Notethatinordertoapplytheoverlay,wedon’tneedto
applyanylogictoourstyles;the<TouchableHighlight>handlesthelogicofthatforus.
Example4-4showsthefullcodeforthisbuttoncomponent.
<i>Example4-4.Touch/PressDemo.jsillustratestheuseof<TouchableHighlight></i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
TouchableHighlight
}=React;
pressing:false
}
},
_onPressIn:function(){
this.setState({pressing:true});
},
_onPressOut:function(){
this.setState({pressing:false});
},
render:function(){
return(
<Viewstyle={styles.container}>
<TouchableHighlight
onPressIn={this._onPressIn}
onPressOut={this._onPressOut}
style={styles.touchable}>
<Viewstyle={styles.button}>
<Textstyle={styles.welcome}>
{this.state.pressing?'EEK!':'PUSHME'}
</Text>
</View>
</TouchableHighlight>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#F5FCFF',
},
welcome:{
fontSize:20,
textAlign:'center',
margin:10,
color:'#FFFFFF'
},
touchable:{
borderRadius:100
},
button:{
backgroundColor:'#FF0000',
borderRadius:100,
height:200,
width:200,
justifyContent:'center'
},
});
module.exports=Button;
Tryeditingthisbuttontorespondtootherevents,byusinghookslikeonPressand
onLongPress.Thebestwaytogetasenseforhowtheseeventsmapontouserinteractions
Whatifyouwanttodomorethanjustmakethings“tappable”?ReactNativealsoexposes
twoAPIsforcustomtouchhandling:GestureResponderandPanResponder.
GestureResponderisalower-levelAPI,whilePanResponderprovidesauseful
abstraction.We’llstartbylookingathowtheGestureRespondersystemworks,because
it’sthebasisforthePanResponderAPI.
Touchonmobileisfairlycomplicated.Mostmobileplatformssupportmultitouch,which
<i>Thetouchresponderistheviewthathandlesagiventouchevent.Intheprevioussection,</i>
wesawthatthe<TouchableHighlight>componentactsasatouchresponder.Wecan
causeourowncomponentstobecomethetouchresponder,too.Thelifecyclebywhich
thisprocessisnegotiatedisalittlecomplicated.Aviewthatwishestoobtaintouch
responderstatusshouldimplementfourprops:
View.props.onStartShouldSetResponder
View.props.onMoveShouldSetResponder
View.props.onResponderGrant
View.props.onResponderReject
<i>Figure4-3.Obtainingtouchresponderstatus</i>
Yikes,thatlookscomplicated!Let’steasethisapart.First,atoucheventhasthreemain
<i>lifecyclestages:start,move,andrelease(thesecorrespondto</i>mouseDown,mouseMove,and
mouseUp<i>inthebrowser).Aviewcanrequesttobethetouchresponderduringthestartor</i>
<i>themovephase.ThisbehaviorisspecifiedbyonStartShouldSetResponderand</i>
onMoveShouldSetResponder.Whenoneofthosefunctionsreturnstrue,theviewattempts
toclaimresponderstatus.
<i>Afteraviewhasattemptedtoclaimresponderstatus,itsattemptmaybegrantedor</i>
<i>rejected.Theappropriatecallback—either</i>onResponderGrantoronResponderReject—
willbeinvoked.
Therespondernegotiationfunctionsarecalledinabubblingpattern.Ifmultipleviews
attempttoclaimresponderstatus,thedeepestcomponentwillbecometheresponder.This
istypicallythedesiredbehavior;otherwise,youwouldhavedifficultyaddingtouchable
componentssuchasbuttonstoalargerview.Ifyouwanttooverridethisbehavior,parent
componentscanmakeuseofonStartShouldSetResponderCaptureand
onMoveShouldSetResponderCapture.Returningtruefromeitherofthesewillpreventa
component’schildrenfrombecomingthetouchresponder.
<i>View.props.onResponderMove</i>
Theuserismovingherfinger
<i>View.props.onResponderRelease</i>
Firedattheendofthetouch(i.e.,“touchUp”)
<i>View.props.onResponderTerminationRequest</i>
Somethingelsewantstobecomeresponder.Shouldthisviewreleasetheresponder?
Returningtrueallowsrelease
<i>View.props.onResponderTerminate</i>
Thisisafairlylow-levelAPI;ifyouwanttodetectandrespondtogesturesinthisway,
Unlike<TouchableHighlight>,PanResponderisnotacomponent,butratheraclass
providedbyReactNative.Itprovidesaslightlyhigher-levelAPIthanthebasicevents
returnedbytheGestureRespondersystem,whilestillprovidingaccesstothoseraw
events.APanRespondergestureStateobjectgivesyouaccesstothefollowing,in
accordancewiththePanResponderdocumentation:
<i>stateID</i>
IDofthegestureState(persistedaslongasthereatleastonetouchonscreen)
<i>moveX</i>
Thelatestscreencoordinatesoftherecentlymovedtouch
<i>moveY</i>
Thelatestscreencoordinatesoftherecentlymovedtouch
<i>x0</i>
Thescreencoordinatesoftherespondergrant
<i>y0</i>
Thescreencoordinatesoftherespondergrant
<i>dx</i>
Accumulateddistanceofthegesturesincethetouchstarted
<i>dy</i>
Accumulateddistanceofthegesturesincethetouchstarted
<i>vx</i>
Currentvelocityofthegesture
<i>vy</i>
<i>Example4-5.CreatingaPanResponderrequiresustopassabunchofcallbacks</i>
<b>this.</b>_panResponder=PanResponder.create({
onPanResponderGrant:this._handlePanResponderGrant,
onPanResponderMove:this._handlePanResponderMove,
onPanResponderRelease:this._handlePanResponderEnd,
onPanResponderTerminate:this._handlePanResponderEnd,
});
Then,weusespreadsyntaxtoattachthePanRespondertotheviewinthecomponent’s
rendermethod(Example4-6).
<i>Example4-6.AttachingthePanResponderusingspreadsytax</i>
render:function(){
return(
<View
{...this._panResponder.panHandlers}>
{<i>/*Viewcontentshere*/</i>}
</View>
);
}
Afterthis,thehandlersthatyoupassedtothePanResponder.createcallwillbeinvoked
duringtheappropriatemoveevents,ifthetouchoriginateswithinthisview.
Example4-7showsamodifiedversionofthePanResponderexamplecodeprovidedby
ReactNative.Thisversionlistenstotoucheventsonthecontainerview,asopposedtojust
thecircle,andsothatthevaluesareprintedtothescreenasyouinteractwiththe
<i>Figure4-4.PanResponderdemo</i>
<i>Example4-7.Touch/PanDemo.jsillustratestheuseofPanResponder</i>
<i>//Adaptedfrom</i>
<i>// /><i>//Examples/UIExplorer/PanResponderExample.js</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
PanResponder,
View,
Text
}=React;
<b>var</b>CIRCLE_SIZE=40;
<b>var</b>CIRCLE_COLOR='blue';
<b>var</b>CIRCLE_HIGHLIGHT_COLOR='green';
<b>var</b>PanResponderExample=React.createClass({
<i>//Setsomeinitialvalues.</i>
_panResponder:{},
_previousLeft:0,
_previousTop:0,
_circleStyles:{},
circle:null,
getInitialState:function(){
return{
numberActiveTouches:0,
moveX:0,
moveY:0,
x0:0,
y0:0,
dx:0,
dy:0,
vx:0,
vy:0,
},
componentWillMount:function(){
this._panResponder=PanResponder.create({
onStartShouldSetPanResponder:this._handleStartShouldSetPanResponder,
onMoveShouldSetPanResponder:this._handleMoveShouldSetPanResponder,
onPanResponderGrant:this._handlePanResponderGrant,
onPanResponderMove:this._handlePanResponderMove,
onPanResponderRelease:this._handlePanResponderEnd,
onPanResponderTerminate:this._handlePanResponderEnd,
});
this._previousLeft=20;
this._previousTop=84;
this._circleStyles={
left:this._previousLeft,
top:this._previousTop,
};
},
componentDidMount:function(){
this._updatePosition();
},
render:function(){
return(
<Viewstyle={styles.container}>
<View
ref={(circle)=>{
this.circle=circle;
}}
style={styles.circle}
<Text>
{this.state.numberActiveTouches}touches,
dx:{this.state.dx},
dy:{this.state.dy},
vx:{this.state.vx},
vy:{this.state.vy}
</Text>
</View>
);
},
<i>//_highlightand_unHighlightgetcalledbyPanRespondermethods,</i>
<i>//providingvisualfeedbacktotheuser.</i>
_highlight:function(){
this.circle&&this.circle.setNativeProps({
backgroundColor:CIRCLE_HIGHLIGHT_COLOR
});
},
_unHighlight:function(){
this.circle&&this.circle.setNativeProps({
backgroundColor:CIRCLE_COLOR
});
},
<i>//We'recontrollingthecircle'spositiondirectlywithsetNativeProps.</i>
_updatePosition:function(){
this.circle&&this.circle.setNativeProps(this._circleStyles);
},
_handleStartShouldSetPanResponder:
function(e:Object,gestureState:Object):boolean{
<i>//Shouldwebecomeactivewhentheuserpressesdownonthecircle?</i>
returntrue;
},
_handleMoveShouldSetPanResponder:
function(e:Object,gestureState:Object):boolean{
<i>//Shouldwebecomeactivewhentheusermovesatouchoverthecircle?</i>
returntrue;
},
_handlePanResponderGrant:function(e:Object,gestureState:Object){
this._highlight();
},
_handlePanResponderMove:function(e:Object,gestureState:Object){
this.setState({
stateID:gestureState.stateID,
moveX:gestureState.moveX,
moveY:gestureState.moveY,
x0:gestureState.x0,
y0:gestureState.y0,
dx:gestureState.dx,
dy:gestureState.dy,
vx:gestureState.vx,
vy:gestureState.vy,
numberActiveTouches:gestureState.numberActiveTouches
});
<i>//Calculatecurrentpositionusingdeltas</i>
this._circleStyles.left=this._previousLeft+gestureState.dx;
this._circleStyles.top=this._previousTop+gestureState.dy;
this._updatePosition();
},
_handlePanResponderEnd:function(e:Object,gestureState:Object){
this._unHighlight();
this._previousLeft+=gestureState.dx;
this._previousTop+=gestureState.dy;
},
});
<b>var</b>styles=StyleSheet.create({
circle:{
borderRadius:CIRCLE_SIZE/2,
backgroundColor:CIRCLE_COLOR,
position:'absolute',
left:0,
top:0,
container:{
flex:1,
paddingTop:64,
},
});
module.exports=PanResponderExample;
<b>Choosinghowtohandletouch</b>
HowshouldyoudecidewhentousethetouchandgestureAPIsdiscussedinthissection?
Itdependsonwhatyouwanttobuild.
Inordertoprovidetheuserwithbasicfeedback,andindicatethatabuttonoranother
elementis“tappable,”usethe<TouchableHighlight>component.
Inordertoimplementyourowncustomtouchinterfaces,useeithertherawGesture
Respondersystem,oraPanResponder.Chancesarethatyouwillalmostalwayspreferthe
PanResponderapproach,becauseitalsogivesyouaccesstothesimplertouchevents
providedbytheGestureRespondersystem.Ifyouaredesigningagame,oranapplication
withanunusualinterface,you’llneedtospendsometimebuildingouttheinteractionsyou
wantbyusingtheseAPIs.
Inthissection,we’regoingtolookatorganizationalcomponentsthatyoucanuseto
<NavigatorView>,and<ListView>,whichallimplementsomeofthemostcommon
mobileinteractionandnavigationalpatterns.Onceyouhaveplannedoutyour
Let’sstartbyusingthe<ListView>component.Inthissection,wearegoingtobuildan
<i>appthatdisplaystheNewYorkTimesBestSellerListandletsusviewdataabouteach</i>
book,asshowninFigure4-5.Ifyou’dlike,youcangrabyourownAPItokenfromthe
<i>Figure4-5.TheBookListapplicationwe’llbebuilding</i>
Listsareextremelyusefulformobiledevelopment,andyouwillnoticethatmanymobile
userinterfacesfeaturethemasacentralelement.A<ListView>isliterallyjustalistof
views,optionallywithspecialviewsforsectiondividers,headers,orfooters.Forexample,
youcanseethisinteractionpatternintheDropbox,Twitter,andiOSSettingsapps
(Figure4-6).
<i>Figure4-6.ListsasusedbyDropbox,Twitter,andtheiOSSettingsapp</i>
<ListView>sareagoodexampleofwhereReactNativeshines,becauseitcanleverageits
hostplatform.Onmobile,thenative<ListView>elementisusuallyhighlyoptimizedso
thatrenderingissmoothandstutter-free.Ifyouexpecttorenderaverylargenumberof
itemsinyour<ListView>,youshouldtrytokeepthechildviewsrelativelysimple,totry
andreducestutter.
ThebasicReactNative<ListView>componentrequirestwoprops:dataSourceand
renderRow.dataSourceis,asthenameimplies,asourceofinformationaboutthedata
thatneedstoberendered.renderRowshouldreturnacomponentbasedonthedatafrom
oneelementofthedataSource.
<i>ThisbasicusageisdemonstratedinSimpleList.js.We’llstartbyaddinga</i>dataSourceto
our<SimpleList>component.AListView.DataSourceneedstoimplementthe
rowHasChangedmethod.Here’sasimpleexample:
TosettheactualcontentsofadataSource,weusecloneWithRows.Let’sreturnthe
dataSourceinourgetInitialStatecall:
getInitialState:function(){
vards=newListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2});
return{
dataSource:ds.cloneWithRows(['a','b','c','alongerexample','d','e'])
};
}
TheotherpropweneedisrenderRow,whichshouldbeafunctionthatreturnssomeJSX
basedonthedataforagivenrow:
_renderRow:function(rowData){
return<Textstyle={styles.row}>{rowData}</Text>;
}
Nowwecanputitalltogethertoseeasimple<ListView>,byrenderinga<ListView>
likeso:
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
<i>Figure4-7.TheSimpleListcomponentrendersabarebones<ListView></i>
Whatifwewanttodoalittlemore?Let’screatea<ListView>withmorecomplexdata.
WewillbeusingtheNewYorkTimesAPItocreateasimpleBestSellersapplication,
whichrenderstheNewYorkTimesBestSellerlist.
First,weinitializeourdatasourcetobeempty,becausewe’llneedtofetchthedata:
getInitialState:function(){
vards=newListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2});
return{
dataSource:ds.cloneWithRows([])
};
}
Then,weaddamethodforfetchingdata,andupdatethedatasourceoncewehaveit.This
methodwillgetcalledfromcomponentDidMount:
_refreshData:function(){
varendpoint=
' />=json&api-key='+API_KEY;
fetch(endpoint)
.then((response)=>response.json())
.then((rjson)=>{
this.setState({
dataSource:this.state.dataSource.cloneWithRows(rjson.results.books)
});
});
}
EachbookreturnedbytheNewYorkTimesAPIhasthreeproperties:coverURL,author,
andtitle.Weupdatethe<ListView>’srenderfunctiontoreturnacomponentbasedon
thoseprops.
<i>Example4-8.For_renderRow,wejustpassalongtherelevantdatatothe<BookItem></i>
_renderRow:function(rowData){
return<BookItemcoverURL={rowData.book_image}
title={rowData.title}
author={rowData.author}/>;
},
We’llalsotossinaheaderandfootercomponent,todemonstratehowthesework
(Example4-9).Notethatfora<ListView>,theheaderandfooterarenotsticky;they
scrollwiththerestofthelist.Ifyouwantastickyheaderorfooter,it’sprobablyeasiestto
renderthemseparatelyfromthe<ListView>component.
<i>Example4-9.AddingmethodstorenderheaderandfooterelementsinBookListV2.js</i>
_renderHeader:function(){
return(<Viewstyle={styles.sectionDivider}>
<Textstyle={styles.headingText}>
BestsellersinHardcoverFiction
</Text>
</View>);
},
_renderFooter:function(){
return(
DatafromtheNewYorkTimesBestSellerlist.
</Text>
</View>
);
},
<i>Alltogether,theBestSellersapplicationconsistsoftwofiles:BookListV2.jsand</i>
<i>BookItem.js.BookListV2.jsisshownin</i>Example4-10<i>.(BookList.jsisasimplerfilethat</i>
omitsfetchingdatafromanAPI,andisincludedintheGitHubrepositoryforyour
reference.)
<i>Example4-10.Bestsellers/BookListV2.js</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
Image,
ListView,
}=React;
<b>var</b>BookItem=require('./BookItem');
<b>var</b>API_KEY='73b19491b83909c7e07016f4bb4644f9:2:60667290';
<b>var</b>QUERY_TYPE='hardcover-fiction';
<b>var</b>API_STEM=' />
<b>var</b>ENDPOINT=`${API_STEM}/${QUERY_TYPE}?response-format=json&api-key=${API_KEY}`;
<b>var</b>BookList=React.createClass({
getInitialState:function(){
vards=newListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2});
return{
dataSource:ds.cloneWithRows([])
};
},
componentDidMount:function(){
this._refreshData();
},
_renderRow:function(rowData){
return<BookItemcoverURL={rowData.book_image}
title={rowData.title}
author={rowData.author}/>;
},
_renderHeader:function(){
return(<Viewstyle={styles.sectionDivider}>
<Textstyle={styles.headingText}>
BestsellersinHardcoverFiction
</Text>
</View>);
},
_renderFooter:function(){
return(
<Viewstyle={styles.sectionDivider}>
<Text>DatafromtheNewYorkTimesBestSellerlist.</Text>
</View>
);
},
_refreshData:function(){
fetch(ENDPOINT)
.then((response)=>response.json())
.then((rjson)=>{
this.setState({
dataSource:this.state.dataSource.cloneWithRows(rjson.results.books)
});
render:function(){
return(
<ListView
style=
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
renderFooter={this._renderFooter}
/>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#FFFFFF',
paddingTop:24
},
list:{
flex:1,
flexDirection:'row'
},
listContent:{
flex:1,
flexDirection:'column'
},
row:{
flex:1,
fontSize:24,
padding:42,
borderWidth:1,
borderColor:'#DDDDDD'
},
sectionDivider:{
padding:8,
backgroundColor:'#EEEEEE',
alignItems:'center'
},
headingText:{
flex:1,
fontSize:24,
}
});
module.exports=BookList;
The<BookItem>isasimplecomponentthathandlesrenderingeachchildviewinthelist
(Example4-11).
<i>Example4-11.Bestsellers/BookItem.js</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
Image,
ListView,
}=React;
<b>var</b>styles=StyleSheet.create({
bookItem:{
flex:1,
padding:5
},
resizeMode:'contain'
},
info:{
flex:3,
alignItems:'flex-end',
flexDirection:'column',
alignSelf:'center',
padding:20
},
author:{
fontSize:18
},
title:{
fontSize:18,
fontWeight:'bold'
}
});
<b>var</b>BookItem=React.createClass({
coverURL:React.PropTypes.string.isRequired,
author:React.PropTypes.string.isRequired,
title:React.PropTypes.string.isRequired
},
render:function(){
return(
<Viewstyle={styles.bookItem}>
<Imagestyle={styles.cover}source=/>
<Viewstyle={styles.info}>
<Textstyle={styles.author}>{this.props.author}</Text>
<Textstyle={styles.title}>{this.props.title}</Text>
</View>
</View>
);
}
});
module.exports=BookItem;
The<ListView>isagoodexampleofcombiningmultipleviewstogetherintoamore
The<Navigator>isasubtlebutimportantcomponent,andisusedinmanycommon
applications.Forinstance,theiOSSettingsappcouldbeimplementedasacombinationof
<Navigator>withmany<ListView>components(Figure4-8).TheDropboxappalso
<i>Figure4-8.TheiOSSettingsappisagoodexampleofNavigatorbehavior</i>
A<Navigator>allowsyourapplicationtotransitionbetweendifferentscreens(often
referredtoas“scenes”),whilemaintaininga“stack”ofroutes,sothatyoucanpush,pop,
orreplacestates.YoucanthinkofthisasanalogoustothehistoryAPIontheWeb.A
“route”isthetitleofascreen,coupledwithanindex.
Forinstance,intheSettingsapp,initiallythestackisempty.Whenyouselectoneofthe
submenus,theinitialsceneispushedontothestack.Tapping“back,”inthetop-leftcorner
ofthescreen,willpopitbackoff.
Ifyou’reinterestedinhowthisplaysout,theUIExplorerapphasagooddemoofthe
variouswaysofusingtheNavigatorAPI.
NotethatthereareactuallytwoNavigatoroptions:thecross-platform<Navigator>
componentandthe<NavigatorIOS>component.Inthisbook,we’llbeoptingtousethe
<b>SHOULDIUSENAVIGATORORNAVIGATORIOS?</b>
Funnyyoushouldask!TheReactNativedocshaveapageaddressingthatexactquestion.Theshortansweris:you
shouldusethe<Navigator>.<NavigatorIOS>isnotsupportedbythecoreteam,andhencehassomebugs.
Thereareplentyofotherorganizationalcomponents,too.Forexample,afewusefulones
include<TabBarIOS>and<SegmentedControlIOS>(illustratedinFigure4-9)and
<DrawerLayoutAndroid>and<ToolbarAndroid>(illustratedinFigure4-10).
You’llnoticethattheseareallnamedwithplatform-specificsuffixes.That’sbecausethey
wrapnativeAPIsforplatform-specificUIelements.
<i>Figure4-9.AniOSsegmentedcontrol(top),andaniOStabbar(bottom)</i>
<i>Figure4-10.AnAndroidtoolbar(left),andanAndroiddrawer(right)</i>
betweenmultiplemodesorfunctions.<SegmentedControlIOS>and<ToolbarAndroid>
arebettersuitedformorefine-grainedcontrols.
You’llwanttorefertotheplatform-specificdesignguidelinesforhowbesttousethese
components:
AndroidDesignGuide
iOSHumanInterfaceGuidelines
Notallcomponentsareavailableonallplatforms,andnotallinteractionpatternsare
appropriateforalldevices.Thatdoesn’tmeanthatyoucan’tuseplatform-specificcodein
yourapplication,though!Inthissection,we’llcoverplatform-specificcomponents,as
wellasstrategiesforhowtoincorporatetheminyourcross-platformapplications.
<b>TIP</b>
Somecomponentsareonlyavailableonaspecificplatform.Thisincludesthingslike
<TabBarIOS>or<SwitchAndroid>.They’reusuallyplatform-specificbecausetheywrap
somekindofunderlyingplatform-specificAPI.Forsomecomponents,havingaplatform-agnosticversiondoesn’tmakesense.Forinstance,the<ToolbarAndroid>component
exposesanAndroid-specificAPIforaviewtypethatdoesn’texistoniOSanyway.
Platform-specificcomponentsarenamedwithanappropriatesuffix:eitherIOSor
Android.Ifyoutrytoincludeoneonthewrongplatform,yourapplicationwillcrash.
Componentscanalsohaveplatform-specificprops.Thesearetaggedinthedocumentation
withasmallbadgeindicatingtheirusage.Forinstance,<TextInput>hassomepropsthat
areplatform-agnostic,andothersthatarespecifictoiOSorAndroid(Figure4-11).
<i>We’llstartbyimplementingswitch.ios.js(</i>Example4-12).It’saverysimplewrapper
<i>Example4-12.Switch.ios.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>SwitchIOS}=React;
<b>var</b>Switch=React.createClass({
getInitialState(){
return{value:false};
},
_onValueChange(value){
this.setState({value:value});
if(this.props.onValueChange){
this.props.onValueChange(value);
}
},
render(){
return(
<SwitchIOS
onValueChange={this._onValueChange}
value={this.state.value}/>
);
}
});
module.exports=Switch;
<i>Next,let’simplementswitch.android.js(</i>Example4-13).
<i>Example4-13.Switch.android.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>SwitchAndroid}=React;
<b>var</b>Switch=React.createClass({
getInitialState(){
return{value:false};
},
_onValueChange(value){
this.setState({value:value});
if(this.props.onValueChange){
this.props.onValueChange(value);
}
},
<SwitchAndroid
onValueChange={this._onValueChange}
value={this.state.value}/>
);
}
});
module.exports=Switch;
<i>Notethatitlooksalmostidenticaltoswitch.ios.js,anditimplementsthesameAPI.The</i>
onlydifferenceisthatituses<SwitchAndroid>internallyinsteadof<SwitchIOS>.
Wecannowimportour<Switch>componentfromanotherfilewiththesyntax:
<b>var</b>Switch=require('./switch');
...
<b>var</b>switchComp=<SwitchonValueChange={(val)=>{console.log(val);}}/>;
Let’sactuallyusethe<Switch><i>component.Createanewfile,CrossPlatform.js,and</i>
includethecodeshowninExample4-14.We’llhavethebackgroundcolorchangebased
onthecurrentvalueofa<Switch>.
<i>Example4-14.CrossPlatform.jsmakesuseofthe<Switch>component</i>
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
Text,
View,
}=React;
<b>var</b>Switch=require('./switch');
<b>var</b>CrossPlatform=React.createClass({
getInitialState(){
return{val:false};
},
_onValueChange(val){
this.setState({val:val});
},
render:function(){
varcolorClass=this.state.val?styles.blueContainer:styles.redContainer;
return(
<Viewstyle={[styles.container,colorClass]}>
<Textstyle={styles.welcome}>
Makemeblue!
</Text>
<SwitchonValueChange={this._onValueChange}/>
</View>
);
}
});
<b>var</b>styles=StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
},
blueContainer:{
backgroundColor:'#5555FF'
},
redContainer:{
backgroundColor:'#FF5555'
},
welcome:{
fontSize:20,
}
module.exports=CrossPlatform;
<i>Notethatthere’snoswitch.jsfile,butwecancallrequire(./switch)</i>.TheReactNative
packagerwillautomaticallyselectthecorrectimplementationbasedonourplatform,and
<i>useeitherswitch.ios.jsorswitch.android.jsasappropriate.</i>
<i>Finally,replacethecontentsofindex.android.jsandindex.ios.jssothatwecanrenderthe</i>
<CrossPlatform>component.
<i>Example4-15.Theindex.ios.jsandindex.android.jsfilesshouldbeidentical,andsimply</i>
<i>importthecrossplatform.jsfile</i>
<b>var</b>React=require('react-native');
<b>var{</b>AppRegistry}=React;
<b>var</b>CrossPlatform=require('./crossplatform');
AppRegistry.registerComponent('PlatformSpecific',()=>CrossPlatform);
Whenisitappropriatetouseaplatform-specificcomponent?Inmostcases,you’llwantto
dosowhenthere’saplatform-specificinteractionpatternthatyouwantyourapplicationto
adhereto.Ifyouwantyourapplicationtofeeltruly“native,”it’sworthpayingattentionto
platform-specificUInorms.
AppleandGooglebothprovidehumaninterfaceguidelinesfortheirplatforms,whichare
iOSHumanInterfaceGuidelines
AndroidDesignReference
Bycreatingplatform-specificversionsofonlycertaincomponents,youcanstrikea
Inthischapter,wedugintothespecificsofavarietyofthemostimportantcomponentsin
ReactNative.Wediscussedhowtoutilizebasiclow-levelcomponents,like<Text>and
<Image>,aswellashigher-ordercomponentslike<ListView>,<Navigator>,and
<TabBarIOS>.Wealsotookalookathowtousevarioustouch-focusedAPIsand
components,incaseyouwanttobuildyourowncustomtouchhandlers.Finally,wesaw
howtouseplatform-specificcomponentsinourapplications.
Atthispoint,youshouldbeequippedtobuildbasic,functionalapplicationsusingReact
Native!Nowthatyou’veacquaintedyourselfwiththecomponentsdiscussedinthis
chapter,buildinguponthemandcombiningthemtocreateyourownapplicationsshould
feelremarkablysimilartoworkingwithReactontheWeb.
It’sgreattobeabletobuildfunctionalapplications,butifyoucan’tstylethemeffectively,
youwon’tgetveryfar!InChapter3,webuiltasimpleweatherapplicationwithsome
basicstyles.WhilethisgaveusanoverviewofhowtostyleReactNativecomponents,we
WhenworkingwithReactfortheWeb,wetypicallyuseseparatestylesheetfiles,which
maybewritteninCSS,SASS,orLESS.ReactNativetakesaradicallydifferentapproach,
bringingstylesentirelyintotheworldofJavaScriptandforcingyoutolinkstyleobjects
explicitlytocomponents.Needlesstosay,thisapproachtendstoprovokestrongreactions,
asitrepresentsasignificantdeparturefromCSS-basedstylingnorms.
TounderstandthedesignofReactNative’sstyles,firstweneedtoconsidersomeofthe
headachesassociatedwithtraditionalCSSstylesheets.1CSShasanumberofproblems.
AllCSSrulesandclassnamesareglobalinscope,meaningthatstylingonecomponent
caneasilybreakanotherifyou’renotcareful.Forinstance,ifyouincludethepopular
TwitterBootstraplibrary,youwillintroduceover600newglobalvariables.BecauseCSS
isnotexplicitlyconnectedtotheHTMLelementsitstyles,deadcodeeliminationis
difficult,anditcanbenontrivialtodeterminewhichstyleswillapplytoagivenelement.
LanguageslikeSASSandLESSattempttoworkaroundsomeofCSS’suglierparts,but
manyofthesamefundamentalproblemsremain.WithReact,wehavetheopportunityto
keepthedesirablepartsofCSS,butalsothefreedomforsignificantdivergence.React
NativeimplementsasubsetoftheavailableCSSstyles,focusingonkeepingthestyling
APInarrowyetstillhighlyexpressive.Positioningisdramaticallydifferent,aswe’llsee
laterinthischapter.Additionally,ReactNativedoesnotsupportpseudoclasses,
animations,orselectors.Afulllistofsupportedpropertiescanbefoundinthedocs.
<i>Insteadofstylesheets,inReactNativeweworkwithJavaScript-basedstyleobjects.One</i>
ofReact’sgreateststrengthsisthatitforcesyoutokeepyourJavaScriptcode—your
components—modular.BybringingstylesintotherealmofJavaScript,ReactNative
pushesustowritemodularstyles,too.
Inlinestylesarethesimplestway,syntactically,tostyleacomponentinReactNative,
<i>thoughtheyarenotusuallythebestway.Asyoucanseein</i>Example5-1,thesyntaxfor
inlinestylesinReactNativeisthesameasforReactforthebrowser.
<i>Example5-1.Usinginlinestyles</i>
<Text>
Thequick<Textstyle={{fontStyle:"italic"}}>brown</Text>fox
jumpedoverthelazy<Textstyle={{fontWeight:"bold"}}>dog</Text>.
</Text>
Inlinestyleshavesomeadvantages.They’requickanddirty,allowingyoutorapidly
experiment.
Ifyoutakealookattheinlinestylesyntax,youwillseethatit’ssimplypassinganobject
tothestyleattribute.There’snoneedtocreatethestyleobjectintherendercall,though;
instead,youcanseparateitout,asshowninExample5-2.
<i>Example5-2.StyleattributewillacceptaJavaScriptobject</i>
<b>var</b>italic={
fontStyle:'italic'
};
<b>var</b>bold={
fontWeight:'bold'
};
...
render(){
return(
<Text>
Thequick<Textstyle={italic}>brown</Text>fox
jumpedoverthelazy<Textstyle={bold}>dog</Text>.
</Text>
);
}
<i>PanDemo.js,from</i>Example4-7,givesusagoodexampleofausecaseinwhichthe
YouwillnoticethatalmostalloftheReactNativeexamplecodemakesuseof
StyleSheet.create.UsingStyleSheet.createisstrictlyoptional,butingeneralyou’ll
wanttouseit.Here’swhatthedocshavetosay:
StyleSheet.createconstructisoptionalbutprovidessomekeyadvantages.Itensuresthat
thevaluesareimmutableandopaquebytransformingthemintoplainnumbersthat
referenceaninternaltable.Byputtingitattheendofthefile,youalsoensurethatthey
areonlycreatedoncefortheapplicationandnotoneveryrender.
Inotherwords,StyleSheet.createisreallyjustabitofsyntacticsugardesignedto
protectyou.Useit!Thevastmajorityofthetime,theimmutabilityprovidedby
StyleSheet.createishelpful.Italsogivesyoutheabilitytodopropvalidationvia
<i>Example5-3.Styleattributealsoacceptsanarrayofobjects</i>
<b>var</b>AccentButton=React.createClass({
render:function(){
return(
<Textstyle={[styles.button,styles.accentText]}>
{this.props.children}
</Text>
);
}
});
Asyoucansee,thestyleattributecantakeanarrayofstyleobjects.Youcanalsoadd
inlinestyleshere,ifyouwant(Example5-4).
<i>Example5-4.Youcanmixstyleobjectsandinlinestyles</i>
<b>var</b>AccentButton=React.createClass({
render:function(){
return(
<Textstyle={[styles.button,styles.accentText,{color:'#FFFFFF'}]}>
{this.props.children}
</Text>
);
}
});
Inthecaseofaconflict,suchaswhentwoobjectsbothspecifythesameproperty,React
Nativewillresolvetheconflictforyou.Therightmostelementsinthestylearraytake
precedence,andfalsyvalues(false,null,undefined)areignored.
Youcanleveragethispatterntoapplyconditionalstyles.Forexample,ifwehada
<Button>componentandwantedtoapplyextrastylerulesifit’sbeingtouched,wecould
usethecodeshowninExample5-5.
<i>Example5-5.Usingconditionalstyles</i>
Thisshortcutcanhelpyoukeepyourrenderinglogicconcise.
<i>Withinstyles.js,youcreateastylesheet,andexportit(</i>Example5-6).
<i>Example5-6.ExportingstylesfromaJavaScriptfile</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
StyleSheet,
}=React;
<b>var</b>styles=Stylesheet.create({
text:{
color:'#FF00FF',
fontSize:16
},
fontWeight:'bold'
}
});
module.exports=styles;
<i>Withinindex.js,wecanimportourstyleslikeso:</i>
<b>var</b>styles=require('./styles.js');
Thenwecanusetheminourcomponent(Example5-7).
<i>Example5-7.ImportingstylesfromanexternalJavaScriptfile</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var</b>styles=require('./styles.js');
<b>var{</b>
View,
Text,
StyleSheet
}=React;
<b>var</b>ComponentName=React.createClass({
render:function(){
return(
<Textstyle={[styles.text,styles.bold]}>
Hello,world
</Text>
);
Youcanalsopassstylesasproperties.ThepropTypeView.propTypes.styleensuresthat
onlyvalidstylesarepassedasprops.
Youcanusethispatterntocreateextensiblecomponents,whichcanbemoreeffectively
controlledandstyledbytheirparents.Forexample,acomponentmighttakeinanoptional
styleprop(Example5-8).
<i>Example5-8.Componentscanreceivestyleobjectsviaprops</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
View,
Text
}=React;
<b>var</b>CustomizableText=React.createClass({
style:Text.propTypes.Style
},
getDefaultProps:function(){
return{
style:{}
};
},
render:function(){
return(
<Textstyle={[myStyles.text,this.props.style]}>
Hello,world
</Text>
);
}
});
Wetypicallyprefertoreusestyledcomponents,ratherthanreusingstyles,butthereare
clearlysomeinstancesinwhichyouwillwanttosharestylesbetweencomponents.Inthis
case,acommonpatternistoorganizeyourprojectroughlylikeso:
-js
|-components
|-Button
|-index.js
|-styles.js
|-styles
|-styles.js
|-colors.js
|-fonts.js
Byhavingseparatedirectoriesforcomponentsandforstyles,youcankeeptheintended
useofeachfileclearbasedoncontext.Acomponent’sfoldershouldcontainitsReact
class,aswellasanycomponent-specificfiles.Sharedstylesshouldbekeptoutof
componentfolders.Sharedstylesmayincludethingssuchasyourpalette,fonts,
standardizedmarginsandpadding,andsoon.
<i>styles/styles.jsrequirestheothersharedstylesfiles,andexposesthem;thenyour</i>
<i>componentscanrequirestyles.jsandusesharedfilesasneeded.Or,youmaypreferto</i>
<i>havecomponentsrequirespecificstylesheetsfromthestyles/directoryinstead.</i>
FlexboxisaCSS3layoutmode.Unlikeexistinglayoutmodessuchasblockandinline,
flexboxgivesusadirection-agnosticwayofconstructinglayouts.(That’sright:finally,
verticallycenteringiseasy!)ReactNativeleansheavilyonflexbox.Ifyouwanttoread
moreaboutthegeneralspecification,theMDNdocumentationisagoodplacetostart.
WithReactNative,thefollowingflexboxpropsareavailable:
flex
flexDirection
flexWrap
alignSelf
alignItems
Additionally,theserelatedvaluesimpactlayout:
height
width
margin
border
padding
IfyouhaveworkedwithflexboxontheWebbefore,therewon’tbemanysurpriseshere.
BecauseflexboxissoimportanttoconstructinglayoutsinReactNative,though,we’ll
spendsometimenowexploringhowitworks.
Thebasicideabehindflexboxisthatyoushouldbeabletocreatepredictablystructured
layoutsevengivendynamicallysizedelements.Becausewe’redesigningformobile,and
needtoaccommodatemultiplescreensizesandorientations,thisisausefulfeature.
We’llstartwithaparent<View>,andsomechildren:
<Viewstyle={styles.parent}>
<Textstyle={styles.child}>ChildOne</Text>
<Textstyle={styles.child}>ChildTwo</Text>
<Textstyle={styles.child}>ChildThree</Text>
</View>
Tostart,we’veappliedsomebasicstylestotheviews,buthaven’ttouchedthepositioning
yet:
backgroundColor:'#F5FCFF',
borderColor:'#0099AA',
borderWidth:5,
marginTop:30
},
child:{
borderColor:'#AA0099',
borderWidth:2,
textAlign:'center',
fontSize:24,
}
});
<i>Figure5-1.Thelayoutbeforeweaddflexproperties</i>
Next,wewillsetflexonboththeparentandthechild.Bysettingtheflexproperty,we
areexplicitlyoptingintoflexboxbehavior.flextakesanumber.Thisnumberdetermines
therelativeweighteachchildgets;bysettingitto1foreachchild,weweightthem
equally.
WealsosetflexDirection:'column'sothatthechildrenarelaidoutvertically.Ifwe
switchthistoflexDirection:'row',thechildrenwillbelaidouthorizontallyinstead.
ThesechangestothestylescanbeseeninExample5-9.Figure5-2illustratesthe
<i>Figure5-2.SettingbasicflexpropertiesandflexDirection;settingflexDirectiontocolumn(left)andsettingflexDirection</i>
<i>torow(right)</i>
<i>Example5-9.ChangingtheflexandflexDirectionproperties</i>
<b>var</b>styles=StyleSheet.create({
parent:{
flex:1,
flexDirection:'column',
backgroundColor:'#F5FCFF',
borderColor:'#0099AA',
borderWidth:5,
marginTop:30
},
child:{
flex:1,
borderColor:'#AA0099',
borderWidth:2,
textAlign:'center',
fontSize:24,
}
});
Then,thealignItemsvaluedetermineswheretheyarepositionedalongthecross-axis.
Thecross-axisistheaxisorthogonaltotheflexDirection.Inthiscase,thecrossaxisis
vertical.flex-startplacesthechildrenatthetop,centercentersthem,andflex-end
placesthematthebottom.
Let’sseewhathappenswhenwesetalignItems(theresultisshowninFigure5-3):
<b>var</b>styles=StyleSheet.create({
parent:{
flex:1,
flexDirection:'row',
alignItems:'flex-start',
backgroundColor:'#F5FCFF',
borderColor:'#0099AA',
borderWidth:5,
marginTop:30
},
child:{
flex:1,
borderColor:'#AA0099',
borderWidth:2,
textAlign:'center',
fontSize:24,
}
});
Inadditiontoflexbox,ReactNativesupportsabsolutepositioning.Itworksmuchasit
doesontheWeb.Youcanenableitbysettingthepositionproperty:
<b>position:</b>absolute
Then,youcancontrolthecomponent’spositioningwiththefamiliarpropertiesofleft,
right,top,andbottom.
Anabsolutelypositionedchildwillapplythesecoordinatesrelativetoitsparent’sposition,
soyoucanlayoutaparentelementusingflexboxandthenuseabsolutepositionfora
childwithinit.
Therearesomelimitationstothis.Wedon’thavez-index,forinstance,solayeringviews
ontopofeachotherisabitcomplicated.Thelastviewinastacktypicallytakes
precedence.
Absolutepositioningcanbeveryuseful.Forinstance,ifyouwanttocreateacontainer
viewthatsitsbelowthephone’sstatusbar,absolutepositioningmakesthiseasy:
container:{
position:'absolute',
top:30,
left:0,
right:0,
bottom:0
<i>Figure5-4.We’lluseflexboxtoconstructthislayout</i>
Howshouldwegoaboutconstructingthiskindoflayout?
Tostartwith,wecreateaparentstyletoactasthecontainer.Wewilluseabsolute
positioningontheparent,becauseit’smostappropriate:wewantittofillallavailable
space,exceptwitha30-pixeloffsetatthetop,duetothestatusbaratthetopofthescreen.
We’llalsosetitsflexDirectiontocolumn:
<b>parent:{</b>
flexDirection:'column',
position:'absolute',
top:30,
left:0,
right:0,
bottom:0
}
<i>Figure5-5.Theorderinwhichwe’llstylethesections</i>
Westartbycuttingthelayoutintoatopandbottomblock:
<Viewstyle={styles.parent}>
<Viewstyle={styles.topBlock}>
</View>
<Viewstyle={styles.bottomBlock}>
</View>
</View>
Thenweaddinthenextlayer.Thisincludesbotha“leftcolumn”and“bottomright”
sector,aswellastheactual<View>componentsforcellsthree,four,andfive:
<Viewstyle={styles.parent}>
<Viewstyle={styles.topBlock}>
<Viewstyle={styles.leftCol}>
</View>
<Viewstyle={[styles.cellThree,styles.base]}/>
</View>
<Viewstyle={styles.bottomBlock}>
<Viewstyle={[styles.cellFour,styles.base]}/>
<Viewstyle={[styles.cellFive,styles.base]}/>
<Viewstyle={styles.bottomRight}>
</View>
</View>
Thefinalmarkupcontainsallsevencells.Example5-10showsthefullcomponent.
<i>Example5-10.Styles/Mondrian/index.js</i>
'usestrict';
<b>var</b>React=require('react-native');
<b>var{</b>
AppRegistry,
StyleSheet,
Text,
View,
}=React;
<b>var</b>styles=require('./style');
<b>var</b>Mondrian=React.createClass({
render:function(){
return(
<Viewstyle={styles.parent}>
<Viewstyle={styles.topBlock}>
<Viewstyle={styles.leftCol}>
<Viewstyle={[styles.cellOne,styles.base]}/>
<Viewstyle={[styles.base,styles.cellTwo]}/>
</View>
<Viewstyle={[styles.cellThree,styles.base]}/>
</View>
<Viewstyle={styles.bottomBlock}>
<Viewstyle={[styles.cellFour,styles.base]}/>
<Viewstyle={[styles.cellFive,styles.base]}/>
<Viewstyle={styles.bottomRight}>
<Viewstyle={[styles.cellSix,styles.base]}/>
<Viewstyle={[styles.cellSeven,styles.base]}/>
</View>
</View>
</View>
);
}
});
Nowlet’saddthestylesthatmakeitwork(Example5-11).
<i>Example5-11.Styles/Mondrian/style.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>StyleSheet}=React;
<b>var</b>styles=StyleSheet.create({
parent:{
flexDirection:'column',
position:'absolute',
top:30,
left:0,
right:0,
bottom:0
},
base:{
borderColor:'#000000',
borderWidth:5
},
topBlock:{
flexDirection:'row',
flex:5
},
leftCol:{
flex:2
},
bottomBlock:{
flexDirection:'row'
},
bottomRight:{
flexDirection:'column',
flex:2
},
cellOne:{
flex:1,
borderBottomWidth:15
},
cellTwo:{
flex:3
},
cellThree:{
backgroundColor:'#FF0000',
flex:5
},
cellFour:{
flex:3,
backgroundColor:'#0000FF'
},
cellFive:{
flex:6
},
cellSix:{
flex:1
},
cellSeven:{
flex:1,
backgroundColor:'#FFFF00'
}
});
Inthischapter,welookedathowstylesworkinReactNative.Whileinmanywaysstyling
shouldbeabletousestyleseffectivelytocreatethemobileUIsyouneedwithReact
Native.Andbestofall,experimentingwithstylesiseasy:beingabletohit“reload”inthe
simulatorgrantsusatightfeedbackloop.(It’sworthnotingthatwithtraditionalmobile
development,editingastylewouldtypicallyrequirerebuildingyourapplication.Yikes.)
Ifyouwantmorepracticewithstyles,trygoingbacktotheBestSellersorWeather
applications,andadjustingtheirstylingandlayouts.Aswebuildmoresample
applicationsinfuturechapters,you’llhaveplentyofmaterialtopracticewith,too!
Whenbuildingmobileapplications,youwillnaturallywanttotakeadvantageofthehost
platform’sspecificAPIs.ReactNativemakesiteasytoaccessthingslikethephone’s
cameraroll,location,andpersistentstorage.TheseplatformAPIsaremadeavailableto
ReactNativethroughincludedmodules,whichprovideuswitheasy-to-useasynchronous
JavaScriptinterfacestothesefunctions.
<i>ReactNativedoesnotwrapallofitshostplatform’sfunctionalitybydefault;some</i>
platformAPIswillrequireyoutoeitherwriteyourownmodules,orusemoduleswritten
byothersintheReactNativecommunity.WewillcoverthatprocessinChapter7.The
docsarethebestplacetocheckifanAPIissupported.
ThischaptercoverssomeoftheavailableplatformAPIs.Forourexample,we’llmake
somemodificationstotheWeatherapplicationfromearlier.We’lladdgeolocationtothe
app,sothatitdetectstheuser’slocationautomatically.Wewillalsoadd“memory”tothe
<b>IOSANDANDROIDCOMPATIBILITY</b>
Formobileapplications,knowingtheuser’slocationisoftencritical.Itallowsyouto
servetheusercontextuallyrelevantinformation.Manymobileapplicationsmake
extensiveuseofthisdata.
Happily,ReactNativehasbuilt-insupportforgeolocation.Thisisprovidedasaplatform-agnostic“polyfill.”ItreturnsdatabasedontheMDNGeolocationAPIwebspecification.
<b>GEOLOCATIONISCURRENTLYIOS-ONLY</b>
UsingtheGeolocationAPItogetauser’slocationisabreeze.AsshowninExample6-1,
weneedtomakeacalltonavigator.geolocation.
<i>Example6-1.Gettingtheuser’slocationwithanavigator.geolocationcall</i>
navigator.geolocation.getCurrentPosition(
(position)=>{
console.log(position);
},
(error)=>{alert(error.message)},
{enableHighAccuracy:true,timeout:20000,maximumAge:1000}
);
InconformancetotheGeolocationspecification,wedon’timportthisAPIasaseparate
module;it’ssimplyavailableforouruse.
ThegetCurrentPositioncalltakesthreearguments:asuccesscallback,anerrorcallback,
andasetofgeoOptions.Onlythesuccesscallbackisrequired.
Thepositionobjectpassedtothesuccesscallbackwillcontaincoordinates,aswellasa
timestamp.Example6-2showstheformatandpossiblevalues.
<i>Example6-2.ShapeoftheresponsereturnedfromagetCurrentPositioncall</i>
{
coords:{
speed:-1,
longitude:-122.03031802,
latitude:37.33259551999998,
accuracy:500,
heading:-1,
altitude:0,
altitudeAccuracy:-1
},
timestamp:459780747046.605
}
geoOptionsshouldbeanobject,whichoptionallyincludesthekeystimoeut,
Locationdataissensitiveinformation,andthereforewillnotbeaccessibletoyour
applicationbydefault.Yourapplicationshouldbeabletohandlepermissionsbeing
acceptedorrejected.
Mostmobileplatformshavesomenotionoflocationpermissions.Ausermayopttoblock
LocationServicesentirelyoniOS,forinstance,ortheymaymanagepermissionsonaper-appbasis.Iftheuserdeniesyourapplicationaccess,thecancellationcallbackyoupassto
getCurrentPositionwillbeinvoked.
It’simportanttonotethatlocationpermissionscanberevokedatessentiallyanypointin
time.Yourapplicationshouldalwaysbepreparedforageolocationcalltofail.
Thefirsttimeyourapplicationattemptstoaccesstheuser’slocation,theuserwillbe
presentedwithapermissionsdialogliketheoneshowninFigure6-1.
<i>Figure6-1.Locationrequest</i>
Whilethisdialogisactive,neithercallbackwillfire;oncetheyselectanoption,the
appropriatecallbackwillbeinvoked.Thissettingwillpersistforyourapplication,sothe
nexttime,suchacheckwon’tbenecessary.
Chancesareyou’llbedoingmostofyourtestinganddevelopmentfromwithina
simulator,orattheveryleast,atyourdesk.Howcanyoutesthowyourappwillbehaveat
differentlocations?
<i>Figure6-2.Pickingalocation</i>
Youcanalsosetawatchontheuser’slocation,andreceiveupdateswheneveritchanges.
Thiscanbeusedtotrackauser’slocationovertime,orjusttoensurethatyourapp
receivesthemostup-to-dateposition:
<b>this.</b>watchID=navigator.geolocation.watchPosition((position)=>{
this.setState({position:position});
});
Notethatyou’llwanttoclearthewatchwhenyourcomponentunmountsaswell:
componentWillUnmount:function(){
BecausegeolocationisbasedontheMDNspecification,itleavesoutmoreadvanced
location-basedfeatures.Forexample,iOSprovidesaGeofencingAPI,whichallowsyour
applicationtoreceivenotificationswhentheuserentersorleavesadesignated
geographicalregion(thegeofence).ReactNativecurrentlydoesnotexposethisAPI.
TheSmarterWeatherapplicationisanupdatedversionoftheWeatherapplication,which
nowmakesuseoftheGeolocationAPI.YoucanseethesechangesinFigure6-3.
<i>Figure6-3.Displayingforecastbasedontheuser’scurrentlocation</i>
<i>Example6-3.SmarterWeather/LocationButton/index.js:whenpressed,thebuttongetsthe</i>
<i>user’slocation</i>
<b>var</b>React=require('react-native');
<b>var</b>styles=require('./style.js');
<b>var</b>Button=require('./../Button');
<b>var</b>LocationButton=React.createClass({
propTypes:{
onGetCoords:React.PropTypes.func.isRequired
},
_onPress:function(){
navigator.geolocation.getCurrentPosition(
(initialPosition)=>{
this.props.onGetCoords(initialPosition.coords.latitude,
initialPosition.coords.longitude);
},
(error)=>{alert(error.message)},
{enableHighAccuracy:true,timeout:20000,maximumAge:1000}
);
},
render:function(){
return(
<Buttonlabel="UseCurrentLocation"
style={styles.locationButton}
onPress={this._onPress}/>
);
}
});
module.exports=LocationButton;
TheButtoncomponentusedbyLocationButtonisincludedattheendofthischapter;it
simplywrapsa<Text>componentinanappropriate<TouchableHighlight>withsome
basicstyling.
<i>We’vealsohadtoupdatethemainweather_project.jsfiletoaccommodatetwokindsof</i>
queries(Example6-4).Happily,theOpenWeatherMapAPIallowsustoquerybylatitude
andlongitudeaswellaszipcode.
<i>Example6-4.Adding_getForecastForCoordsand_getForecastForZipfunctions</i>
<b>var</b>WEATHER_API_KEY='bbeb34ebf60ad50f7893e7440a1e2b0b';
<b>var</b>API_STEM=' />...
_getForecastForZip:function(zip){
this._getForecast(
`${API_STEM}q=${zip}&units=imperial&APPID=${WEATHER_API_KEY}`);
},
_getForecastForCoords:function(lat,lon){
this._getForecast(
`${API_STEM}lat=${lat}&lon=${lon}&units=imperial&APPID=${WEATHER_API_KEY}`);
},
_getForecast:function(url,cb){
fetch(url)
.then((response)=>response.json())
.then((responseJSON)=>{
console.log(responseJSON);
this.setState({
forecast:{
main:responseJSON.weather[0].main,
temp:responseJSON.main.temp
}
});
})
.catch((error)=>{
console.warn(error);
});
}
ThenweincludetheLocationButtoninthemainviewwith_getForecastForCoordsas
thecallback:
<LocationButtononGetCoords={this._getForecastForCoords}/>
I’veomittedtherelevantstyleupdatesandsoon,asthefullyupdatedapplicationcode
willbeincludedattheendofthischapter.
Havingaccesstoaphone’slocalimages,aswellasthecamera,isanothercriticalpartof
manymobileapplications.Inthissection,we’llexploreyouroptionsforinteractingwith
users’imagedataaswellasthecamera.
<b>CAMERAROLLISCURRENTLYIOS-ONLY</b>
TheCameraRollmodulewillbesupportedonAndroidsoon,butfornowit’siOS-only.
InteractingwiththeCameraRoll,initsmostbasicform,isnottoocomplicated.Firstwe
requirethemodule,asperusual:
<b>var</b>React=require('react-native');
<b>var{</b>CameraRoll}=React;
Then,wemakeuseofthemoduletofetchinformationabouttheuser’sphotos,asshown
inExample6-5.
<i>Example6-5.BasicusageofCameraRoll.getPhotos</i>
CameraRoll.getPhotos(
{first:1},
(data)=>{
console.log(data);
},
(error)=>{
console.warn(error);
});
WemakeacalltogetPhotoswiththeappropriatequery,anditreturnssomedatarelated
totheCameraRollimages.
InSmarterWeather,let’sreplacethetop-level<Image>componentwithanewcomponent,
PhotoBackdrop(Example6-6).Fornow,PhotoBackdropsimplypullsthemostrecent
photofromtheuser’sCameraRoll.
<i>Example6-6.SmarterWeather/PhotoBackdrop/camera_roll_example.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>Image,CameraRoll}=React;
<b>var</b>styles=require('./style.js');
<b>var</b>PhotoBackdrop=React.createClass({
getInitialState(){
return{
photoSource:null
}
},
componentDidMount(){
CameraRoll.getPhotos(
{first:5},
(data)=>{
this.setState({
photoSource:{uri:data.edges[3].node.image.uri}
})},
(error)=>{
console.warn(error);
},
render(){
return(
<Image
style={styles.backdrop}
source={this.state.photoSource}
resizeMode='cover'>
{this.props.children}
</Image>
);
}
});
ThegetPhotoParamsobjectcantakeavarietyofoptions,whichareoddlynotincludedin
thewebdocumentation.WecantakealookattheReactNativesourcecodetoseewhich
optionsareavailabletous:
<i>first</i>
number,thenumberofphotoswantedinreverseorderofthephotoapplication(i.e.,
mostrecentfirstforSavedPhotos)
<i>after</i>
string,acursorthatmatchespage_info{end_cursor}returnedfromapreviouscall
togetPhotos
<i>groupTypes</i>
string,specifieswhichgrouptousetofilterresults.Maybe<i>Album</i>,<i>All</i>,<i>Event</i>,etc.;
fulllistofGroupTypesarespecifiedinthesource
<i>groupName</i>
string,specifiesafilterongroupnames,suchas<i>RecentPhotos</i>oranalbumtitle
<i>assetType</i>
oneof<i>All</i>,<i>Photos</i>,or<i>Videos</i>,specifiesafilteronassettype
<i>mimeTypes</i>
<i>arrayofstrings,filtersbasedonmimetype(suchasimage/jpeg)</i>
InourbasicinvocationofgetPhotosinExample6-5,ourgetPhotoParamsobjectwas
quitesimple:
{first:1}
Howdowerenderanimagewe’vereceivedfromthecameraroll?Let’stakealookatthat
successcallback:
(data)=>{
this.setState({
photoSource:{uri:data.edges[0].node.image.uri}
})},
Thestructureofthedataobjectisnotimmediatelyapparent,soyou’lllikelywanttouse
thedebuggertoinspecttheobject.Eachoftheobjectsindata.edgeshasanodethat
representsaphoto;fromthere,youcangettheURIoftheactualasset.
Youmayrecallthatan<Image>componentcantakeaURIasitssourceproperty.So,we
canrenderanimageobtainedfromthecamerarollbysettingthesourceproperty
appropriately:
<Imagesource={this.state.photoSource}/>
Inmanyapps,wegivetheusertheabilitytoselectaphoto.Howdoyourenderthatphoto
selectionscreen?
<i>Figure6-5.PhotoselectionscreensintheTumblr(left)andTwitter(right)iOSapplications</i>
<i>Figure6-6.Defaultdialog</i>
So,youcaneitherusethebuilt-inelementforthis,orrollyourown.Applicationsoften
CameraRolltocreateasimplecustomviewoftheuser’sphotolibrary,shownin
<i>Figure6-7.TheCameraRollexamplefromtheUIExplorerapplication</i>
It’slittlemorethantheCameraRollinteractionswesawearlier,coupedwitha
<ListView>.Youcouldusethisapproachtodevelopacross-platformimageselection
componentforbothAndroidandiOS.
<b>ANDROIDSUPPORTFORPHOTOSELECTION</b>
Currently,ReactNativeprovidestheImagePickerIOSAPIforselectingphotosoraccessingthecameraon
iOS,butthereisn’tanequivalentforAndroidyet.Checkthedocumentationforthemostup-to-date
information.
YoucanimporttheImagePickerIOSmoduleintheusualway:
<b>var{</b>ImagePickerIOS}=React;
Then,usingitissimple.WecanqueryImagePickerIOStoseeifweareabletousethe
cameraorrecordvideos(Example6-7).
<i>Example6-7.Checkingifwemayaccessthecameraorrecordvideosusing</i>
<i>ImagePickerIOS</i>
ImagePickerIOS.canUseCamera((result)=>{
});
ImagePickerIOS.canRecordVideos((result)=>{
console.log(result);<i>//boolean</i>
});
Then,totriggerthephotoselectiondialog,wecallopenSelectDialog,furnishingitwith
someoptionsaswellascallbacksforsuccessfulphotoselection,andusercancellation
(Example6-8).
<i>Example6-8.TriggeringthephotoselectiondialogusingImagePickerIOS</i>
ImagePickerIOS.openSelectDialog(
{
showImages:true,
showVideos:false,
},
(data)=>{
this.setState({
photoSource:{uri:data}
});
},
()=>{
console.log('Usercanceledtheaction');
});
<i>Figure6-8.iOSselectiondialog</i>
Whatifyouwanttouploadaphotosomewhere?ReactNativeshipswithbuilt-inimage
uploadingfunctionalityintheXHRmodule.TheUIExplorerexampledemonstratesone
approach:
<b>var</b>formdata=newFormData();
...
formdata.append('image',{...this.state.randomPhoto,name:'image.jpg'});
...
xhr.send(formdata);
XHRisshortforXMLHttpRequest.ReactNativeimplementstheXHRAPIontopofthe
iOSnetworkingAPIs.Similartogeolocation,ReactNative’sXHRimplementationis
basedontheMDNspecification.
UsingXHRfornetworkrequestsissomewhatmorecomplex,comparedwiththeFetch
API,butthebasicapproachshouldlooksomethinglikeExample6-9.
<i>Example6-9.BasicstructureforPOSTingaphotousingXHR</i>
<b>var</b>xhr=newXMLHttpRequest();
xhr.open('POST',' /><b>var</b>formdata=newFormData();
formdata.append('image',{...this.state.photo,name:'image.jpg'});
xhr.send(formdata);
Mostapplicationswillneedtokeeptrackofsomevarietyofdata,persistently.Howdo
youaccomplishthiswithReactNative?
iOSprovidesuswithAsyncStorage,akey-valuestorethatisglobaltoyourapplication.If
youhaveusedLocalStorageontheWeb,AsyncStorageoughttofeelquitesimilar.
AsyncStorage,asthenamesuggests,isasynchronous;itsAPIisquitesimple,too,anda
ReactNativemoduleforitisincludedbydefault.Let’stakealookathowtouseit.
ThestoragekeyusedbyAsyncStoragecanbeanystring;it’scustomarytousetheformat
@AppName:key,likeso:
varSTORAGE_KEY='@SmarterWeather:zip';
TheAsyncStoragemodulereturnsapromiseinresponsetobothgetItemandsetItem.
ForSmarterWeather,let’sloadthestoredzipcodeincomponentDidMount:
AsyncStorage.getItem(STORAGE_KEY)
.then((value)=>{
if(value!==null){
Then,in_getForecaseForZip,wecanstorethezipcodevalue:
<i>Ifyouareworkingwithmorecomplicated,structureddata,orsimplymoreofit,youwill</i>
likelywantoptionsbeyondasimplekey-valuestore.
<i>Figure6-9.ContentsoftheSmarterWeatherproject</i>
<i>Thetop-levelcomponentislocatedinweather_project.js.Sharedfontstylesarelocatedin</i>
<i>styles/typography.js.ThefoldersForecast/,PhotoBackdrop/,Button/,andLocationButton/</i>
<i>Thetop-levelcomponentislocatedinweather_project.js(</i>Example6-10).Thisincludes
theuseofAsyncStoragetostorethemostrecentlocation.
<i>Example6-10.SmarterWeather/weather_project.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>
}=React;
<b>var</b>Forecast=require('./Forecast');
<b>var</b>LocationButton=require('./LocationButton');
<b>var</b>STORAGE_KEY='@SmarterWeather:zip';
<b>var</b>WEATHER_API_KEY='bbeb34ebf60ad50f7893e7440a1e2b0b';
<b>var</b>API_STEM=' />
<i>//Thisversionusesflowers.pngfromlocalassets</i>
<i>//varPhotoBackdrop=require('./PhotoBackdrop/local_image');</i>
<i>//Thisversionhasyoutopickaphoto</i>
<b>var</b>PhotoBackdrop=require('./PhotoBackdrop');
<i>//Thisversionpullsaspecifiedphotofromthecameraroll</i>
<i>//varPhotoBackdrop=require('./PhotoBackdrop/camera_roll_example');</i>
<b>var</b>WeatherProject=React.createClass({
if(value!==null){
this._getForecastForZip(value);
}
})
.catch((error)=>console.log('AsyncStorageerror:'+error.message))
.done();
},
_getForecastForZip:function(zip){
<i>//Storezipcode</i>
AsyncStorage.setItem(STORAGE_KEY,zip)
.then(()=>console.log('Savedselectiontodisk:'+zip))
.catch((error)=>console.log('AsyncStorageerror:'+error.message))
.done();
this._getForecast(
`${API_STEM}q=${zip}&units=imperial&APPID=${WEATHER_API_KEY}`);
},
_getForecastForCoords:function(lat,lon){
this._getForecast(
`${API_STEM}lat=${lat}&lon=${lon}&units=imperial&APPID=${WEATHER_API_KEY}`);
},
_getForecast:function(url,cb){
fetch(url)
.then((response)=>response.json())
.then((responseJSON)=>{
console.log(responseJSON);
this.setState({
forecast:{
description:responseJSON.weather[0].description,
temp:responseJSON.main.temp
}
});
})
.catch((error)=>{
console.warn(error);
});
},
_handleTextChange:function(event){
varzip=event.nativeEvent.text;
this._getForecastForZip(zip);
},
render:function(){
varcontent=null;
if(this.state.forecast!==null){
content=(
<Viewstyle={styles.row}>
<Forecast
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>
</View>);
}
return(
<PhotoBackdrop>
<Viewstyle={styles.overlay}>
<Viewstyle={styles.row}>
<Textstyle={textStyles.mainText}>
Currentweatherfor
</Text>
<Viewstyle={styles.zipContainer}>
<TextInput
style={[textStyles.mainText,styles.zipCode]}
returnKeyType='go'
onSubmitEditing={this._handleTextChange}/>
</View>
</View>
<Viewstyle={styles.row}>
<LocationButtononGetCoords={this._getForecastForCoords}/>
</View>
{content}
</View>
</PhotoBackdrop>
}
});
<b>var</b>textStyles=require('./styles/typography.js');
<b>var</b>styles=StyleSheet.create({
overlay:{
paddingTop:5,
backgroundColor:'#000000',
opacity:0.5,
},
row:{
width:400,
flex:1,
flexDirection:'row',
flexWrap:'nowrap',
alignItems:'center',
justifyContent:'center',
padding:30
},
zipContainer:{
flex:1,
borderBottomColor:'#DDDDDD',
borderBottomWidth:1,
marginLeft:5,
marginTop:3,
width:10
},
width:50,
height:textStyles.baseFontSize,
}
});
module.exports=WeatherProject;
<i>Itmakesuseofsharedstyleslocatedinstyles/typography.js(</i>Example6-11).
<i>Example6-11.SharedfontstylesarelocatedinSmarterWeather/styles/typography.js</i>
<b>var</b>React=require('react-native');
<b>var{</b>StyleSheet}=React;
<b>var</b>baseFontSize=18;
<b>var</b>styles=StyleSheet.create({
fontSize:baseFontSize+8,
color:'#FFFFFF'
},
mainText:{
fontSize:baseFontSize,
color:'#FFFFFF'
}
});
<i>//Foruseelsewhere…</i>
styles['baseFontSize']=baseFontSize;
Thiscomponentdisplaystheforecastinformation,includingthetemperature.It’susedby
the<WeatherProject>componentabove.Thecodeforthe<Forecast>componentis
providedinExample6-12.
<i>Example6-12.Forecastcomponentrendersinformationabouttheforecast</i>
<b>var</b>React=require('react-native');
<b>var{</b>Text,View,StyleSheet}=React;
<b>var</b>styles=require('../styles/typography.js');
<b>var</b>Forecast=React.createClass({
render:function(){
return(
<Viewstyle={forecastStyles.forecast}>
<Textstyle={styles.bigText}>
{this.props.main}
</Text>
<Textstyle={styles.mainText}>
Currentconditions:{this.props.description}
</Text>
<Textstyle={styles.bigText}>
{this.props.temp}°F
</Text>
</View>
);
}
});
<b>var</b>forecastStyles=StyleSheet.create({
forecast:{
alignItems:'center'
}
});
The<Button>componentisareusablecontainer-stylecomponent.Itprovidesaproperly-styled<Text>wrappedbya<TouchableHighlight>.Themaincomponentfileisprovided
inExample6-13,anditsassociatedstylesareprovidedinExample6-14.
<i>Example6-13.Buttoncomponentprovidesanappropriatelystyled<TouchableHighlight></i>
<i>containinga<Text></i>
<b>var</b>React=require('react-native');
<b>var{</b>
Text,
View,
TouchableHighlight
}=React;
<b>var</b>styles=require('./style.js');
<b>var</b>Button=React.createClass({
propTypes:{
onPress:React.PropTypes.func,
},
render:function(){
return(
<TouchableHighlightonPress={this.props.onPress}>
<Viewstyle={[styles.button,this.props.style]}>
<Text>
{this.props.label}
</Text>
</View>
</TouchableHighlight>
);
}
});
module.exports=Button;
<i>Example6-14.StylesfortheButtoncomponent</i>
<b>var</b>React=require('react-native');
<b>var{</b>StyleSheet}=React;
<b>var</b>baseFontSize=16;
<b>var</b>styles=StyleSheet.create({
button:{
backgroundColor:'#FFDDFF',
width:200,
padding:25,
borderRadius:5
},
});