Hi and
welcome to this course on React Native. My name is Max, and in this course I'll
teach you React Native from the ground up. And React Native is an amazing library
building up on the React, on the React library,
allowing you to build real native mobile
apps for iOS and Android with React. And therefore, I'd suggest let's not waste
any time, let's dive right in. And let's start
with the most important question first: What exactly is React
Native and how does it work? - [Maximilian] So, what
exactly is React Native? Well, as the name applies,
it is related to React.js. And with React.js and React Native, you can build real React
Native based mobile apps for iOS and Android, which are real apps you can distribute through the app stores
and apps that can be used by any user with an iOS or Android device. Now React.js is a library
that's independent from React Native though. React.js is a JavaScript library for building user interfaces. And it is typically used
for web development. That's also the environment
for which it was first created. But actually, if you worked with React.js, you might know that there,
it's actually another library the React DOM library that
adds the actual web support because React itself,
the library React itself, without React DOM is platform-agnostic, which means you can use React in conjunction with React
DOM to build web apps, but React the library itself,
actually does not care about the underlying platform. React just gives you
tools for managing state, for building virtual component trees, and then you need an extra
library like React DOM for translating the result React produced to an actual platform like the browser. Now, React Native is basically an alternative to React DOM, therefore. React Native gives you a collection of special react components, which you can use in your JSX code. So React Native ships with
built-in components you can use. And those components are then
compiled to native UI elements for the iOS and Android platforms, and React Native will also take care about this compilation step. In addition, React Native also exposes certain native platform APIs like using the device camera so that you can use such
features in your JavaScript code even though you need to tap into native device APIs for that. And therefore in the end,
React Native is like React DOM. It just does not target the
web, the browser as a platform, but instead iOS and Android. And React Native gives you all the components and the
APIs you need to interact with those platforms and to
build apps for those platforms. That is what React Native is all about. And therefore, in order
to work with React Native, you also must know React.js because you will write
your code in React.js and then just use these
extra React Native components and APIs in your React JavaScript code to produce those native mobile apps for iOS and Android. - [Maximilian] Now, how
does React Native work Under The Hood though? If you're building a React
and React Native app, then you are typically
writing code like this which should look very familiar, if you worked with React.js before maybe, or most likely for building browser based apps. This is a React Component what you see here. And it's just one way of writing such a Component. We could also use the
function keyword instead of defining an arrow function. And we could also use a
class based Component. But it's code like this, which you will write for React Native and it's code like this that will be compiled
to Real Native App code. So which will be bundled into a Real Native App in the end. Now, if you take a closer look at the code snippet on the left here, you will see two Components two JSX elements in there, which you don't know from web development, the View and the Text element. These are some of these special Components that are exposed by React Native, which will be compiled by React Native. It is worth noting though, that it's really just the Views. Just this JSX code that is compiled not the JavaScript logic itself. I will come back to that in a second. So the Components, the JSX elements you use are compiled to native elements for
the respective platforms. And to make this a bit
easier to understand, take a look at this comparison table here, here we got a bunch of elements, as we would use them in the browser if we would be using react-dom and then the equivalence for the native platforms, and then the Components exposed by React Native to
target those equivalence. So for example if you want to display an input element where users can enter some text, then when building a web app we would use the HTML input element which is available in JSX when we're using react-dom. If we would be building
a native Android app with Java or Kotlin then it would be the
built in added text widget that is provided by Android, which we need to build. For iOS it would be the UITextField element and with React Native we would use the built
in TextInput Component which is provided by React Native and which will then be
compiled to added text for Android or to UITextField for iOS. So React Native maps and
compiles reusable Components like TextInput or View or many other Components about which you will learn
throughout the course to their respective platform equivalents. That's one of the main jobs React Native will take care of. But what about the logic? I mentioned that those Views the JSX elements are compiled to their native equivalence. But what about the JavaScript code you write outside of JSX? What about all these functions? If statements? State management? What about that? Well, unlike the UI elements, the logic is not compiled. The UI elements which are those Components exposed by React Native are compiled as you learned, but the logic which you write in JavaScript is actually not compiled but instead it's running
on a JavaScript thread that's hosted by React Native in the native app that was built. So React Native basically spins up a simple JavaScript process as part of your native app that's being built. And it manages this process for you. And it allows this process to talk to the underlying native platform and therefore your JavaScript code will run as JavaScript in the native app that's being built but it will then talk to
the underlying Android or iOS platform, through a basic translation bridge. You could say that is provided by React Native as part of your native app. That's how you can think about this. And that is it for now. That's how React Native works Under The Hood and what it does for you. You don't need to know more. You can of course dive into the official docs if you do want to learn more but that is what you should know in order to work with React Native, because I believe it's always important to have a basic understanding
of how a tool works. And this is how React Native works. - [Maximilian] So I would say it's enough for the theory for now. Let's get started. And for that, you can simply
search for React Native, to find React Native dot dev, which is the official
React Native website. Now on this site, you can learn more about React Native and it is a great additional resource if you do wanna dive deeper
and learn more about it, but of course you will
learn all the essentials and everything you need to know
to build React Native apps, in this course. Now in here, on this side, it is a good idea to click
on get started though, because there you'll learn how you can create a React Native project. And what you will learn here, is that if you do click on
environment set up here, on setting up the development environment, that you got two choices. That you can use a tool called Expo CLI, which seems to be recommended because it's the by
default selected choice. And that you can use the React Native CLI. Now, as I just said, Expo
CLI is selected by default and indeed it is my recommendation as well and it is the tool we
will use in this course. But you could also use
this alternative approach. Both approaches will give
you a development environment in which you can build React Native apps. So what's the difference then? Then we have the Expo CLI
and the React Native CLI. CLI stands for command line interface and both tools are tools that exist in order to help you create
React Native projects and run React Native apps on
testing devices and simulators, as well as build React Native apps so that you can ship
them to the app stores. So these are the tools you
need to really build apps and to get those packages,
those distributable packages, which you can upload to the app stores. These two tools are crucial, but you don't need to use them both. You can use either of the
two and one tool is enough. But why do we have these two tools? And what's the difference? Well, the Expo CLI, or just Expo as I will also
call it throughout the course, and the company behind this CLI is in the end a third party
service that you can use. A service that is entirely free. You don't have to sign up,
you don't have to pay for it. You can sign up and you can pay, they do offer extra paid services but to build React Native
apps and to ship them you don't need to pay. When using Expo, the CLI, and a couple of other free
tools which they give you, you get a managed app
development workflow, as I like to call it, which means creating projects is easy, writing code is a bit easier, tapping into Native
device functionalities, like using the device
camera and so on, is easier and in general, when using Expo, the Expo CLI and also
couple of other tools provided by Expo, writing Native apps with React Native is more convenient than with
just the React Native CLI and without Expo. There is less friction to this process. And the best thing is that you can still
leave this Expo approach and ecosystem anytime, if you need to. If you start by using Expo because it's easier to
get started with that and then at a later point you find out that you wanna switch to just using the React
Native CLI workflow and not using Expo. You can do this anytime. That's why it's also the default selection to start with Expo, because it's easier and you can still switch
anytime you want to. Now, why do we have the
React Native CLI then? Well, because it existed
before we had Expo because this is the tool
provided by the React Native team and the community around React Native. It gives you a barebone React
Native development setup. Which means you will need
to do more configuration and setup work on your own. There are less convenience features, and if you need to tap into
certain Native device features, like using the camera or other features, then it is often a bit more work when not having Expo around
because, as mentioned before, Expo is not just about the
tool that creates projects, it will also give you
helpful packages and tools that you can use when writing code, that will make your life
easier in that case as well. Now, what the pure React Native
workflow does have though is that it is often a
bit easier to integrate with Native source code. So with source code written in Java, or Objective-C, or SWIFT or Kotlin. So if you must mix JavaScript code and Native device source code, then using just their React
Native CLI could be beneficial. But the idea behind React Native is that you don't have
to do this too often and therefore this might
not really be an advantage. And for all these reasons, and especially since you can
switch anytime if you need to, we will use Expo in this course. As you will see it's super
easy to get started with that and it will make building
React Native apps much easier. - [Maximilian] Therefore, back here on the official React Native website, we will stick to this Expo
CLI quick start approach here, and we need to run this command in order to install the Expo CLI, which is the tool that will then allow us to create Expo-based
React Native projects. Now, we do install this tool with NPM, which is the Node Package Manager, and therefore, you need
to install Node.js. Now, we don't need to install Node.js because we will write any Node.js code, but simply because we need
to install this package, and also because Node will be used by some packages under the hood. Hence, you should visit nodejs.org and make sure that you download
the LTS version from there, which in my case is version 16, but simply download whatever
the LTS version is for you when you are viewing this. I will download this and simply
walk through the installer. And there is an installer
for both Windows, macOS, and also for Linux, so you
can and should install this on whatever device you are on. And once you do have Node.js installed, you are ready to run this command in your terminal or command prompt. Here I'm in my terminal, and in there, I'll just paste in this command, and I'm on macOS where I must add a sudo in front of this command
in order to make it work. You might not need to do this, you definitely don't need
to add this on Windows because this is not a Windows command. On Windows, you just execute
NPM install -g expo-CLI, but on macOS and Linux,
you might need to add this, especially if you are getting some permissions error otherwise. With that, I will install this by also entering my password here and this will now download and install this Expo CLI tool globally, that's what -g does, on my system here. I will be back once this finished. Here it finished for me. You can ignore any warnings
you might have gotten here. It did finish successfully if you can run the Expo command thereafter and you don't get an error
when trying to do this. Now this, by the way,
shows you all the commands you can then execute with Expo. There are a lot. And the official Expo documentation is the place to go if you
wanna learn more about that. You do find that
documentation on expo.dev, but we don't need to take a closer look at those commands right now because there's only one
command we need to run right now and that is expo init, which
initializes a new project. But since I'm already on
this official website, as always, you might
wanna explore those docs in addition to this course, simply because in there you
can learn more about Expo, the philosophy behind Expo, how you could switch to a
React Native only project, though we will take a look at this later in the course, as
well, and how you can use it. This is a great backup resource
in addition to this course. Now, here we did now install Expo. Now we can create a new
project by running expo init. For this, I switched to another
folder here on my system, the folder where I wanna
create this new project. I switched to it here in the
terminal with the CD command, which you can also use on
Windows in the command prompt to switch folders and pops. And then, in the place where I wanna create
my new project folder, I will run expo init, and then I'll name my
project RNCourse here. This will be the name of the
folder which will be created and the default name of the
app that will be created in that project folder. If we now hit enter, we
get a little menu here where we can choose which kind
of project we wanna create. The options and the text
here could change over time. Right now, I can choose a blank project, a blank project with TypeScript support, a project that already has
navigation added to it, which we will take a look
at later in the course, or a bare workflow project, which is actually a project
that doesn't use too much Expo. But again, we will take a look
at that later in the course. Right now, we wanna have such
a managed workflow project where we fully embrace Expo
and take advantage of it, and I don't wanna use TypeScript, I also don't wanna use
navigation right now. We will add it later. Hence, I'll just use blank here. And this will now create a new blank Expo React Native project in a folder named RNCourse since that is the name I chose here. Now, this did now create
this project folder and it shows us some commands
we could execute in there to start the Expo Development Server, which will basically watch our code and create a development app bundle whenever we change the code. And we can then also run
that code in preview mode on iOS and Android, and even for the web, though that's not the
focus of this course. If you wanna build React web apps, then my React Complete
Guide is the best choice. Here we'll focus on Android and IOS. Nonetheless, I'll not execute
those commands right now. Instead, I wanna open
this new project first so that we can see what was
created in greater detail. - So we created this
project, this expert project before we run it now, I wanna open the created project in an editor that allows
me to edit the code in a convenient way. Now you can use any code editor you want, My recommendation is Visuals Studio Code, which is an amazing free code editor, That's very extensible and fun to use. You can download it for all platforms. And I already do have
installed it for Mac OS. Now with vscode installed, you can open it and you
might see a welcome screen. If you installed it for the first time, you can simply close that screen. And then you should see
something like this, where you can open a new folder. Alternatively, you can
always go to the menu file and find open on Windows, or open folder on Mac OS still. And then if you click that, it will open such a file picker, where you can now select that project folder you just created. So here I'm selecting this RNCOURSE folder which I created before, and
I'll open this in vscode. So here I got this expo
react-native project opened up and you can of course
configure the visuals, the way vscode looks, however you want. by going to view appearance, you can hide or show the sidebar, the
status bar at the bottom, and you can also go to preferences and then choose a color theme, that suits your needs. I'm using the dark plus theme here. so you can configure this
editor however you want but what was created for us here? Well, we see actually not too much. We got two folders, expo-shared and vscode which we can ignore, vscode just holds some settings i configured for this workspace here, And expo-shared simply just holds some internally used information. The assets folder will
become important later, because we can store images there. For example, the icon of our app but also images which
be used inside our app. And we got the node modules folder which holds all the third party packages that are used under the hood. We got a package, Json file which lists all the dependencies of our project here. And here For example, we see expo because expo is not just
the command line interface. It's all so a JavaScript
package which we can use, and which we will use heavily as you see because it gives us many
utility functionalities that make writing
react-native code easier. But you also see we're using react, react-dom, react-native,
and react-native-web, though again, that's not
the focus of this course. You could build web apps with react-native but support is a bit clunky. And yeah, we're simply not focusing on that because you could
just use react itself. If you want to do that
together with react-dom. React-dom of course
therefore is also just here because of react-native web. So that's what we have here. We also got a couple of script commands which we could execute with NPM but overall there's nothing fancy in here. And you know, these kinds of files if you worked with react before, which you should have because this course assumes that you have react and
JavaScript knowledge. So that's the package, Json file . Package-lock is related to that. And the babbleconfigjs
file just configures how code is trans piled under the hood, and unless you know what you're doing you also shouldn't change
this file therefore. Now we do have the app Json file, which will become important later because here we can
configure some settings and behaviors of our react-native app. And this is a file that
will be picked up by expo. When our app is built for preview or for the actual app stores. We can set the name here. We can set some background colors here and we will come back to this file occasionally
for configuration. So this is a file, which we
have because we're using expo. And then we got App js. This is the only real code file we have in this starter project. The exact content could change over time to make sure that we're
all on the same page. I attached my App js file to this lecture so that you can download it
and replace yours with it so that you have the
same code as I have it. Because we'll build up on this
code in the next lectures. This in here is a regular react component which uses special components special js x elements,
exposed, bio-react-native. That's what I mentioned before. And then we got some styling
which could look a bit weird but I'll come back to how styling works in react-native apps later. So for the moment, this is our app code, and now it would be nice
to see this code in action. For this, we need some device
on which we can preview it. - [Maximilian] So we got
our code opened up here. We created this project with Expo. Now it would be nice to preview
our app because of course, whilst we're working on
the React Native app, we want to constantly preview it so that we can see our changes and tweak the app as required. It would basically be nice to
have the same kind of workflow or a comparable workflow as we have it with the
browser and just React when we're building React Web apps. There, we can also create a React project and preview it in the browser. Now thankfully, since we're using Expo, previewing our app on a
real device is super easy. You just need an Android
device or an iPhone. In my case, I have an iPhone here, but the same is true for Android, and you visit the app store there. And in the app store,
you can search for Expo. Now, you should find a Expo Go app. This is an app provided by the
company behind the Expo CLI and this Expo package, which
is part of our project here. So you can now download
this app onto your device. This app is available for the iPhone and for Android devices. And once downloaded, you can open it. Now in here, you can
preview your Expo apps that are built on your local computer. To do that, you should
open up your terminal or command prompt. Here, I'm using the one
integrated into VS Code. You can open this by
going to terminal here and opening a new terminal. And this is the default system
terminal integrated here into your VS Code. And then in here you can run npm-start. Now, what this will do,
(device chiming) is it will actually start
the Expo Development Server which watches and builds our code and which builds our code, basically such that we can preview it. And it also gives us a QR code here. You can scan this QR code
with your Expo app on Android, not on iOS. I'll come back to this in a second, but on Android, you will
have a barcode scanner, a button that allows
you to scan this barcode in your Expo Go app which you downloaded. In addition, you also
should have a browser tab that opened up where you
also find this barcode, and where you will find log
messages printed by Expo as your app is running. Now on the iPhone, the
scanner is not integrated into the Expo app. Instead, there you need
to open your camera and then scan this barcode here
or the one in the terminal. And then once you tap to open the link that is sent by that barcode,
you should click Open, which opens the Expo Go app. Then you should grant any permissions the app might
(device chiming) be asking for. And now you see here that
the JavaScript bundle is being built. So the Expo app is being built. The React Native app
based on Expo is built and it opens up in this simulator
on our real device here. So in this Expo Go app here. We can close this info screen. And now here, we see our
app which we're building on our local machine
running on a real device. Now we can tell that this is
our app by changing this text. If I remove all the text
here, and instead I say, "Hello, World," good old
"Hello, World" message, and I then save this file, you will see that it
basically instantly updates on your phone as well in this Expo Go app which we use for previewing. And this is how simple it is to build and test those Expo
based React Native apps with the Expo Go app and
why it's so recommended to use this Expo workflow. It simply makes building and testing React Native apps much easier. And this still is a React Native app here. We're just using Expo to
make our life a bit easier. - [Maximilian] So, this is
how easy it is to get started with Expo and React Native. You can easily create an app and with help of the Expo Go App, you can easily preview it. And you might not need more for now. You can now follow along and preview your app
changes on your real device. But maybe you don't want
use your real device only. Maybe you also have an iPhone and you wanna also
preview the app on Android but you don't have an Android device. For those reasons, you might want to install a simulator which is an extra piece
of software that runs on your local machine that
simulates an iOS device or an Android device. Now, for this, you can
search for Android studio, to download Android
studio to get such a piece of software that allows you
to run Android emulators on your local machine. On developer.android.com/studio. You will find a download
link that allows you to download Android studio
for your operating system. And this can be downloaded
for Mac Os and Windows. Now the download can take a while but make sure you do download
and install it in order to be able to run emulators
on your local device. Now for iOS, it's different. There you must open
the app store on Mac Os and search for Xcode and
download this Xcode tool which is Apple's developer environment for building iOS apps. Unfortunately, this is not available for Windows or Linux devices. So if you are on a Windows
or a Linux machine, you can't run iOS
simulators through Xcode. This is a limitation applied by Apple. So unfortunately on Windows or Linux you can only build and test for Android. Actually, you will be able to
build real iOS apps as well through some Cloud build service about which I'll talk a little bit later but when it comes to
previewing the app on an iPhone if you don't own a real iPhone on which you can use the Expo go app or a Mac Os device on
which you can install Xcode you have to focus on Android instead. It is what it is. Now, once you did install Android studio, you can open it and you
should see a welcome screen that looks something like
this and that should have some more actions button. And in there you will find
a Virtual Device Manager. You can use this manager
to build virtual devices so to build emulators. So here I already have an emulator but you can click on create
device to build a new one. And here you can select from
a broad variety of presets like the pixel five, for example, here. Click next, then choose an Android version that will run on this emulator. And here I'll choose the API 32 version which is the latest version
in my case, click next keep all the default settings
here and click finish. And now this will create
such a new emulator. And then you can click the play button to launch this emulator. And this will now launch
this emulator on your device. So on your Windows or
Mac OS or Linux device. I'll just increase it a little bit here. So that's now an Android phone so to say running on our computer. Of course just a dummy phone, an emulator, but a real phone when it
comes to testing Android apps. Now I will show you how to run this app on this emulator in just a second. First, I wanna show you how it would work for Xcode and Mac OS. Which as mentioned only works on Mac OS. Here in Xcode you wanna open this as well. You don't have to open a project. You just need to open Xcode, and then you should go
to the preferences there. In the preferences make
sure you go to locations and here under command line tools make sure you have a
version selected here. By default no version is selected and you should do that
then select a version here. Once this is done, you can close it. And then we can go back here
to our Expo app in Vs Code. Now I used the terminal to
run this NPM start process. And this process is still running. You should by the way
keep this process running as long as you are working on your code. If you quit this process, your
code is not watched anymore. Preview builds are not built anymore and they are not pushed to your connected real
devices or simulators anymore. You can always quit this
process by pressing control + C and you can then restart it
by running MPM start again. But as long as your writing code you should keep it running. Now in there, we have this QR code which we could scan to use the
Expo go app on a real device but we can also press
A to run it on Android or I to run it on iOS and it will pick those running simulators or
start a simulator for you. Nonetheless it doesn't hurt to already start an Android simulator as I did it here so
that I can then press A. And now this is opening this on Android. So this app is now built and previewed on this Android emulator. Now, in case you're getting an error like this that is most likely related to the emulator you created. You might wanna create a new emulator and make sure that you create one or that you pick a template
that has this placed or icon. Because Expo needs to be able
to access this play store. So the play store must be part of the emulator in order to
download this Expo go app which is used for previewing
onto this emulator. So use an emulator
template that has this icon then still the latest
version, all the defaults and create this emulator
and start that emulator. So now I'm launching a new emulator based on this new template here. And once this finish starting, we can again restart NPM-Start maybe, and then press A again
to find this emulator, it will be found automatically by Expo and then it will install this Expo go app onto this emulator and
open our app there as well. We get this info screen,
which we can close. We get this debug screen here which we can use to reload
the app, for example or turn on certain debugging tools. But I will we'll come
back to debugging later. We can close this for
now and see our app here. So that's Android. Now for iOS development you should go to your applications and locate Xcode and show the package contents off that. Because in there in contents,
developer, applications you will find this simulator app. And you can double click on
this to start an iOS simulator. Now, in this case, it's
an iPhone 13 simulator. You can change this by
clicking on it and going to the menu and then
under file open simulator you can choose all kinds of devices. But I'll go with iPhone 13 here, and we can all increase this in size to make that a bit easier to view. And now we can press I here. You don't even need to restart NPM start. You can serve the preview on
Android and iOS simultaneously. So I just served it on Android. Now I will press I to also
started on the iOS simulator which also should be
detected automatically. And now Expo will also
install the Expo go app on that simulator and open this app in that Expo go app on our simulator. So here it's opening up
and after a short while and it only takes that long initially when this is all first
installed and set up, of course, after a short while we will see our app preview on this device as well. Here we go, it's loading up now and now we see Hello World here as well. So now we can preview
our little application on a real device as I
showed it to you a couple of minutes ago, and on
those simulators like this. And this process here
should always stay up and running as long as your writing code. So we'll just make it smaller. And now we can change our code add a couple of exclamation marks. And if we save this file, this is automatically
picked up and reloaded on those apps without us doing anything. You can still manually reload
it though by pressing R in this running terminal here this will then manually reload those apps. And that's the development environment we will use for this course. This is now what we need to
dive deeper into React Native. And this course here. - [Tutor] So let's dive into React Native and let's of course start with the basics. In this course section, we are going to explore the
core React Native concepts. This means that we'll dive into the core React Native Components, and we will start building user interfaces with those Components, but we will of course also dive into styling React Native apps. We will learn how we can style those apps and those Components, such that we can really
build the user interface we wanna build. And last but definitely not least, we will explore how we can
add interactivity to our app and how we can manage state, how we can make sure that there, for example are buttons
that can be clicked and that something meaningful happens when a button is clicked. Now for this, in this course section, we are going to build this demo app here. It's a first simple React Native app which allows you to track your goals. For example, your goals for this course, what you wanna achieve
by taking this course or as you are taking this course. In this app, we got a list of goals and we can open a model and overly to add new goals. And once a goal was added, we can also tap the
goal to remove it again. This is maybe not the
next billion dollar app but it's a great first app to learn about many core
fundamentals you must know about React Native. - [Instructor] So, let's
dive into React Native. For this, I'm back in this project we created in the first course section. In case you did not create it there or you did remove it thereafter, just make sure you got the development environment
set up mentioned there, and then you can download the attached starting project. Once you did that, you
will get the same code as I have it here. Just make sure that you open your terminal or command prompt. Here, I'm using the one
integrated in this code and that you install all the dependencies this project needs, by
running npm install. And once that completed,
you can run npm start to this Expo process that watches the code
and builds the preview and serves it on our emulators. I'll then press a and i to start and run this app on my running Android and iPhone emulators and simulators. Here you go. These are my two preview devices. Now, and actually as we
already got some code on which we worked in the
first course section already, and what we have in here in the end is a React component, a
Functional React component. And indeed, in this course, we will only work with
functional components and React hooks. We will not use class based components. So this is a regular functional component. We also got this Styles constant, this SyleSheet object down there. And I will come back to
that in a couple of minutes. At the top of this file,
we got some imports and we're importing one component from an Expo related package. And the other components,
namely View and Text as well as this SyleSheet object here, from React Native. And Text and View are two of those built in components, actually two of the most important built in components, React Native exposes to you, to use in your JSX code. Because in here, you
can't use divs or h2 tags or anything like this. These are HTML elements,
which work in the browser when you have a DOM to work with, but they won't work
here on a Native device, because Native devices are not browsers. They don't have that DOM, and they don't support
those HTML elements. Keep this slide from the
first course section in mind, that is how React Native
translates different elements into different Native elements for Android and iOS. And that's why these
components are exposed by React Native. Now, if you visit the
official React Native website, then there, you will find various guides which you can definitely take a look at but you will also find the components tab where you get a list of the components provided by React Native. And as you can tell, we actually don't have
that many components here because it turns out that with a couple of core components, you can build any kind of user interface. For example, we got a Button here and Image and Text and View. And especially View
and Text and Text Input are super important components which you will use all the time. And as you will learn from this course, you can build very complex user interfaces with those core components
that you have here. Now, React Native is all about working with those core components
that are built into it. You build your overall app UI and your custom components that might make up that UI, by simply combining those core components. Because these core
components, as you learned, are translated into Native UI widgets, Native UI elements for Android and iOS by a React Native. That's why we work with
those core components that are provided by React Native. And we can combine them in our React Code, in our JSX code, to
build our own components and ultimately our own user interface. So in the end, we just
compose our components by combining those core opponents. And if you think about it, that's the same for React for the web. HTML happens to have more elements. There are h1, h2 elements,
divs, article section, there are many HTML elements. But in the end, you also just combine those HTML elements to build
the web user interface. It's the same here for Native apps. So that's what we do
with those components. Of course, another important aspect of building user interfaces in general is that you wanna style those apps. And when using React
Native, there is no CSS. You can't write CSS code, because again, it's not a browser, so the CSS language doesn't exist here. Instead, you can, of
course, still apply Styles, but you do this either in line, essentially by using props on those core components that are provided or with help of these SyleSheet objects or this SyleSheet object which you already saw in the code before. You therefore. write your
styling in JavaScript. And hence, you have no
extra styling language. It's all JavaScript, but
the React Native team gives you a lot of JavaScript properties which you can set and pass
to those core elements, which are similar to the CSS properties. But it's only a subset of the overall CSS
property and feature set. But in the end, as you will learn throughout this Course, you get all the important CSS properties and features that you need to build powerful and
beautiful user interfaces. But back here in our component code, we can see how these core components that are provided by React Native are combined to build a new component, in this case, the only
component, the app component, and how we then get
this StyleSheet object, which is used to set various styles which if you take a look
at those properties, look similar to what you know from CSS. And then actually this
Styles object is provided to this View component
through a style prop. But we will take a closer look at styling and at building our own
components in general throughout the next minutes
and hours, of course. Now, just one final note
about this app component: At the moment, this is
our only component here. And indeed the app
component in the app JS file has a special purpose. This is the root component that is rendered in your app. Expo will automatically
take this component, export it in this app JS file, and render this as the root component. So, any other user interface
elements and components must go into this app component or into children or descendants
of this app component. But again, that is also
something we'll explore in greater details soon. - [Maximilian] So let's
start by diving deeper into core components like Text and View. And for this I'll actually
delete this status bar component and also this import. If I save that, this is
updated here on those devices and not much change here, so
we don't need this right now. I also will delete this Text
component and save that, so that for now we have a blank screen. And now let's try re
adding the text like this, "Hello World" between the View, without the surrounding text. If we do that, we notice that
we get an error down here. We get an error that "Text strengths must be rendered
within a Text component" which is actually a quite
helpful error message which tells us in a pretty
clear way, what's wrong. And before we fix this, let's also take a look at those devices, those simulators, and here we also get
the same error message. And this is actually something important which is why I'm showing this. It's different than what you might know from web development. If you would build a web app, and this would be react for the web and this would maybe be
a div instead of a View, then this kind of code would be fine. We could put text inside of a div there. React Native and native
platforms to be precise are stricter than that. You can't put a Text into a View which is pretty much
the equivalent to a div because it's not meant for that. Instead Views, so the of
to divs in the web world, Views are meant to generally build boxes and containers that hold other content. A View could hold some text
wrapped into a Text component. It could also hold a TextInput or a button or an image, or anything like this, but you have to put the content into an element that
is able to display it. And the View is only able
to hold other components, It's not able to display text, and that is something really
important to understand. If you wanna display a text, for example, you have to use this Text component and wrap the text opening and
closing text around your text. And if you do this and
save, then this updates and works again, but that's
a first important takeaway even though we can kind of
compare those component to HTML. And even though it looks kind of similar, there are important differences. Now, what you will often do
when building React Native apps is you will build more
complex user interfaces that don't just have one
piece of text inside of them. And what's important to understand then is that those different core components have different roles. The Text is a component
that's used for, guess what? Displaying text. The View is a component
that's meant to hold and lay out other components. Now, we will learn more
about laying things out with help of styling
in a couple of minutes, but the idea behind the
View component is that you typically have multiple
child components in there. For example, another text, "Another piece of text". In a Text component, as you learned, but now we got two of them. And we grouped them together in a View just as we might group
them together in a div if this would be for the web. Now, in the web, we also have
other components like section or article, here in React Native world, it's just the View. This is our container component that we use for holding our components. And of course, those other
components also can be nested. We could have another View which actually holds this inner text. So this is all something
that's totally fine, something you will typically
build when building more complex user interfaces
with React Native. The View then makes it
very easy to use styling into which we'll dive
in a second to control where which of its child
elements should be positioned. Of course, we don't just have
a View and Text to though. If you take a look at
the official website, the official React
Native documentation site you'll learn that there are more React Native core components and those components
fulfill different purposes. We got View and Text, which
are two of the most important and commonly used components, but we also could display a
image, add some scrolling, something will do later as well, or add a TextInput which allows
users to enter some text. We could also add a button, and indeed, that is what I'll do here. Below this text, I'll add a button, but for this, we need to import it. And that is also important and an important difference
compared to react for the web. With react for the web,
you don't need to import the HTL elements you might be using, with React Native, you do. If you wanna display a button, you have to import this core
component from React Native, and then you can add your button. And number important difference
is that unlike for the web, you don't build a button like this by adding the caption between
the opening and closing tags. Instead, it's a self-closing element where you add a title prop, and then you add your title here. This would now display a button as well with the kind of default styling and tap effects you would expect for the different platforms. And you see that the look adjusts to the underlying platform. It looks like an iOS button on iOS, and like an Android button on Android. But of course the button
doesn't do anything yet because I have added any listener. Now, we will add interactivity, and learn how that works later. For the moment, let's
just add the button here and let's understand that we always work with core components like this where every component has
its clearly defined purpose, and you build your overall UI
by combining those components. But of course a UI is not just
about combining components, it's also about styling them, and that's therefore
what will explore next. - [Instructor] So let's dive into styling. And here you learned before that there is no CSS
support in the React Native, so we don't add CSS files or use the CSS language in general, but you learned that we have
two main ways of adding styling by either adding styles in line, by passing a style object to props or by defining a separate object which is then also passed through props. Now we do define our
styles in JavaScript then but the JavaScript properties we can set are inspired by the CSS language though it's only a
subset of the properties and features supported by CSS and not all the names are exactly
the same, as you will see. But let's now see how
styles can be applied. For that, we can use the style prop, which is not supported on all elements, but on some elements,
for example on the view, but also on the text. For example, here on
let's say this text here, we can add the style prop. And then as a value, you can
pass a JavaScript object. And in this object you can set all the supported style properties
and React Native decides which properties are supported. And these are the
properties that are similar to the CSS properties,
but not exactly the same. For example, here on this
text, we could add a margin. And as you see, as I start typing, we get this nice auto
completion here in VS Code. We get this automatically
because VS Code understands which kind of properties
can be set on this object that is passed to the style prop. Now, here we could add a margin either in one specific direction
or in multiple directions by combining multiple properties or in all directions by setting margin. Now different style properties need different kinds of values. And you will see many examples and learn which kind of value
goes into which style property throughout this course. Margin, for example,
typically wants a number and this number will then
be translated to pixels which are automatically adjusted to the device pixel density. For example, here we could enter 16 to have a margin of 16 pixels. If we save this, we got
some spacing around this. Now it's not super good to see because we don't know exactly
where the text box ends. To make this a bit more
visible, we could add a border. Now in Vanilla CSS,
you could add a border, like this for example. This would add a red
border with one pixel width around an element in Vanilla CSS. But if I type this kind of code and saved this here in React Native, we actually got no effect here, and I got a error here in the terminal and a warning here on my simulator. We can click on this to get more details and we see that this is an
invalid value for border. Actually the key border
already is invalid. So it's not even the value but border is not a supported key. The reason for this is simply that this border property doesn't exist. I mentioned that the styling
language is inspired by CSS and close to CSS, but
it's not exactly the same. Instead, as we type border, we see that various properties
exist, which we can set. For example, we can set
a border with property and this again wants a number to set a certain border width in
pixels, like one, one pixel, or two or whatever you want. And then we can also set a bordered color. Now this does not want a
number, but instead of string, and in this string, you
could define a hex code value Or you provide one of
the supported shortcuts like red or blue or
green or black and white. And here I will go for red. And this will now add a two
pixel, white, red border around this text. Now I'll press the auto format shortcut to make this a bit easier
to read and then save this. And with this, we now got
a border around this text. The border is pretty close to the text but thankfully we can
also add some padding, padding just as in the browser, is the spacing inside of an element. So here we could add a
padding of let's say 16 again, 16 pixels, just as for the margin. And if we save this and auto format again to improve readability, now
we got some internal spacing. Now definitely feel
free to play around more with those style properties. And of course also take a
look at the official docs to learn more about
styling if you want to, but you will see plenty
of styling examples throughout the course. These style properties
were all about styling the element itself that holds
a certain piece of content. We will later also learn
about style properties that help us with laying
out multiple components and with achieving a certain overall look. But again, that is something
we will explore later. For the moment, I wanna leave this in-line
styling approach though. This approach of defining
the styles in the same line as we define this component. Because whilst this is
possible and allowed, it's rarely the best
way of adding styling. So typically we should go
for a style sheet objects. The reason for this, is that this allows us to clearly separate our JSX code and our styling code and it also makes our styles reusable. If we define styles in line and I want the same styling on this text, I have to copy and paste this, so that I can add my style prop here and add the same styling, and I'll auto format this
to make it more readable. So now I had to copy and
paste to replicate this style. And that is typically
something you wanna avoid. What you should instead do is create a new style sheet object. Or if you already have
one, as we have it here, you go to this object and
you add a new property. We already have the container property, now on the same level, I'm
adding another property. The name is up to you and
I'll just name it dummy text. Again, this is totally up to you. What's not up to you is what
you set as a value though. The value should be an object, as we see it here for a container. And then in this object, you
define your style properties. So here I could now set
my margin and so on. So I can set margin 16, border
width two and border color, I'll set this to red. And I will all set my padding
and the order does not matter. I just group margin and
padding together here, since they're both about spacing, but we could also add padding here, and it wouldn't matter. Of course I could have
also just copied down my object from up here. Now with such a style
object to find down here, we can use it here in our JSX code as this container is being used. It is defined down here,
but you can still use it in JSX because this only gets executed after the entire code file
has been parsed basically. So here we can now replace this object by referring to styles.dummytext and I get nice auto
completion for this as well. And I'll do this here as well. And now, yes, we had to
copy the word, dummy text, but of course it's way less to copy. And if we ever adjust the style, for example we changed the color to blue, we don't have to do this in two places because the name didn't change, we just had to do it in one place instead. So if I save this, we
now got a blue border. And that's how we use
such a style sheet object. Now, why is it a style sheet object? So an object created with
help of style sheet create which is a built-in method
on this built-in object which is imported from React Native. Well, we could have also created a styles object like this,
and set our styles there, but using such a style sheet
object has the advantage that we get convenient auto completion as we type our style properties here. And that makes our
development life a bit easier. In addition, React Native
could potentially optimize style sheet creation and
management internally and pick up style sheet objects like this. This is not done right now though, so for the moment, the main
reason is the auto completion. But with that, we now learn
some important styling basics and some important basics
about core components. I would say, as a next step, we should take a closer
look at creating layouts and positioning multiple elements because typically you don't just want a couple of centered
pieces of text like this. Typically you are going
for different layouts and we need to know how we can create such layouts therefore. - [Maximilian] So let's
explore how we can build real layouts where we
don't just have a bunch of centered texts and buttons. And for this, I will actually
clean up my JSX content and I will remove this style prop here on the outer view for the moment and remove all the styling
here in this style sheet object but I will keep the style sheet object. And now here in my view, I actually wanna add a neverview and below that a neverview. Now I'm doing this because I wanna start building
a basic interface here that allows us to manage goals. And the first view, the first nested view here I should say, should actually hold the input area where users can enter
the text for their goal and click a button to add that goal. And the second view should
later hold the list of goals that are rendered. And I'm using two views as rappers to have a clear separation
between these two areas, and because data will
help me with styling. So for this, I'll then therefore start by adding a text input component
here in the first view. This is a component that
allows users to enter text and like all components,
all core components, we need to import it from react-native. Now text input is a
self-closing component. And here we can, for example,
add some placeholder text as you could add it to a
input element in the web and say, your course goal. Then, next to it I want to add a button with the title of add goal. And then here in this view,
I will add a text element where I say, list of goals and later this will become a real list. Now, if we save all of that, we see that everything is
crunched here at the top. We also see that it goes
beneath the status bar and there would be different
ways of fixing this. For the moment, I'll fix it by giving this outer view a style prop and defining a style, which
I'll name, app container. And in there I'll add a padding
of, let's say, 50 pixels. And then here we can refer
to styles.appContainer to add to this padding to
the overall outer view. Now, if you save this, we can see that there's
more space around our UI. Again, later, we will
learn about different ways of making sure that we don't interfere with those native status bars and so on. Now, still, this is not the layout I want. For example, I want the button
to be next to this input, not beneath it. I also want to have more space
between the list of goals and maybe this area where we add a goal should take up a quarter
of the overall height and the list should take up the rest. And that's where we need
new styling features. Styling properties about which
we haven't learned before. Specifically, we need flex box. - [Instructor] When it
comes to building layouts in React Native apps,
Flexbox is super important. And you might know Flexbox
from the web and CSS. And then the next lecture,
will have a little deep dive into Flexbox and how to use it. But Flexbox is a key
approach, a key concept which is basically a
collection of CSS properties that you use to control
how things look like. And the Flexbox implemented
here in React Native, which can be set with all
those React Native properties, is very similar to what
you know from CSS Flexbox. In the end Flexbox is all about
a couple of CSS properties, or a couple of styling
properties to be precise here, that are about positioning
elements inside of containers. You can control how much space
certain elements take up, and if they are to the left or right, or bottom or top of other elements. And the positioning is then controlled with the style settings that
you apply to the container that holds other elements,
as you will see in a second. Now Flexbox is all about Q-axis which you have on a container. For example, the styles which
you see here at the top, flex one, flex-direction:
column, and so on, would be applied to the container that holds the three boxes inside of it. Now the flex one instruction
here, for example, would tell the container
that it should expand to occupy all available space,
though as you will see soon, this is seen in relation
to other containers that might also take up space. And then flex-direction
controls whether the elements are laid out in a column
as you see it here, or in a row as you see it here. So that all works. Basically, flex-direction
controls if the main axis, as it's called, is top to
bottom or left to right. With column, it's top to bottom, with row, it's left to right. And then you also got other
properties like justify-content or align items that allow you to control how the elements are
laid out in their axis. So if there is some space between them, if there are crunched together and so on. - [Teacher] So back in our code, this means that we can
use flex box on this view, which is the container
of text input and button, to control how these two
elements are positioned, because you learned flex box is a concept that is applied to container elements to control how much space
the container takes up. That's something we'll do in a second. And then how the elements inside of the container are positioned. That is what we'll do right now. For this, I'll add a
number, style property here in my style sheet object, and I will name it Input Container. Now on this element, we can
now set flex direction to row, and then we can apply this
input container styling to this view. So here we add the style prop, and then refer to styles
dot input container. If we save that, you will see the button is
now next to the input element. So this already utilizes
flex box because by default flex box is enabled on all those views. You can start using those
flex properties right away. The default setting was that
flex direction was column, which is why the elements
were beneath each other. But if we switched to row, as I just did, they sit next to each
other in the same row, which hopefully makes sense. Now we might want to control
how they are laid out, and for this, we can add another property
to the input container, and that is the justify content property. You will learn more about
all these flex box properties in depth in the next lecture, but justify content basically controls how these elements are
distributed in the row or column depending on what the value
here is that they are part of. So here, for example, space between is one
of the allowed values, and as you set this to space between, you see there now is more
space between these elements. Now we can also still control
the width of those elements. For example, here, we could
add a text input style object in our style sheet object, and I will add this to
the text input here. And here I then wanna add a
border with a width of one and a border color of CCCCCC,
which is a light gray. By the way, I get a color picker here because of an extra
extension, which I installed. You don't need that though. And now, in addition, I will give this text input
a width of, let's say, 80%. And this is all the new. Now in most places where you
can set a size as pixels, so as numbers, you can also
set sizes in percentage, though you need to wrap that in quotes so you set a string in the end. But then you add the percentage sign and your percentage number
to set a certain percentage. And with this, I'm saying
that I want this element, which has this style, to take up 80% off the available width. The available width will
be defined by the container in which the element sits, but then the element
which receives this style will take 80% of that container's width. So therefore, now I want to
add this text input style here on this text input element, and we do this by adding the style prop and then its styles, text input. If we do this and save that, now we got this border, and
this looks like 80% width. Maybe we also want a little
bit of margin to the right so that there is some spacing here. There is no spacing right now,
despite having space between, because this takes 80% of the width and the button automatically
takes the rest. And therefore to add some spacing here, we'll add a margin to the
right on the text input of, let's say, eight pixels. With that, there is some spacing again. We could also add a little
bit of padding maybe so that the text isn't
directly on the edge. So, therefore, I'll also
add padding eight here on the text input object. So with that, we get some padding as well. Now that's it for this basic
introduction to flex box. This is an not all we'll do. This is not the finished layout. I do see that the
spacing is a bit off here on the right and the left. I do see that the button
doesn't look that great here on Android. And we will work on this, but first let's dive a little
bit deeper into flex box and the different things we can do with it before we then continue
working on this app. - [Maximilian] In this lecture, I wanna dive a bit deeper into
Flexbox especially regarding how you use it in React Native apps. If you know all about that already, you can of course skip this lecture. So for this, I prepared a
simple dummy application and of course, you find data attached. It's a normal React Native
app built with Expo. And in the App.js file here, what I have in the end, is
just a view with three views in there where each view then has a text with text one, two, three. This simply creates some boxes with different colors,
red, blue, and green. And now we'll use Flexbox
to move these boxes around so that you can get a feeling
for how Flexbox works, because it's so important. Now, first things first, by default, every view in React Native, even if you assign no
special styles, uses Flexbox. And that's different to
the web, for example. There, if you have a div,
which would be your equivalent to a view kind of, it doesn't use Flexbox by default. In React Native, it does. Every view by default
organizes its children with the help of this Flexbox thing. Flexbox simply is, is a term, It's simply a concept from CSS that is all about
organizing child elements in a one dimensional space. So here, for example, in a column. That's also in every default, not only does every view
by default you use Flexbox, it also by default organizes
children in a column, so from top to bottom. That's all the difference to the web and I don't wanna emphasize
these differences too much because of course you don't
need to be a web developer to build React Native apps. But I think a lot of people
do know web development, do know a CSS Flexbox, and
therefore it makes sense to all the talk about the differences. So in the web, when you use Flexbox not only is it not turned on by default, in addition if you do turn it on, the default is to organize
all child elements in a row. And here, the default is to
organize them in a column. You can change that default though. So in this case, on the
view, which holds my boxes by adding flexDirection here, and setting this to row, for example. Now, you will see that these
three boxes are organized in a row from left to right. Now besides row and column, you also have row reverse
and column reverse. And this simply also, well
does what the name implies, now we still have a row, but the first element, the red
box, actually is on the right and not on the left anymore. So that's also something you can do. Let me go back to row view. So that's the first thing you can do. Another important thing about Flexbox is how child elements are sized. Here, I gave every child element a width and a height of 100. Now, if we would remove
that width and height thing on every child element, then you will see that now we have a very,
very small row here because every box now is only as wide as its child requires it to be. And only as tall as its
child requires it to be. So every box here which holds a number is only as wide and tall as
the number it's containing. Now, you can change that with the surrounding Flexbox container 2. Let's give that width
of let's say 300 pixels or of 80% of the parent width. So in this case, since it's the root element
of the device width, and let's give it a
height of, let's say 300. If we do that, and now really important, I'm doing this on the view,
which holds all these boxes. I'm not doing it on the boxes themselves. So if we assign this width and height on the surrounding box, you
see something interesting. The height is assumed
for all the elements. Now, all the views in the Flexbox take the
height of the parent. The width has no impact here. That's also a default
behavior you got here, obviously since we
haven't changed anything. The default behavior here indeed,
is that the child elements in a Flexbox, so in this outer view here, are organized such that
they align themselves along the cross axis by stretching. Okay, that were a lot of terms, what does this mean? Now, when working with Flexbox, we have two important axis. The main axis depends
on your flex direction. For row, which we have
here, flex direction row. the main axis is from left to right. For a row reverse, it
would be right to left. For column, it would be top to bottom and for column reverse it
would be bottom to top. So that's the main axis. And then you also have a cross axis and that's simply the
opposite of the main axis. So for a row where the main
axis is from left to right the cross axis would
be from top to bottom. If the main axis is from right to left which would be the case for row reverse then the cross axis would
be from bottom to top. Okay, so that's the main
axis and cross axis concept. Now, you can organize your child elements. So in this view where
we have the free boxes as child elements, you can organize these child
elements along these axis. You use justifyContent
to organize elements along the main axis. And you have alignItems
to organize elements around the cross axis. Now you see the values you
got for justifyContent here. If you add these quotes or if you place your cursor in there and you hit control+space. You see you can center
elements, you can position them at the end or at the
start of that container or you can add some space in between. For example, if we use space between here and we use alignItems center,
then things will change. Now, you will see they're taking the width of the surrounding container, every box itself still is pretty small but they're split or they're distributed across the width of the parent container. And they're no longer taking the height because along the cross axis, we're aligning them with align items. And there I set this to center. The default here is stretch. And if I set it back to stretch, then unsurprisingly they do
stretch for the entire height. Now, if you want to make sure that they take the available width, you can set stretch
here on justify content which is your main axis
positioning vehicle. So you can set stretch here. So what can you do regarding that then? Well, that is something you now configure on every child item itself. You can tell a child item
how much space it should take off the space it's potentially getting. Stretch here is kind of a special case, there you set this up on the parent item. Normally, you set this
up on the child item. So for example, if I
set this to center now so that the parent doesn't tell the child how much space it should take. then we can fully control
the space a child takes by going to the child style. And there you can add
flex, the flex property. The flex property is applied to items that are inside of a Flexbox, so that are inside of a
view in this case here. And that can be a view itself but that could also be another component like a text, for example. So now here you can add flex and you can set this to a
value of one, for example. So flex needs to be a number. If you set this to one, what you will see is that
now the red container where I did set flex to one, takes all the available width it can get just so much that it leaves enough space for the blue and the green container so that they can squeeze their content into the surrounding Flexbox. Now, we can't see the boundaries of the surrounding container but the boundaries would
essentially be where the red item starts and the green item ends on
the horizontal axis here. So now flex 1 makes sure that the red item gets
as big as it can get, So it takes as much space as it can get. but default views only take as much space as their child elements require. So as this one character required, but with flex one you change this and they now take as much
space along the main axis. So along the width here, as they can get. For the cross axis, again,
that's a special case, You have to do this on the parent. For the main axis, and
since we have row here, the main axis is a horizontal
axis from left to right, you do this with the
flex property on a child. Now, of course you can add flex to other child elements as well. Like to the second, to the blue container, with the two in there. You can add flex 1 there as well. So now I have flex 1 on the red container and flex 1 on the blue container. And what now happens is that both of them take the available free space,
and amongst these two boxes, the space is distributed evenly. And that's what this
number here indicates. This number is a relative number. All items in the same Flexbox with the flex property
distribute the available space by considering the number you assign here. And these numbers are
relative to each other, so if I give the blue
container flex 2 here, then this means that of the
available space you have in that surrounding container. After deducting the space, every element needs to
squeeze its content in there. The blue container will
take twice as much space as this one, because here we have flex 1, here we have flex 2. If we had flex 3 here, then this would take three fifths of the available free space, because we have three plus two, so we have five available
segments, so to say. And here the red container
would take three segments, blue container would take two segments. If we have one and two, then we have three available segments, and the blue container takes two of them, red takes one. So you always add up these flex numbers and then distribute or that's
automatically done, of course but then the available space
is distributed accordingly. So now here we'll see that the blue container is
twice as big as the red one or it takes twice as much
free space as the red one. So this is how you can work with flex. You can organize how items are positioned with flex-direction, with justify content and with align items. And you can also make your items grow and shrink the help of flex. So now that's our brief
introduction to Flexbox in React Native. As I said, inspired by Flexbox for CSS. So if you knew that, all of what I explained here
is probably not new to you. We'll work with Flexbox
throughout this course, so there're things also
will become clearer and we'll work a lot with it. And you'll see you how
you can create beautiful user interfaces with Flexbox. Flexbox in the end is the tool in React Native to structure your content on a page, to organize your content. And you would typically
work with a lot of views which you also nest into each other so that you can position
elements the way you want. Because of course, and
that's also important, you don't just have to
have one view in your app which uses Flexbox. You could have another view in there which also uses Flexbox. And actually, as I mentioned every view by default uses Flexbox and you can then nest
these views into each other so that you position
everything the way you want. And you'll also see this
in this module already. And actually I'm already
doing it here in my views here which are in the surrounding view. So my boxes here, there
I also use justifyContent and alignItems to center
my numbers in these boxes. So that one, two and
three are centered there horizontally and vertically. And that works because
we have Flexbox turned on by default and we can't
turn it off by the way. And therefore, I just use
these two properties here to align my content of this view along the main and the cross axis. And here, since I have set
no special flex direction for this view, the main
axis is top to bottom because the default
flex direction is column and the cross axis is left to right. That's just a side note. - [Instructor] So now after
this deep dive, let's use our newly gained knowledge to
improve this overall layout. One thing I will do for example, is work on this app container padding. This actually doesn't use
any of the new knowledge but I need to do this to make
this look a bit prettier. I basically just want to have
a padding to the top of 50 and then I wanna have
a padding horizontal, so left and right of 16. So not of 50 but of 16. With that I'd say, this
generally looks better, though we can see that this
padding is kind of ignored and our button here is
going off the screen. This can be fixed by
decreasing the amount of width our text input has to 70% maybe. Now this looks better. What doesn't look better
though is where this text is in this button. This is something we can
improve with align items on the input container though. At the moment, the problem
is that basically this button is stretched to be as
high as this text input. And the text is then not
centered in the button because the button doesn't have any styles that would center the text. Now you could think that
maybe we can add some styling to the button but it
turned out that the button is a component that
doesn't have a style prop. It might look like it
has because I'm getting some auto completion here
but that's actually only some auto completion I get here
because I typed the word style before in this code file. The proper auto completion that
tells me that a certain prop is supported looks like
this with this kind of icon next to it. So the button doesn't have a style prop because indeed it doesn't support styling. You can always check out the
official docs and dive into an element there to see
which props it supports. And for example, on the
button, there is no style prop. Whereas on the view, for example,
you do find a style prop. But that's just a side note,
that's why we have to fix it differently and we can fix it
by not stretching this button. That would be one way of achieving this. So we can set a line items
here on input container and set this to center instead of stretch. With that, the button is now
centered and looks better. We could also build our own button but that is something
you will learn later. So now this looks better. I also wanna change how much
space this input area takes up though and how much space
the list of goals takes up. To achieve this, I'll
do a couple of things. For example, I wanna add
some padding to the bottom on this input container of let's say 24. so that there is some space
between the view that holds the text input and the button
and the view that will hold the list of goals later. You can see that spacing here now. We also could add a little border here, so we could add a border to the bottom and give it a width by setting
border bottom width of one. And then adding a border to the
bottom and giving it a color of let's say, maybe this gray again. But of course you can
pick any color you want. Now we got this border and
below that to the list of goals. But now I want to make sure
that my text input area takes up one fourth, so 1/4 of
the overall available height. And this can be done
with the flex property as you learned. We can set this to one and
then add a styling object to this view that holds the list of goals that takes up three quarters
of the available space, which can be achieved by
adding flex three to this view. So therefore I'll add
another styling object in my style sheet here and
name it, the goals container. And here I'll set flex three. As you learned, all the flex
values are added together as long as the containers
to which they are added are siblings to each other and are part of the same surrounding parent
element which is the case for these two views and then the space is
distributed accordingly. So one plus three is four
and therefore this takes one fourth and this will
take three quarters. We just have to add this here,
so I will add a style prop to this view and set this equal
to styles goals container. With that, it will look horrible. And the reason for this is
that we also need to work on the outer container now. The container that holds
these two containers on which we just set the flex properties. This outer container needs
to take up the entire height of the app so that the inner containers can then distribute this space. By default, without the flex property, this container will only take
as much space as it needs and that's only defined by the space the content in the container takes up. But now that we're setting
flex on those containers in the container, this
doesn't work anymore. And to fix this, we need to
force this container to take all the available height and
we do this by simply adding flex one to the app container
so that the outer container takes all the height because
it's the only container here. So flex one will give it all the space and then the inner containers can divide that available space. So by adding flex one to the
app container and saving this, this now looks better. And now this is a quarter and
that here are three quarters. Now if I look at it like this, this still doesn't look perfect
so what we might wanna do is actually give that list here
4/5 of the available space, so that goals container will get four and the input container stays at one. And instead of setting a padding bottom, I will set a margin bottom
here on the input container. And with that, that looks better. We could maybe even go
to flex five down there and leave it like this. Feel free to experiment with this and choose your own values. But this will work for me and
this is now flex box in action to achieve a first basic layout. Of course, this is not the final
layout we will have though. - [Instructor] So we did add some styling and a basic layout. This is nice, but of course, not all we wanna do. In most apps, no matter if we're talking about web apps or native mobile apps, we also want to allow the
user to interact with the app. And we got a text input here and we also got a button
that could be pressed, but at the moment, of course, nothing happens if you type here or if you press this button, this is ignored. And this is ignored because we're not handling these events. And just as with a web app that
you're building with React, you, of course, need to handle them if you want something to happen. The good news is that you do
handle events in the same way you would do it in web apps though. You can add event listeners and connect them to
event handler functions and you can manage
state in your components with the useState Hook, just as you would do it
in any other React app. Because this is just a React app, the only difference, as you learned in the first course section, compared to a React app
that targets the web is that we use a different platform with React Native instead of React DOM. But when it comes to React's core features like handling events or managing state, that all works in exactly the same way as you know it from React for the web. And here I got two events
that I wanna handle. The first one would be
a click on this button but before we handle that I also wanna handle keystrokes
on that TextInput element so that I can grab the
value the user entered and so that we can then use this value to finally add a new goal in this list of goals as a next step. And therefore, these are the first two things I wanna do. I will add a function that could be called goalInputHandler that is responsible for
fetching that user input as the user types. Then I'll another function addGoalHandler which should be fired when
this button here is clicked. Now to connect those functions, we use special event listening props that are provided by React
Native on its components. For example, to connect goalInputHandler to TextInput, we can add the special onChangeText prop. This is a prop exposed by React Native which wants a function as a value, a pointer at a function. In my case, I'll point at this
goalInputHandler function. So I'll pass that as a value here and I'll press the auto format shortcut to make this a bit easier to read. Now, please note that I don't
execute this function here. I don't add parentheses. If you would add parentheses this function would be executed as soon as this code is
parsed and evaluated. And this would be too soon, because that would be the case when this user interface is rendered. I don't wanna do that. Instead, I wanna point at this function so that React can execute it for me whenever this text changes in this input and that's what will happen if we set it up like this. Now in this goalInputHandler
function here, we then receive the
entered value automatically because keep in mind, it will be React that calls this function because it's React Native
that exposes this prop. And therefore, React, or to be precise React Native, will also provide us a value as a input as a parameter to this function and that will be the enteredText. So we get the enteredText automatically. And then we could, for example, console.log this here. Like this. If we save this code and we have the npm-start
process up and running, we can go back here. And for example, enter Test. And as I type this here, you see the log update here in my terminal with every keystroke until
we have the word Test. So that's how this works and this works totally on its own. So that's how we can
get the entered value. Of course, we don't want to get the value because we need it in
this function though. Instead, we need it in this function. So we need it when the button is clicked. And for this, we need to connect this function to a button click as a first step. Now, if you would be building a web app and you had a HTML button here, you would add the onClick prop but with React Native, this prop doesn't exist on a button. But we have a similar prop, it's called onPress. It's called onPress instead of onClick because technically we don't have clicks in native mobile apps. Instead, we have taps or presses. So here we have onPress. And just as with onChangeText, here I point at my function
that should be executed. In this case, addGoalHandler. So now this function will be triggered whenever the button is clicked or tapped. Now we want the value entered in this TextInput in this function. So what we need to do is we need to store it as state. We need to store it as state which is updated with every
keystroke in this function so that we can then use it in this second function. And state management works just as it works in any other React app. We can import the
useState Hook from React. So not from React Native, but from React: the core React library itself. And then we can register a new state here in our App component function just as we do it in any other React app. So here I'll call useState and set this to an empty string initially, because this will be my user input state or my enteredGoalText state, which can be updated with the
setEnteredGoalText function. And then in case this syntax
doesn't tell you anything, definitely have a look at
some dedicated React resources like my React complete guide course because this is standard React and you should know syntax like this. So now we got this enteredGoalText state, which initially is an empty string, but with every keystroke in my TextInput, I will update it and set my enteredGoalText to the enteredText I'm
receiving here as a parameter. And therefore, now, in addGoalHandler, we can access this state and do something with it. And for the moment, I will simply console.log my
enteredGoalText state here. But of course, soon, we will update a list of goals which we then output down there. So let's save this. And now here, if I enter Test and click Add Goal, we got this Test log here. Or if we enter Some other text! to see that this is really
coming from our updated code. Now we see Some other text!, here. So this is working as intended. The next step now is to make sure that we
can manage a list of goals, which we then can also output. - [Maximilian] So let's now
manage that list of goals. For this I will again add some state here because my list of goals is also some data that changes dynamically and as it changes, the
UI should be updated. And that's a typical case for using state. And this state will be
initialized with an empty array because I wanna manage a list of goals and initially I have no goal. Now I'll give this state
a name of course, goals, because these are my goals
for this course, let's say. And to set course goals,
updating function. And now in add goal handler, instead of logging our
entered text to the console, we can call set course
goal and update our goals. And in the end I wanna
take my existing goals and append a new one. Now, there are various ways of doing that. One way would be to create a new array and use the spread
operator, these three dots, which is standard JavaScript code, modern JavaScript code, to spread existing course
goals into this new array, so that I keep all the
existing course goals. And then I add my new goal, by adding the entered goal text, let's say, as a new goal. In this case, a goal is simply a string. We could do this, but if you
followed a good React resource, like my course or the
official docs, then you know that this is not the best
way of updating state if your new state depends
on the previous state. Which this state here does. If your new state depends
on the previous state, a better way of updating is to pass a function to
this state updating function, a function which will be
called automatically by React Q then derive the new state. And this function will automatically receive the existing state by React. So here we could turn this
into an arrow function that gets the current course
goals as a input, let's say. So as a parameter. And again, the value for this parameter will be provided automatically by React when it calls this updating function or this function that's passed
to the updating function, to be precise. And now this value, current core goals, can be used inside this
arrow function body. And now we do derive this
new state in a better way. The other approach would've worked as well but this is a recommended way, a best practice way of
updating your state. So now we're updating
this course goal state based on the old course goals
by appending a new goal. The next step of course is to now output the list of goals here. And how does this work? Well, it works basically as it does in React for the web as well. We have an array of values
which we wanna output and in React for the web we
would use the map method, typically, to map our array of
data, of strings, or objects, into an array of JSX elements
which can then be output in the JSX code returned
by this component. And we can do the same thing here. Instead of outputting this
dummy text down there, I want to output something dynamic, which we do by adding curly
braces, opening and closing. We can now refer to
the course goals array, and call the map method on it. A standard JavaScript method because we are writing
standard JavaScript code here. As you learned. Now, this map method receives
a function, as it always does, and here I'll pass in an arrow function, which as an argument, as a parameter, gets the individual values
stored in course goals, so my goals here, the single goals I have
in this array of goals, this function is called for
every item in the array. So every function called
receives a single goal. And then as a return value
in this arrow function, we returned a JSX element that should be rendered
for the individual goal. And in this case, I wanna
output the goal text. So here I will render a text JSX element, and then between the
opening and closing text I output goal. Since my goals are just
strings here in this array because I just add my entered goal text, which is just the text that was provided, and therefore just a string, I can simply output the goal
between those text like this because goal will just be a string. And with that, if we save this and we give this a try here. Learn React Native. If I click add goal, this is added but we also get a warning here. The same will be true on the iPhone here. If I learn React Native, it works. If I learn it with
multiple exclamation marks I still see that it works. I add another goal now as expected but we get a warning here. We get a warning that each child in a list should have a unique key prop. And this is actually a
warning you might know from React for the web as well, because this is a warning that has nothing to do with React Native. Instead with React in general, when outputting a list of
data, as we're doing it here, every item in that list
should receive a key prop which uniquely identifies
the individual list item. Under the hood this helps React update the list in an
efficient way, so to say. I do explain this in greater
detail in my React course, but you should always add this key prop to the individual items that you're outputting as part of a list. Now, the value you pass
to key can be anything but it should be something unique that uniquely identifies
the concrete value that you're outputting here. In this case, I'm just outputting goals, which are just strings, so I'll take the goal string itself. This is actually not necessarily unique because I could enter the
exact same goal text twice but it's good enough for
now, we'll improve this soon. Now with this key prop added here though. Now, if I add another goal, I
don't get the warning again. And of course here, we don't have to enter the
same text all the time. We can also learn
different pieces of text, like learn all the details, and as you see, our list
keeps on growing here. So with that, we're now able
to handle the user input, store it and React to
a tap on this button. And then we do something
which you certainly did many times with React for the web. We're outputting that data here in a list, but that's of course
not all we want to do. - [Maximilian] Outputting
the data in a list as we're doing it here is nice but it would also be
nice to have some styling on those list items. And therefore, that's
what I wanna do next, of course feel free to
also pause the video here and practice this on your own. Apply some styling, any
styling of your choice to these individual list items, and then compare your solution to mine to see how I style this. Now, here's what I will do. I will create a new object that keeps or that holds the styles
for those list items. And I'll name it, goal item. The name is up to you though, here in this style sheet
object, and then in this object which defines the styles for
this identifier, you can set up any styles you want, and
I'll give every goal item a little bit of margin, for example so that we have some spacing
around it, maybe eight pixels. I also wanna add a border radius here so that we have rounded corners and this also wants a value,
that's defined as a number. And here I'll set this to six pixels. Now to make sure that
we can see this radius, I will give this a background color so that this item has an
overall background color of a specific hex code that
I prepared ahead of time which is five E zero A C C,
which is a nice purple color. I also wanna have some padding in here so I'll actually add it next to the margin not because it's required but to keep those related values together. And I'll add a padding of,
let's say eight pixels as well. So that we have some
inner spacing as well. And last but not least, I
also want to add a color here and I'll set this to white and
this will be the text color. So again, you see the property
names are pretty similar to what you know from CSS. We of course have this camel
casing instead of using dashes because dashes are not
supported in JavaScript but besides that, the
names should look familiar. Now we can assign this goal item style to our text items here. So here we can add the style prop and set this equal to styles.goalItem. And I'll reformat this to
make it a bit easier to read. With that, if we save this, this updates and we see our items here. What you will see is that
we got this changed styling but actually on iOS, the
rounded corners are missing. We only have them on Android. Now, why are they missing here? And what can we do to fix
the styling there as well? This is a good time to talk about the differences
between the two platforms. The idea behind React Native
is that you can write code with React that will actually
give you native mobile apps for multiple target platforms. So you don't need to write different code to target different platforms, but one of the same code
base can get you there. Now this is generally true, but as you will also
learn later in the course, sometimes there are differences and sometimes you need to
make tiny code adjustments to target all platforms in the right way. And for example, here,
we got a difference. The rounded corners are missing on iOS because we added our styling here that applies the rounded corners directly on this text component. Now it turns out that this text component is
translated by React Native to a fitting native widget,
a native UI element. And the element to which it is translated in case of Android seems to be an element where corners can be rounded. But in case of iOS, it looks like the underlying
native text output element does not support rounded corners. And that's why border radius has no effect on the text elements on iOS. Now to work around that, we can wrap our text here in a view. So in this more generic container element, this is also translated to a
fitting underlying element. And it turns out that
the underlying element to which view is compiled by
React Native is an element that supports surrounded
corners on both platforms. How do I know that? From the official docs and experience and I'm explaining it
to you in this course so that you don't have to
find it out the hard way. So what we can do now is we can grab our style assignment here and add this on this view and also add the key here on this view, because now the view is the main element that's output here in the list. The text now is a nested element. We still need the text because we still have some text to output. And as you learned before,
text must go between text text but the styling is now applied to the more versatile view element. If we do that and we save everything we now see the rounded
corners on iOS as well. What we also see though is that the text is no longer white. Now we still have the
color white setting here but this is part of the
goal item, which now is actually assigned to this
view here, not to the text and unlike in CSS for the
web, the styles don't cascade. That's another important learning here. We do have this CSS like
property language here if you wanna call it like that, these CSS like property
names, but it's not CSS. It never was. So therefore core CSS characteristics like the cascading nature
of CSS, where child elements and descendant elements,
inherit styles from parent and ancestor elements is not part of React Native's styling approach. We don't have style inheritance here. So if we assign a text color to the view, this does not affect this text or the text inside this
text text to be precise because that's a totally
standalone element. It is not related to this view. It does not inherit any
styles from that view. So setting the text color
on this view does nothing to the text inside that text element. Therefore, what we need to do is add another style object here, the goal text, that sounds
like a fitting name. And we set our text color on that and remove it from goal item on the view. We could leave it there, but it doesn't have any effect, anyways. With that, we now set our text color here and now we can add this goal
text style to this text here. So we add the style prop and set this equal to styles.goalText. And with this, if we save this now, we now have the white text back and we have the rounded corners. And this is really important. This is something you must understand about styling and about
targeting different platforms. The general idea is that
you have one code base that targets both platforms but still there are
differences under the hood. And some differences can
simply be solved like this by adding an extra wrapper. Later in the course, you will also learn
that sometimes you need to write totally different
code for different platforms and you will learn how that works as well. And you also learned that styling is a bit
different than in the browser. It looks like CSS, at least a bit but there is no inheritance
and no cascading going on. That's why you really
have to set the styles on the elements where
they are supported instead of some global parent element, just as the rounded corners
couldn't be applied to the text, the text color applied to this
view didn't have any effect to the internal text here. We need to set that on the
appropriate text element. And these are these tiny
details, which you will see over and over again throughout this course and which are super important
when you are working with React Native. But with this, we now
got the look we want here and hence, we can now move
on and dive a bit deeper into how we can and should
output lists like this. - [Maximilian] So we got our list here. Now, one thing you will
notice about this list is that if I add more and more items here and I just add exclamation
marks to make them unique, at some point we will go
beyond the available space. Now you would expect that
you simply scroll now, but it turns out you can't scroll. No matter how you try,
this is not scrollable. Because by default the
view generated here, this view that holds
everything in our app component is not scrollable. That's also a difference
compared to the browser. On a website, if you
exceed the available space by default scrolling bars will appear, and you can scroll the website. This is not the case here in mobile apps. If you want to scroll, you have to explicitly tell React Native and the native platforms. Now, of course, this is a common problem
and scenario though, and, therefore, making lists, or content in general,
scrollable, is very easy. There is a dedicated component
provided by React Native that helps you. And that's the ScrollView. We import ScrollView
from React Native just as we imported text
input button and so on. And ScrollView does what the name implies, It gives you a view so
such a container element, which will be scrollable. And therefore we can
now use this ScrollView around our goals here. So let's try replacing this view here with our ScrollView, simply. We add ScrollView here instead of view around our list of goals. And I leave my style on this ScrollView. Keep in mind this style here controls that this container
takes up a lot of space. If we save this, this
doesn't look too good. This now is indeed scrollable, as you see, but the space is different
than it was before, we have way more space for the input and way less space for the list. The reason for that is that
the ScrollView has the job of making something
scrollable, but the area that's scrollable is in the
end determined by the current, the container that holds the ScrollView. So what you should do here
is add another normal view, which restricts the available height, to which you then apply the style that sets the height that
will be taken up by that view. So that is the setup we
had before, but now we have a ScrollView inside of that view. So the outer view, controls
how much space this area of the screen should take up. And the inner ScrollView
then makes sure that this space, and the items in
that space, to be precise, will be scrollable inside
that overall space. That's how we set up such a ScrollView, and how we let it interact
with the surrounding container. If we set it up like this
and we save everything, we get the same proportions as before, but now, indeed, as you see,
this list is scrollable here. And that's, of course, what we want, because now we have a list where we can add more and more items and it stays scrollable as we
exceed the available space. Of course, if I force a reload, by pressing R here in the terminal. And I then go back, all the data is lost, and now you see, nothing
seems to be scrollable here. If I add a single goal here, we do see, though, that this is indeed scrollable, but of course it's not really scrollable, It always jumps back to the top because we don't exceed the available height. So, ScrollView is super useful
because it does allow you to make lists, or content
in general, scrollable. Now you can learn more about ScrollView and the props it supports
in the official docs. I wanna draw your attention
to those props here, because there is a lot you can configure, about the ScrollView. You can, for example,
configure how it bounces, and you'll also see that some
props are specific to iOS. And others specific to Android, whilst yet others are
available for both platforms. And again, these are some of
these platform differences, which we will encounter here and then throughout this course. For example, you will notice that in our app here, I can scroll and I have this bouncing effect. If I would want to disable this, I could set always bounce vertical, which is true by default, to false. So if I go to ScrollView, and I set always bounce vertical to false, and I then save this, you
will notice that here in iOS, if I now try to scroll, this bouncing effect, which
we had before, is gone. Now, if I add more and
more goals to make sure that I exceed the available
space here, at some point, it still is scrollable, and now it also bounces,
but it didn't bounce, if there was not enough content
to fill the available space, the available height here. If you do want to set this or
not as totally up to you and which look and feel you
want, but it is important to be aware of the vast configuration
options you have here. And therefore taking a
look at those props here, and understanding what you can set and how you can use
this view is definitely something I would encourage you to do because every app you're
building will be different and every app needs slightly
different configuration. And therefore the official docs are always a great place where you can find all kinds of supported configuration settings. - [Maximilian] So we got
our scrollable list here, and this is implemented
with this ScrollView. Now, this solution has one downside, one disadvantage though. ScrollView is great, if you wanna add scrolling
around some content. For example, if you had an article, which would be too long to fit on a screen or you simply don't know which device sizes your
users will be using and you wanna make sure
that an article for example, is scrollable. In such situations, ScrollView is perfect. For lists as we have it here,
it's not perfect though. The reason for that is, that lists like this
could become very long. Sure, you might only have
like 10 or 20 items on there. And especially for such a goal list, it's very likely that
you have around that size or around that amount of items. But you could also have lists with hundreds or thousands of items. Now, what ScrollView does is, it always renders all the
items that are inside of it whenever the overall UI is rendered. So even if an item is not
currently on the screen like this last list item here, this item is still
rendered behind the scenes even though it's not visible yet. Because ScrollView renders
all its child items, no matter if we're talking
about 10, 20, or 10,000 items. And for very long list, this
can become a performance issue. If you have a long list with dozens or hundreds
or thousands of items, rendering them all at the beginning even though vast majority is
not even visible to the user, will slow down the app. And of course, that's
not something we want. Therefore ScrollView is great
for limited amounts of content like an article which has
a fixed end at some point. But for dynamic lists, which
could become super long. We typical don't wanna use ScrollView. We can, but we typically don't want to, to avoid possible performance issues. A better solution for such scenarios is another built-in component
that ships with React Native, the FlatList component, this is another component
that will help scrolling. But as the name suggests, it's specifically built
for scrollable lists. And internally, it will
only render the items that are actually visible. And all the items that are off screen will only be loaded and rendered lazily as they are needed because
the user is scrolling. Now, internally, FlatList
will have a small threshold to make sure it already starts
loading and rendering items before they are visible. But it will only render them
as the user gets closer to them as he or she is scrolling the list. If you have 1000 items in your list the vast majority of
items will not be loaded and will not be rendered
when using FlatList. So how do we with FlatList then? Well, you do use it by
replacing ScrollView with it. So we replace ScrollView with FlatList. Now, regarding this
alwaysBounceVertical prop here, this is still supported. So you can leave it there because
FlatList actually supports pretty much all of the
props ScrollView supports. Because internally it kind of
has a similar implementation but with this optimization of only loading data when it's needed. So this is a first step,
but we're not finished yet. We replace ScrollView with FlatList. The next step is that
we get rid of our custom mapping code here, we remove that, we no longer map our data manually. Instead, we now have to
pass this task to FlatList so that it can render
this list efficiently by only rendering what's needed. FlatList is now in charge of this hence we need to let FlatList do that. And this is done by giving
FlatList two important props. The first one is the data
prop, which is a required prop. This is a prop that points at the data that should be output in this list. In our example here, that
would be the courseGoals array. That's the data that
should be output as a list so we pass this as a value to data. But that doesn't tell FlatList how the data should be rendered. You don't tell FlatList by
passing content like this, between the opening and closing text, that's not how we use FlatList. Instead, we should cut this and we can turn FlatList into
a self-closing component. And we add another key prop now, and that's another required
prop, the renderItem prop. This is a prop that wants
a function as a value, a function that will tell FlatList how to render the individual data items. Therefore it is a function that will receive the individual item as a parameter, automatically. FlatList will pass in this item whenever it calls this function, and it will call this function whenever it determines that
a new item must be rendered because the user is scrolling and a new item should be made visible. And therefore, in this function, in this array function
which I defined here, you then return to JSX code
that should be rendered for a given item. Now, of course, we wanna
output our item data and we received this item parameter value automatically by FlatList. But this item which we get here is actually an object generated
by FlatList internally. An object that is wrapped
around the individual data items we have in our data array. And therefore I will
actually name this itemData because it's actually an object that does not just contain our values, but also some metadata. Now, itemData does for example, also give us access to a index property. Again, this object with this property is generated by FlatList. And this holds the index of the different items that are rendered. First item here has an index of zero, the second item has an
index of one and so on. But itemData also holds another property that's very important for us and that's the property
I will output here, the item property. This will hold the actual data items, one, for each list item
that's being rendered. So with itemData.item, we get access to the different
data items we have here. Now, with that, we can
get rid of this key prop because we're no longer
mapping this list manually and hence keys are no
longer managed like this. We will come back to keys in a second but for the moment we'll remove it. And now I will reload the
apps by pressing R here, in the terminal with
the terminal selected. And if I now add another new item here, you will see it shows up and
I can add multiple items here. And if I add enough items to
exceed this screen height. So if I add more and more items here, you will see that this
list also is scrollable, I can scroll here. But you also see of course
that I'm getting a warning, I did get it right from
the start actually. And the warning is the end,
that I'm missing some keys here. Because it's still a list and therefore we still should add keys, but we don't add them
as we added them before. Instead when using FlatList, there are two main ways of
adding keys to these list items. The first approach is
to turn your data values from primitive values like
strings as we have it here, into objects that have a key property. And that's what I'll do here. When I add a new courseGoal, instead of just adding the
entered text like this as a goal so as a plain string, I will
wrap this into a object. Where I, for example have a text property that the actual goal text, but where I actually
also add a key property which I set to a unique key. Now, to generate a unique key here, I will actually use
Math.random toString like this. Which is not really a unique key. I could generate the same number twice but it's good enough for this demo here. And now with that every item
in my course goals array is actually an object, an object with a text property
and with a key property. And the special thing is that FlatList will automatically look
for such a key property. So FlatList does work
with primitive values in the data array as well as you saw a couple of minutes ago. But it works even better, if your data in this data
array is a list of objects. So if you have a list of objects as data and every object has a key property, because then this key property will automatically be used as a key for the items that are rendered. Now, that we turned our items
into objects here though, we have to make sure that
when we access the data that belongs to a single item, we dive into the text
property here on the item. Because every item is now an object with a text property,
that's what we set up here. Hence we have to access this
text property down here. And then with that, if we save this and again reload this and I now start adding items here, you will see that now I can
add items as many as I want and I don't get this warning anymore. And I got this strollable list. And under the hood, only the items that are
needed are being rendered and the other items will be loaded lazily as we scroll closer to them. That's how this FlatList works now. Now, if you don't have
a property named key because you're maybe
getting data from an API and you can't influence its shape, and you don't wanna transform it just because flat list
needs that key property. You also got an alternative to setting up such a key
property in the input data. Let's say this would be named ID, this would be a reasonable property name because this could be a unique ID. But it would be the wrong name because this list is not
looking for an ID property but for a key property. Now, of course we could transform it but if you don't wanna do that,
you can go to your FlatList and you can add another prop
to the FlatList component. And that would be the keyExtractor prop. The keyExtractor prop wants
a function as a value, for example, an inline arrow function. But you could also define
a function somewhere else and pass up a point or edit here. And this is a function which will automatically
receive two parameter values, item and index. These two values will
be provided by FlatList when it calls this function for you. And it will call this function for you, for every list item that's being rendered. Now, the renderItem function is also called for every
list item that's rendered. But the renderItem function is responsible for
generating in the JSX code that should be rendered for every item. KeyExtractor on the other hand, this function is simply
called to get a key out of every item, which then under the hood will be attached to the item by the FlatList. And therefore here you
should return the key. And since you get the item
as the first parameter value, and since I know that every
item for me is an object with an ID property. And since I know that the ID
property makes for a great key since it will be unique,
I can return item.id here. And with that, I simply tell FlatList how it gets to a unique key
for every item it outputs. So with that, if I save this and I then again, reload everything here. If I again, learn React Native
here, this works as before. And I don't get any warnings. This off course, all the
works here on Android if you wanna see it there. I can add multiple goals there as well and there it's also scrollable. So that's FlatList and action,
and you should use FlatList whenever you have lists with dynamic data that should be scrollable. And especially if you have
lists that could be super long. Then, for performance reasons FlatList is better than ScrollView. - [Maximilian] So by
now, we already learned a lot about important
React Native components and concepts in general. Our app JS file is
getting bigger and bigger. And this is something that
happens in most React apps, no matter if you're
building for React Native, or if you're building for the web. Your components get bigger as
you add more functionality. Now, what do in such cases is
you break up your components and you split them into
smaller components. That is a common approach
and a common best practice for keeping your code maintainable. And it's no difference
here for React Native. Thus far we're working
with one component only but this of course is the exception. The norm is that you do work
with multiple components. And we wanna do that here as well. For this, I'll add a
new folder in my project which I'll name "components" because I wanna store
my components in there. You don't have to use this name but it is a common convention since we will be building and
storing components in there. And then I want to add two
components files in there. Goal input JS, because there I plan to store the data input related JSX
code and functionality. And goal item JS, here I plan to store the actual JSX code and functionality that is related without putting single goal items. And of course you could split
this into more components, if you wanted to. Now I'll start working on
this goal item component. In there I'll add a function goal item because I'll create a functional component and I will export this as a default. So I will build a standard
React component in there. Now you don't need to import
React from React here. You did need to do that in the past but with the modern versions
of React Native and React you don't need to do that anymore and therefore a functional component can be created just like this. Now in this goal item component, I wanna return some JSX code and I actually wanna return the code that is responsible for
outputting a single goal item. So not the list of
items, but a single item. And that means I wanna use this code here. Hence, I will cut this code from app JS and return it here in goal item JS. Now, of course, here, I'm
referring to some styles though and those styles therefore
also should be brought over. For this I have to create
a new style sheet object that belongs to this component. So to this component file also and hence I will import from React Native and I will import the
style sheet object here. Which allows me to call the create method, which creates a new React
Native managed styling object, which helps with auto
completion and so on. I'll store this in a
constant named styles, though the name is up to you, but since I'm already referring
to this kind of constant here in my code, I will keep the name here. Now as a side note, we could
have also passed our styles from app component to goal
item component via props. We don't have to define the styles here but it is a good practice
to keep your styles close to your JSX code. And therefore I wanna move my styles that belong to the goal item from the app component into the
goal item component as well. And that would be these two objects with their style properties. I will cut them in app
JS and move them over to goal item JS. And here in this object which
we pass to the create method, I'll paste in these two properties. And with that, I can
reference those properties in my JSX code. So with that, we are
creating this component. We still need to take
care about this text, but we get our first component and then app JS we can
now import this component. We can now also get rid of some imports which we don't need anymore
because I'm, for example, not displaying a text component in there and I'm not using scroll view anymore But I will now import goal
item from components goal item. And this works just as it
works in our regular React app, we import our components
from other files like this, we have a capital starting character because we plan on using this in JSX and we wanna make it clear to React that this is a custom component. Hence we start with a capital character. Now down here in our JSX code, where I return some markup
inside of render item. I now return my goal item
component, so my own component. And this is how we define
and use a custom component. And of course in the end
it's totally the same as we do it in a React for web project. There isn't really any difference here. - [Maximilian] Now of course
we will need to pass some data to goal item though, because
we got the item data here in the app component and we need it here. And this is a problem solved With props. Again, just as we do it in any react app because we are building
a regular react app here, we have a regular react component and we can use props therefore. And we can expect and
use any props we want, because it's our component. So here for example, we could expect that on this props object,
we get a text property, or a value property whatever you want. I will go with text here. And now we just have to make
sure that in the place where we use this component, so
in the app component here, We pass this prop so that
we set a text prop here, on goal item in the app component. And then here we have to pass in item data dot item dot text. Because again, text is
the property that holds the actual text and I wanna
pass this text as a value to the text prop in my goal item. And with that if we save
this and I reload everything, if I go back and I learn React
Native here, this crashes. Because of this error,
can't find variable view. What's wrong here? Well, what's wrong is that
in the goal item component, I'm using the view and the text components but I'm not importing them. And I showed this on purpose, because this is important to understand. If you are working with
a react for the WAP, you can use DVs and h2
elements just like this. You don't need to import them. That's different in React Native. There, all these components
are provided by react native, and you have to import
them in order to use them. So besides importing the style sheet here, we also have to import view and text, because we are using these two
components in this JSX code. And once we import them, if
we now reload our apps again, you will see that now of course
if I do learn React Native, this works again. And now it's looking
the same way as before, Because in the end we get
the same UI as before, but we're achieving this with help of another custom component. - [Maximilian] So now we
got this custom component. But we got another custom
component, which I wanna build. And this component should hold the input related markup and logic. Now, definitely feel free to
extract the appropriate code from App.js into GoalInput.js on your own and build your own GoalInput component, which you use in App.js. In a couple of seconds, which
I give you to pause the video. We will do this together. So, did you succeed? Let's try this together. Now here, I want to extract
some markup into GoalInput. For this, I'll First of all, create a GoalInput component here, in the GoalInput.js file and export this. and then we wanna return
some JSX code in here. The JSX code that should be
returned is the TextInput and the Button, but I
will also grab the View that holds these elements
as the container, Because that View holds the input related container styling. And I wanna outsource this well. So here, I will add this
container with the TextInput and the Button to my GoalInput.js file. Now, just as before, we need
to do a couple of things. We need to import View,
TextInput, and Button and we need to bring over
our style sheet objects. Now, I'll start by importing, and I will import View,
TextInput, and Button from react-native, so that we
can use these components here. And it will also import StyleSheet, because, of course, I
will create a style sheet. I will create such a styles object here with StyleSheet.create, so
that we can set up the styles that belong to this component
in this component file. And in our case, that would be the inputContainer
and TextInput styles. So in App.js here, I will
grab the inputContainer and TextInput style objects, and cut them, so that
our style definitions in App.js are now much leaner. And it will move them over into the GoalInput component file. So here in this object,
I'll now paste them in, and that makes sure our styles are there. But now we're not done. We also are handling some state here. We're getting the user
input and we're interacting with the main courseGoals
state with help of this Button. But now the problem, of course, is that the courseGoals state is managed in the App component, whereas, the inputs are
in a different component. But that's a common problem in react apps and not specific to react-native, and we can solve it as we
solve it in all react apps. We can also talk to the parent component by passing event handler
functions via props. So here, for example, we can
accept props on GoalInput and expect a special prop which
could be named, onAddGoal, to be provided by the parent component, which actually holds a function as a value that will be executed upon a press. So now in the App component, we can use the GoalInput
component We just added, we can import GoalInput
from ./components/GoalInput. And we can use this
component here in that place, where we had all the input
related markup before. And set this onAddGoal prop, which we expect to get here
in our GoalInput component. And now, since we set this prop's value as a value for the onPress prop, and that prop wants a
function to be executed, we should pass a function to
onAddGoal in the App component. And we still pass our
addGoalHandler here, just like this. Now what does change though is how we handle the user input. How we fetch the user input? Because this GoalInputHandler
and the state that manages the entered input
is in the wrong place now. We don't do this in the
App component anymore. Because the TextInput element
is in the GoalInput component. What we should do therefore
is grab that state, and cut it from the App component, and move it into the GoalInput
component, like this. For this, of course, we must
make sure that we import useState from react, in
this GoalInput component. So that we can use this function here. Now we got this state. We also need this
GoalInputHandler function, which updates the state though. So that in the App component,
only the courseGoals state and the addGoalHandler function is left. We will treat this function
as second as well though. In GoalInput.js, we can now paste in that GoalInputHandler function. And therefore this is now, again, connected to onChangeText, where we point at this function. So this enteredGoalText is
still being handled here. Now, the only problem is
that this enteredGoalText is eventually needed in the App component, when we add a new goal. and therefore we also need to make sure that this data arrives
here in addGoalHandler. The easiest way of ensuring this would be to expect the enteredGoalText
as a parameter. So that whenever addGoalHandler is called, we get this text and
we can use it in here. Now, this function is fine. But right now this text
wouldn't be provided. addGoalHandler is passed as a value to onAddGoal on the GoalInput, and onAddGoal is forwarded to onPress, and onPress which ultimately
calls this function does not provide this enteredGoalText. To make sure that it does, we have to change
something in our code here. For example, we can add
another function here in our GoalInput component file and name this function
addGoalHandler, again. So now we have this function
or a function with this name in both GoalInput and
App, but that's no problem because we're in
different components here. Hence, we have no name clash. Now in this function, I actually want to
execute props onAddGoal. So I want to call this onAddGoal function, which we receive here manually And then, onPress is connected to this addGoalHandler
function which I just added. By calling this function manually, I can now make sure that I
forward my enteredGoalText. So this state, I passed this
as a value to the function I receive on this onAddGoal prop. And therefore, since function
is the addGoalHandler in the App component. That addGoalHandler function will receive the enteredGoalText because I'm passing it manually here. And again, this is nothing
react-native specific. This is standard react. Now as extra bonus, we could also set our
enteredGoalText to an empty string to clear it, whenever we added a new goal. We didn't do that before
but we can do it now. And I think it is a
change that makes sense. But of course, it's up to you whether you want to do that or not. Here, I will do it by setting the state
back to an empty string. With all of that, back in App component, this should all work. We can now get rid of
some unused imports again, which we don't need anymore. And if we now save everything, and reload the app on both devices, if I now learn react-native,
this goal still gets added. Now what's not happening is that the input would be reset though. Actually internally it is. If I click add goal, you
see there was no text. But it's not reflected
here in the input element. The reason for that is that I'm resetting the state here in GoalInput, but I'm not binding
the state to TextInput. We don't have any proper
two way binding here. We just have one way binding. In the past, this didn't matter, because as we never changed the text besides typing into the TextInput field. But now since we're also
changing the state here, like this by resetting it, we should add a value prop to TextInput, and bind this to the enteredGoalText. So that whenever this goal text changes, because we for example, reset it, this is reflected in the TextInput. If we now save this,
and reload everything, I can, again, learn, react-native. Now it's reset. And I can add my goals. So this is now working. And now, we split this
into multiple components. And in the end, as you see, here we had absolutely
nothing react-native specific. Splitting our app and passing
data and functions around, that all works just as
you know it from react. - [Maximilian] We're making good progress. We're now able to add
items to our list here, and we're managing this
list in an efficient way. Now, what we're not able to do yet though, is remove items, and it would be nice if you
could simply tap those items and they would be deleted. Now to make them tappable,
if this would be a web app, all you have to do would be
that you add an "onClick" prop to the item. That's how you would do it in the web. But this is not how it
works in React Native. Instead here, if you wanna
make an item pressable, you have to let React Native know. And you let React Native
know by wrapping the item that should be pressable
into a specific component provided by React Native. And that would be the
"Pressable" component. React native also knows a
component named "Touchable" and various related components
like "TouchableHighlight," and "TouchableNativeFeedback," and "TouchableOpacity," but those are components that are not deprecated, but
that should be replaced by "Pressable" now. So these "Touchable" components are the old React Native way. "Pressable" is the way forward. So we use "Pressable" by
simply wrapping the item that should be pressable with it. Like This. In my "GoalItem," I wrap my view with "Pressable," and with that, this item becomes pressable. And whenever we press any
item inside of "Pressable," so in this case, this view with this text, this "onPress" prop will
trigger the function that we provide to it. So we should provide a
function to "onPress" and since I plan on deleting
items in the future, I will define this function in App.js, because here, I'm managing
my "courseGoals" state, and here, I will have to
delete items in the future. So I will add a "deleteGoalHandler"
function here in App.js. And for the moment, I'll just
"console.log('DELETE')" here in this function. We will of course replace
this with proper code later. Now, "deleteGoalHandler"
should be provided to the goal item, In the infer, I'll add a prop here, and since it's my component, it's my choice how this
prop should be named, and I'll name it "onDeleteItem" and pass my "deleteGoalHanlder"
function to that. A pointer to this
"deleteGoalHandler" function is passed as a value to "onDeleteItem" on my "GoalItem" component. And now this "onDeleteItem"
prop can be used in that component. And here, I can for example, simply forward it. Like that. We might tweak it later
but for the moment, we can just do it like this. Now, if I save this, If I tap on one of these items,
I get no visual feedback, but if I go back, you see
the that "DELETE" was locked to the terminal. So indeed, this already works. 'Course, some visual
feedback would be nice and we will add it in a second, but for the moment, it's
great that this works. - [Maximilian] As a next step, we can now make sure that an
item actually does get deleted. And for this, in the app component, in the deleteGoalHandler, it would be convenient if
in this handler function, we would receive the ID
of the to be deleted item. Because that allows us
to uniquely identify and remove an item. So here in this function, we could then set our course goals again, to update this course goals state. And now the new state should
be based on the old state where we wanna take the old
state, but remove a item, and therefore we pass a function to this state updating function. 'Cause that's how we should update state in React if it's based
on the previous state. So here, I again, get
my current course goals as I did here, when we added a goal and I return the updated state. And the updated state which I
return is current course goals which is in Array, dot filter. Filter is a built-in method in JavaScript, just as map was built-in method, which we can call on in Array. And filter will return a new
Array, which is the old Array minus all the items we filtered out. Now filter itself takes a function, which has to return true or false and if this inner function
returns true, an item is kept, if it returns false, our item is dropped. Now this inner function
is executed by JavaScript for every item in this
array and therefore, it receives these items
as inputs, as parameters. In my case, these individual goals. And I wanna return false,
if my goal has this ID here. So I will check if the goal ID, so this ID property on my goal object is not equal to the ID I'm receiving here. This will return true
if there is no match, which is good because I want to keep items where there is no match,
but if there is a match, if a item has the idea
I'm looking to remove, this will return false
because then the IDs are equal and then this item will be
dropped and the new array will no longer contain it. That's how we can delete a course goal. Of course, we have one problem though, the ID which we expect to get here is currently not provided. In goal item, where I'm binding on the lead item to onpress,
onpress won't give me that ID. Now to make sure that idea is provided, we could, again, define a
little helper function here as we did it in goal input,
where we added addGoalHandler, where we then manually called the function and passed our own parameter and that would be absolutely fine. But alternatively, we can also
call the built in bind method on the function which we get through that onDeleteItem prop. Bind is a standard JavaScript function which basically allows you
pre-configure a function for future execution. Now, the first value you pass to bind sets the this keyword in
the to be executed function and we don't care about that here, so I'll just set it to
this, but it doesn't matter. The second value, the second
argument you pass to bind will be the first parameter received by the to be called function. And in my case, that should
be the ID of the goal item that should be removed. So here I'll set this to props.id, so that the ID which I get through props on this goal item component
is sent as a parameter value, queue the function that we receive on this onDeleteItem prop which is this delete
goal handler function. Of course, for this to
work, we must make sure that we do get an ID prop on
the goal item component though. And currently that's not the case but we can update our code here and make sure at an ID
prop is set on goal item and that would be itemdata.item.id. Now there would be other
ways of solving this as well, but this will work and if we save this now and we tap a item, it is removed. So now I can add items, multiple items and I can then tap them to remove them. By the way, you could also add validation to make sure that empty
items can't be added but here I will not do that to
keep this app a bit simpler. But with that, we can remove items. Now what's missing
though, is some styling, some visual feedback. At the moment, when I tap this item I get no visual feedback
other than its disappearance. So let's focus on the styling next. - So let's now work on the styling of this pressable component
or of this component if the item is currently pressed because at the moment we
got no visual feedback. Now, adding some nice styling here is fairly easy for Android because there is a dedicated
android_ripple prop which you you can set to an object where you can set the
color of the ripple effect. And for example here, I could set this color to some dark gray by adding six Ds here, this hex code. And if I do that, if I set this value for the android_ripple
prop here on Android, if I add a goal and I press this, there is a slight ripple effect. Let me show this again. Here, if I then press this. You see? There is this ripple
effect around this item. Now, if you wanna have the ripple effect inside of that item instead, what we have to do is move
pressable into our view here like this so that it
only surrounds the text instead of the view box as well. If we do that and we reload all apps to make sure these changes get applied. Now, if we do this again on Android here, you will see there is a slight
ripple effect on the item. Now, that ripple effect
is now only on the text. Not on the overall container. And to fix this, what we
can do is move our padding from the goal item which is the
view, which is the container to the text because then the
padding is part of the text and the ripple effect
will take this padding into account as well. So now with that, if we set this up we got this ripple effect here. And of course, you can play
around with the ripple color to get the look you want. You could, for example, also
take this purple color here. And maybe choose a very
dark purple as a color, and now we have this, which
maybe looks a bit nicer. So, this is pretty easy on Android. Now on iOS, this doesn't
have any effect though. Here, if I press this and keep it pressed so that we could see a potential effect, we don't see any effect at all. Now for iOS to add some styling, we can add the style prop to pressable which generally works
like all your style props. It takes such a styling object but it also takes a
function as an alternative. And this function will be called
automatically by pressable whenever the press state changes. You will get a argument here, a parameter, with information about
the current press state and you can use object
destructuring to get hold of the pressed property that's
part of this object you get. You could also name the
overall parameter pressData and access pressData.pressed
in that function. It's up to you. Here, I will use object destructuring to directly get that pressed property. So, that's provided by pressable. And now we can return
different style objects based on the pressed state. We can define them down here of course. We could have our pressed item here. And there, we could say that we wanna set the
opacity to 0.5, for example, to make it slightly transparent. With that up here, we can check pressed and if it's true, we
return styles.pressedItem. Otherwise, we return no special styling. If we save this and I now do learn react native again, you'll notice that if I press this we also get some feedback here on iOS. Now of course, you could go
for all kinds of feedback. You could also bring this view back into the pressable component here and add another surrounding view so that you can influence
the style in greater detail, and you can, for example, also
change the background color. Whatever you want. Here, I'll keep this simple feedback but now we also do have
some feedback with help of this function form of
this style property here. - [Maximilian Schwarzmüller] So again. We made good progress here. To conclude this section. I wanna improve the overall look and feel of this application
a little bit though. And I wanna start by actually
outsourcing this input part into a modal, modals are these overlays. That typically slide up or pop up, on mobile apps that
overlay the main screen. And allow you to take some action, after which they disappear. Adding such a modal, and
adding the input logic here into such a model, is thankfully pretty easy in React Native. Because it comes with a
built in modal component. So, all you have to do in
the goal input component, where I want to create that modal. Is, you import modal from React Native. And then you wrap that, around the content that
should go into the modal. In my case here, that's the view with the text input and the button. If you do that, we have this look here. So our list of goals seems to be gone and the styling is a bit off. And as I add goals, I don't see them. The reason for that is, that we actually see the modal here. The overlay, which we
have on the entire screen. Now, of course the modal
shouldn't always be there though. Instead it would be nice to have a button on the start screen, that allows us to open the modal when we want to add a new goal. And then to have a
button that allows us to close this modal here
on this modal screen. Now let's start with that button that opens the modal. For this, I'll go back to App.js, and I want to add that button there. We still have the goal input component. But above that component,
I wanna add a button. And for this I'll use the
good old built in button. Though, of course we could
also import "pressable". And build our own button, as you learned it a couple of minutes ago. Because the button here
in the end, all it just uses is the "pressable" component
or those old "touchable" components internally. But here, to save some time, I'll use the prebuilt button
instead of building my own one. And I'll give this button a
title off, "add new goal". I'll also give the button a
color, through the color prop. Because "button" doesn't
take a style prop. It doesn't support it. Because it is already pre-styled. That's why you can't
influence it too much. If you do want to style
your own button, you have to build your own button with
the "pressable" component. And then some text and views inside of it. Here, with the pre-built
button, we can change the color through the color prop though. And I will set color here to, actually to a string. And I will use the same purple, as I used here in goal item. For the background of my goals. I'll use this purple color here. And set this as a color
of this button here. Like this. So now we have the button. But of course, we don't see the button if we save this. Because we still have
the modal everywhere. Now this button should
control the modal visibility. And for this we need "state", because the modal should
either be visible or not. And that's a typical
case for using "state". So, here in the app component, we can add a new piece of "state". And use, "use state" to
initialize it and set it to false initially, because that will be my
"modal is visible" state And the "set modal is
visible" updating function, will allow me to change it. And initially the modal
should not be visible. Now we wanna change this
whenever the button is clicked. - So for this, I'll add a "function" here, maybe here, the exact
position doesn't matter. Which I will name "start at goal handler". And in this function I want to set, "set modal is visible" to true. Because I wanna show the modal. Now, this "start at
goal handler" function, should now be connected to this button. And this button, being a built in component
has this on press prop - Which we now use to connect
it to "start at goal handler". Now to show the modal only
when this state is true. We could now render goal
input conditionally, as we would do a it in
most React Web Apps. We can add curly braces around that. And then check our
"modal is visible" state. And if it's true, then we render goal input. Which internally contains this modal. If we do It like this
and we save everything. Indeed we see the button here. And if I press the button, the modal appears But the model then is just there. There is no animation and nothing. And therefore that's typically not, how you would do that. Instead of just showing or
hiding this component like this. A better approach is to embrace
some of the props, "modal". This built-in modal
component, offers to you. To be precise, this "modal"
component has a "visible" prop. And if set to "true",
the model is visible, If set to "false", it's not. Now, this is useful. Because in addition, modal
also has a animation type prop. Where you can set how it will be animated. That there maybe is no animation at all. That it should fade. Or that it should slide in. And I will go for sliding. And now I just expect to get the, "should it be displayed or
not" information, via props. So here I'll access "props.visible", though this prop name here is up to you. It could also be "show modal". But I'll choose "visible". And now we have to set
this prop on goal input, in the app component. So here, I'll now add this "visible" prop. And set this equal to "modal is visible", which is true or false. And with that, if we now saved this. And we force a reload, by pressing R here in the terminal. Now the modal slides up. Of course we can't
dismiss it at the moment. And the styling in
there also is a bit off. So, therefore let's next
focus on that styling. And let's then make sure that, we can also close this modal. - [Maximilian] So to
work on the styling here. In the end we need to go
into the GoalInput component. There, we have this model and in the model we have this View. Which in the end is
responsible for controlling how the textInput and button are displayed. Now I wanna change the overall look. I now wanna have the button
beneath the textInput and I wanna have another button in addition to add goal. Because that other button should allow us to close that model. Now to achieve this I will
restructure this a bit. And I will actually add
another View in here, below the textInput, and move
my button into this View. And add another button here with a title of Cancel. So that I can add the
goal or cancel adding. And this should in the
future close my model. at the moment it won't do anything though. Now with that, we got that View in a View. But now I wanna change the
styles of that outer View, which is the main View inside the model. Because it's the first
element in the model. The top most element in the model. And on this input container,
I wanna use flex 1. So that it takes all the
available space in the model. But I wanna change the
flexDirection back to column. And therefore we can also remove it because that is the default anyways. With this, if I save this, the buttons are now below the textInput. But they are totally stretched out, because we have space
between for justifyContent. This should instead be center to make sure all the content is centered. This now looks way better. Now in addition these two buttons should be next to each other and therefore I added this
View around the buttons. and I will give this View some styling. So here, maybe at the bottom, I'll add my buttonContainer object. And actually give this
a flexDirection of row. So that the two buttons
are next to each other. And then add this
buttonContainer on this View. So here I'll add a style which is styles.buttonContainer. And with that, the buttons
do sit next to each other. Now it would be nice if the buttons had maybe a fixed width and
some spacing between them. And to achieve this, since
I can't style these buttons, since they're built in and since I don't wanna
build my own button which we could do but
which is some extra work. I'll actually wrap each
button into a View as well. Because then I can style that
View instead of the button. And therefore still influence
the look of the button a bit. So here I'll assign a style
property to those Views here. And setup a dedicated styling
object for those buttons. I'll name style object button. And for every button
here I'll just make sure that it takes, lets say,
40% of the available width. And then we might want to
add a marginHorizontal, So margin left and right, of eight pixels. So that we have some
spacing between the buttons. And it's now this button style here which I wanna add to the
Views that hold the buttons. And that's a quick and easy way of setting my own styles to the buttons even though I can't change
the button style itself. But you can change the
width or height with that, you can add margins between them. You wouldn't be able to change the color or text color though,
if that was your plan you would have to build your own button. And in general, if you
need more adjustments, you might wanna build your own button by using the pressable component. But this is good enough for now and it gives me these equal sized buttons below the textInput. I also wanna have some spacing between the textInputs and the buttons. And maybe we can shrink them a little bit. So to shrink them I'll
change the width to 30% here or we set this to a fixed
pixel width of 100 for example, like this. And for the spacing between
the textInput and the buttons. On the buttonContainer
I will add a marginTop of let's say, 8 or maybe 16. So now I save this there is some distance between the buttons and the
textInput above the buttons. The textInput now also can become wider. So I will set the width
of the textInput to 100%. And with that, I reloaded
the app in between so I will reopen this, this is now wider. Now it would still be
nice to have some area around that which is not occupied. And therefore on the overall container, Which is that root View in that model, I will add a little of
padding in all directions and let's say add 16 pixels of padding. And now I'd say this doesn't look too bad. Last but not least we still have some extra
spacing to the right of the textInput, because we had a margin to right and I wanna get rid of that as well. So now this does look quite good. And of course you can
adjust this a bit further if you want to. Now I wanna make sure the model closes whenever we cancel or
click add goal though. Because at the moment
that's not happening. - [Maximilian] So to close the modal, whenever we click add goal or cancel, we have to react to taps on those buttons. And then in the end, we have to change the visibility
state in the app component because modal is visible
in the end controls whether the modal is shown or not. Now we already got a 'StartAddGoalHandler' which opens the modal. Now we can add a 'EndAddGoalHandler' which should set
'ModalIsVisible' to false. And now we just need to
make sure this function gets called whenever a user taps on the 'Add Goal' or 'Cancel' buttons. Now, if a user taps on 'Add Goal', the 'AddGoalHandler' is triggered which triggers the function
received on the 'onAddGoal' prop which turns out to be this function. So in order to close the
modal if a goal was added, we just have to set 'ModalIsVisible'
to false here as well or call the 'endAddGoalHandler'
manually here. Both would work. Now to also close it, If we tap the cancel button, in goal input we have to add 'onPress' to this button as well, And then expect that we get a pointer to this 'endAddGoalHandler' passed into 'GoalInput' through props. So here we could expect that we get a function on a cancel prop. It's our component, so the
prop name is up to you. Now we just have to make sure
that this prop is provided in app component. Here we pass the 'onCancel' prop to the 'GoalInput' component and point at the end,
'addGoalHandler' function, so that this function is executed whenever the cancel button
is clicked, or pressed. With that, if we save this. Indeed, if I click
cancel the modal closes. And if I add a goal here, like this, and I click add goal, it also closes, but the goal is there. And of course it also works on Android. Both adding and removing,
and opening and closing. - [Maximilian] Now to
finally finish this app here. I wanna improve the overall look and feel, by changing some colors
and by adding an image. Which is something we haven't done yet. But which is of course,
something you will often do. And you should therefore know how to do. Now, adding an image, thankfully is easy. Because React Native
has a component for it. There is an "image" component, which you can import from React Native. And just as you have, the "image" element for the web, in HTML. This is a component that helps
with displaying an image. This "image" component
is used in your JSX Code. And here I wanna display an image, right above the text input. So, I will add my image here. On this image, you can
then add some style. Which we'll do in a second. But most importantly, you
can also add a "source" prop. And this should point at
the image you want to add. Now, when it comes to that image, you do find an example image attached. And you should download that image and move it into your "assets" folder. Or to be precise create an "images" folder in that "assets" folder. Because that "assets" folder is generally, the folder where static assets
like images should be stored. And then move that attached image, into that "images" folder
in the "assets" folder. And it's this "goal image",
which you got attached. And now when it comes to
linking to that image. It works differently than what
you're used to from the web. We won't create some link
here, which points to "assets/images/goal.png" at least not quite. It's not too wrong, but it
wouldn't work like this. If I save this, you see, I get a warning. I get an error, this doesn't work. Instead, you have to import the image here with a special import syntax. You should use a "require" function. Which you might know from Node.js, if you worked with that. And to that "require" function, you pass the path to the image. However, the path is a
relative path from the file in which you're using
this "require" function. So, seen relatively from
that goal input file, which is that file, where this "required" function gets used. We have to go up one level, to leave the "components" folder. And then dive into images
and select the image. Now to go up one level we type ../, this means go to the parent folder. And then we dive into
assets, images, goal.png. This is how we add an image. Now let's also add some styling to it. And for this, I'll add
an "image styling" object in my style sheet here. And here we could set a width
of let's say 100 pixels. And a height of 100 pixels. And give this a margin
of 20 in all directions, to have some spacing around the image. As always, you can play around with the styling, to find your favorite styles. Now I wanna apply that style to my image. And thankfully the "image" element, the "image" Component, does
support the style prop. And we can set "styles.image", as a value on that style prop. And with that, if we reload
the app by pressing R here in the terminal. You will see nothing. As it seems. The reason for that is
that the image is white. And we can't see that, because the background is also white. That's no problem because, I wanted to changed
the background as well. And since we're in the modal here. This background can be changed. By setting a different background
on this input container. Because that is our root
element in the modal. And it defines the overall
modal container look therefore. The modal itself does
support the style prop. But for styling the
overall modal background, you should use a nested view,
as we are doing it here. And now here on this input container. We can add a background color. And I prepared a nice dark purple, which I'd like to use here. Which has to hex code 311b6b. And whilst we're here, I also wanna change some other styles. For example, we don't need the border at the bottom anymore. Because there is nothing below that. And that border here looks really strange. So I will remove that border. And I will also remove
that margin to the bottom which I have here. So that these are the remaining
styles that I have here. And with this, now we can see the image. Because now we got this
nice purple background. Only in the modal though. We will work on the
other background later. But here in the model, we got that. Of course, we now also might want to, tweak the text color here. When we enter something
into this input field. And we might wanna tweak
the buttons here as well. To make those look a bit prettier as well. Because currently they
don't fit the background. But most importantly, we
did now add the image. Which was the main task for this lecture. And as you saw, adding
an image is super simple. With that "image" Component and
this way of adding a source. So let's next improve the overall styling of those modal elements. The other elements, I mean. - [Tutor] So for improving the
overall look and feel here, I wanna start by changing
the button colors. For this, we can go to our JSX code because you learned that
for these built in buttons, you change the colors with the color prop. And again, I prepared some hex codes, which I think look quite nice. And for adding an item here, I wanna use the code 5E0acc. And for canceling, I
prepared another code, which is F31282. If we save this, this is
how the buttons look like. And it looks good on Android, but on iOS, it's a bit hard to read I would say. This is a bit hard to read. So actually, we might wanna tweak this and pick a slightly lighter purple, like B180f0, which I think looks good on iOS as well. It doesn't look as good on Android, but for the moment I don't wanna dive into writing different code
for different platforms. We will do that later in the course, but for now let's stick to
this uniform solution here, where we have the same
code for both platforms. Of course, feel free to optimize the app for one of the two platforms. For example, if you're on
windows and you can't test this on iOS anyways, you can of
course choose a prettier color for this button there. But we got these buttons now, let's now work on the text input styling. And for this, we got this text
input styling object here. In there, I wanna to change
the border color a little bit and choose E4d0ff. And also give this a background color which should be the same color. So I'll just copy that as a
background for the text input. I will now also change the
color of the text that's entered to 120438, which is another prepared hex code. Since I added a border, I also wanna change the border radius, so that I have some rounded
corners with border radius and set this to six pixels. And with that, that is
how this input looks like. And I think that doesn't look too bad. Last but not least, I
will ramp up the padding a little bit though, and change it to 16, so that we got this padding here. So that's now how this model looks like. And I would say it doesn't look too bad. We can now learn React Native
and this app looks quite nice. I just wanna switch to button order because I ended a bit more intuitive if the add button is on the right, but of course, this comes
down to personal preference and is totally up to you. But now, this works. Last but not least, I also wanna improve the styling
of this main screen though. Because I think that
white background color isn't too pretty. Here, I prepared a little color for us. And it has the hex code 1e085a. Now, if we set it like this, this works and we got this as a background color. And this works just fine here. But if we had multiple
screens in this app, so not just the model,
but also other screens, which we will have later
once we add navigation, then manually setting the
background color like this all the time, can become annoying. Therefore what you can do when using expo, is you can go to the app.json file. And in that file, you
can actually also add a special background
color configuration item and set this to a hex code. And this will then
automatically be applied as a background color
to all screens by expo. So if I now reload after
adding this to app.json and saving that file, we also
got this background color, but now it's applied by expo
to all the main screens, not to the model, but
to all the main screens that are not overlays. Here, we only have one
screen, so it didn't matter. But this can be the
more convenient approach if you do have multiple screens. Of course, like this,
I'm not totally happy with the way this add
new goal button looks. I think it's a bit hard to see on iOS. Therefore, what I will do is in App.js, I will change the color of this button. And we could go for a
lighter purple like this. And I think that looks a bit nicer. So now with the styling changed, there's one thing that
might become obvious. The status bar is really difficult to see on both these devices. The reason for that is
that it's still black, that it doesn't know that
we have a dark background. Now, thankfully, expo gives
us a solution for that. We can import a special
component from expo. And that component is the status bar. We can import it from expo-status-bar, which is a standalone package which is already part of this project. This package here. This is a component which
we can use to fine tune the look of the status bar. We simply use this status
bar as a component, here in our JSX code, in
this root app component. So here we can add StatusBar. And since it's now next to
another sibling component, right at the top of
the return to JSX code, I will wrap it with such a fragment, since you're not allowed
to have sibling elements at the root level of JSX code otherwise. So we add this wrapping fragment and add the StatusBar component next to our root view component. And on this StatusBar component, you can now set a style prop,
which one's a string though? And you can now set the
status bar to dark or light or let it infer it automatically. Now, auto doesn't work here. So I will set it to light. And if I do that, the
status bar becomes white and is now way easier to see again. And that is definitely a
tweak we also wanna apply so that we do have an app that looks good and that still allows the user to see crucial phone information
like the battery or the time. - [Maximilian] So, that's
it for this course section. We learned a lot about React Native. We built the first basic application here, where we can add our course goals, and whilst building this application, we learned a lot about
building user interfaces with React Native, in general. You learned that you must use these built-in core components, for example. You are not allowed to use HTML elements like div's or anything like that. Instead, you build the
entire user interface and all your custom components from those built-in components. But then you can combine them as you need to, to build your
own elements and features. You also learned how styling works, that you create such style sheet objects which hold nested objects, which you can then assign to different elements in your JSX code. In those nested objects, we
have CSS-like properties, which allow us to set
margins, paddings, colors, and all these things. Now even though it looks a bit like CSS, or the names are similar, it isn't CSS. We have no inheritance and in general, it's just not CSS, it's JavaScript and under the hood,
React Native translates our style settings to the
Native style instructions for the different platforms. We, for example, saw that we had problems assigning rounded corners
to these elements on iOS, whereas it worked
without issues on Android and therefore, its really important to keep in mind that we
have two different platforms and React Native just does its best to talk to both platforms and transform our instructions for them. Now, what we also of course learned, is that besides the
components and the styling, we write regular React applications. We can still use state,
we can react to events, those events are just not called onclick, but instead onpress. But then we can react to
them, we can change state, we can output state and
we can do all these things and this part, works
exactly as you're used to. There's nothing special about this. React Native Plus React is
exactly as React Web Plus React when it comes to managing state and so on. Now, we saw differences when it came to outputting lists though. We did this with a scroll view, but then we switched to flat
list for better performance. Which is another built-in core component. And you will see cases
like this from time-to-time throughout the course, where
we have a special solution for the Native platforms
for better performance or a Native look and feel. But that's it for the basics for now. In the end it's not too complex, it's a React app, just with
a different target platform, but of course it is something
you must get used to. Working with those built-in components, setting up styling like this. It takes practice to get used to that. And thankfully we'll
have plenty of practice throughout this course, because this was just
the first real section and just the first app. We're going to build way more and we're going to learn way more throughout the rest of this course. I hope you enjoyed this "Getting Started" guide
which got you started with React Native, with the project setup,
and with building your first app. If you liked this,
and if you would like to learn more, for example, how to use native device
features, how to send push notifications, how to add multiple page
navigation to your app and much more - then my complete course on React
Native might be interesting to you! And you'll find a link to the course, with a nice discount,
directly below the video. And I'd of course love to welcome
you on board of this course and continue this journey
into React Native together with you.