0% found this document useful (0 votes)
31 views126 pages

React Basics

This document provides an overview of core concepts in React Native, focusing on building a Goal Tracker App. It covers essential components, styling methods, and state management techniques, emphasizing the differences between React Native and web development. The section also includes practical steps for setting up a project using Expo and highlights the importance of functional components and hooks.

Uploaded by

Rakesh kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
31 views126 pages

React Basics

This document provides an overview of core concepts in React Native, focusing on building a Goal Tracker App. It covers essential components, styling methods, and state management techniques, emphasizing the differences between React Native and web development. The section also includes practical steps for setting up a project using Expo and highlights the importance of functional components and hooks.

Uploaded by

Rakesh kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 126

🚀 Diving Into React Native – Core Concepts

📌 Section Objective:

Understand and apply the fundamentals of React Native by building a small Goal Tracker App.

🧱 What You Will Learn in This Section:

1. ✅ Core React Native Components


2. 🎨 Styling in React Native
3. 🎮 Adding Interactivity and Managing State

All these are essential building blocks for every React Native app.

🧩 1. React Native Core Components


React Native apps are built using pre-defined building blocks (just like HTML elements in web
development, e.g., <div>, <p>, etc.).

Key components include:

Component Purpose
View Like a <div> in HTML — used for layout & grouping
Text To display text
TextInput To accept user input
Button A clickable button
FlatList Efficiently render scrollable lists
ScrollView Scrollable container for child components
Modal To show overlay content like popups

These components use JavaScript + JSX syntax and are rendered into native UI elements under the
hood — that’s what makes React Native powerful!
🎨 2. Styling React Native Apps
Unlike web development, React Native does not use CSS files. Instead, it uses a built-in object-based
styling system.

🔑 Key Styling Concepts:

 Styling is done using StyleSheet.create({ ... })


 Styles are applied via style prop like:
 <View style={styles.container}>

 Values (like padding, margin, fontSize) are not unit-based (px is not used). Just use
numbers.
 const styles = StyleSheet.create({
 container: {
 padding: 20,
 backgroundColor: '#f0f0f0'
 }
 });

🧠 Why This Is Important:

React Native’s styling engine is optimized for mobile apps and mimics CSS concepts (like Flexbox),
but with a JavaScript-based API.

🎮 3. Interactivity + State Management


Adding interactivity means:

 Responding to user input (e.g., button presses)


 Dynamically updating UI based on data or user actions

This is achieved using React's useState() hook — a key React concept that works in React Native
too.

🧠 State Example:
const [goals, setGoals] = useState([]);

function addGoalHandler(newGoalText) {
setGoals((currentGoals) => [...currentGoals, newGoalText]);
}

When a user clicks a button, you update the state → this re-renders the UI with updated data.
✨ Interactivity Elements:

 TouchableOpacity or Pressable: Used to handle taps on custom components (instead of


default <Button>)
 onPress prop: Used to define what should happen on a tap

🧪 Demo App – Goal Tracker


To apply all the concepts above, you'll build a simple but powerful practice app.

📱 What This App Does:

 Show a list of goals


 Open a modal input form to add new goals
 Add a goal to the list
 Tap a goal to remove it from the list

🔍 Why This App Is Useful:

 Covers multiple UI elements


 Involves state management, user input, and rendering lists
 Teaches modal usage
 Helps you learn how to build a real interactive layout

Though this app is simple, it mirrors the fundamentals you'll use in any real-world app:

 User input
 Updating UI based on changes
 Component re-rendering
 List rendering and manipulation
 Styling and layout control

🔄 Flow of the App:


1. User clicks "Add Goal" → Opens a modal
2. User types in the goal → Submits the form
3. Goal gets added to the list → React state updates
4. Each goal is rendered using a dynamic list
5. Clicking a goal → Removes it from the state list
📘 Summary of Key Learning Goals:
Concept Details
Core Components Learn & practice with foundational building blocks
Styling Learn React Native’s unique object-style system
State Use useState() for dynamic interactivity
Modal Handling Use native modal overlays
Lists Render lists using FlatList or ScrollView
Interactivity Use Button, Pressable, and event handlers (onPress)

🔧 Tools Used
 Expo CLI for development and testing
 VS Code (recommended editor)
 Expo Go App (to preview on your device)

✅ What You Should Do:


 Code along with the course videos
 After each video, experiment:
o Change button labels
o Modify the UI
o Try different styles or layouts
o Add validation to the input
 Try making your own version of the app after completing the instructor’s version

🧠 Section Overview:
This section covers:

1. ✅ Project setup using Expo


2. ✅ Functional components and React Hooks
3. ✅ Core React Native components (View, Text, etc.)
4. ✅ Styling in React Native
5. ✅ How the app bootstraps and renders
🔧 1. Project Setup Using Expo

🛠 Key Steps:

 Open terminal/command prompt: This can be the built-in terminal in VS Code or any other
terminal app.
 Install dependencies:
 npm install

This reads package.json and installs all required packages.

 Start development server:


 npm start

This launches the Expo dev server, which watches your code and hot-reloads changes.

▶️Run on emulator:

 Press:
o for Android emulator
a
o for iOS simulator (macOS only)
i
 This will open your app in your preview devices.

Why Expo?
Expo is a development toolchain that simplifies running and building React Native apps. It handles a
lot of the native code and configuration for you, especially in the early stages.

🧱 2. Functional Components & Hooks (No Classes)


React Native supports two types of components:

 Class-based components (older style)


 ✅ Functional components (modern standard)

In this course:

 You will only use functional components


 These components use React Hooks like useState and useEffect for state and lifecycle
management
function App() {
return (
<View>
<Text>Hello React Native!</Text>
</View>
);
}

🧠 Why Hooks?
Hooks simplify logic reuse and improve readability. They also match the future direction of React and
React Native.

📦 3. Core Components in React Native


Unlike web React, you don’t use HTML tags like <div>, <h1>, etc.

Instead, you use React Native’s built-in components, such as:

Component Purpose
View Container (like <div>)
Text Render text (like <p> or <span>)
Button Basic button UI
TextInput User input field
Image To show images
ScrollView Makes content scrollable
FlatList Efficient way to render long lists
Modal Display overlay UI like popups
import { View, Text, StyleSheet } from 'react-native';

🧠 Native devices don’t have HTML or a DOM. React Native translates these components to real
Android/iOS UI widgets.
🖼 4. Styling in React Native
React Native does not use CSS files. Instead, you style with JavaScript objects using
StyleSheet.create.

🔑 Syntax Example:
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: '#fff'
},
text: {
fontSize: 18,
color: 'blue'
}
});

<View style={styles.container}>
<Text style={styles.text}>Welcome!</Text>
</View>

✅ StyleSheet vs Inline Styles:

 StyleSheet.create is better for performance because styles get optimized


 Inline styles are allowed but not preferred for complex styling
📘 Key Notes:

 Units like px, em are not used. Numbers are treated as density-independent pixels (dp).
 Styling uses a subset of CSS (mainly Flexbox for layout).

🌐 5. React Native vs React (Web)


Concept React Web React Native
Markup HTML (<div>, <h1>) Native Components (<View>, <Text>)
Styling CSS (separate files) JavaScript object styles
Rendering In browser DOM On native mobile widgets (iOS/Android)

Even though the syntax is similar, React Native renders to native UIs, not to a browser DOM.

🌲 6. App Component & Entry Point


In every React Native app, the file App.js contains the root component.

export default function App() {


return (
<View>
<Text>Hello World</Text>
</View>
);
}

🚀 How the App Launches:

 Expo takes the component exported from App.js


 This component becomes the root of your entire app
 All other components must be rendered inside this App component

🧩 7. Composing Components
Just like React web apps:

 You build the UI by composing (nesting) components


 Combine smaller reusable pieces to build complex UIs

🛠 Example:
function GoalItem(props) {
return <Text>{props.text}</Text>;
}

function App() {
return (
<View>
<GoalItem text="Learn React Native" />
</View>
);
}

🔁 8. Recap - Core Concepts


Concept Description
Expo Toolchain for quick setup and live previews
Functional Components Stateless, hook-based modern React components
React Hooks Like useState, useEffect for logic & state
Core Components View, Text, Button, etc. that render native UI
No HTML/CSS Use React Native components + JS-based styles
App Component Root entry point for your entire app UI
🧠 Pro Tips for Mastery
 Read the official docs for components: https://reactnative.dev/docs/components
 Focus on understanding:
o How layout works with Flexbox
o How state changes trigger UI re-renders
o How styles are scoped and managed via StyleSheet
 Practice by building mini-apps like:
o A todo list
o A calculator
o A login form with validation

Code snippet
Cmd: npx create-expo-app --template blank 00-starting-project
App.js:
--
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {


return (
<View style={styles.container}>
<Text>Hello World!!!!</Text>
<StatusBar style="auto" />
</View>
);
}

const styles = StyleSheet.create({


container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
🧱 Core Components Deep Dive: Text, View, and Button

📌 What Are Core Components?

React Native comes with a limited set of core components that replace HTML elements. Every UI
you build will be composed using these. They are the "building blocks" of native UIs.

🪛 1. View and Text: Roles & Rules

🧠 Key Rule:

In React Native, plain strings can't be rendered directly — they must be wrapped inside a <Text>
component.

❌ Invalid:
<View>
Hello World
</View>

🔴 This throws the error:

Text strings must be rendered within a <Text> component.

✅ Valid:
<View>
<Text>Hello World</Text>
</View>

✅ Why This Rule Exists:

Unlike web development, native platforms require strict separation of text and containers.

 View is like a <div> — a layout container, not meant to render text.


 Text is specifically designed to render text content with proper formatting and behavior
across iOS and Android.

Think of React Native as more structured than the web. You must use the correct component for
the correct job.
🏗️2. Nesting & Structuring UI

🧱 Component Nesting:

You’ll often nest components like this:

<View>
<Text>Title</Text>
<Text>Subtitle</Text>
</View>

This is like:

 Grouping related UI in a section


 Using View as a layout wrapper (like a div or section)

🧠 Rule of Thumb:
Component Purpose
View Layout container for grouping other components
Text For displaying any readable text
Image For showing pictures
TextInput For getting input from users
Button For user interactions (taps)

The hierarchy and structure you build with View and Text is essential to layout design and
interaction flow.

🔄 3. Nesting View Inside View


<View>
<Text>Main text</Text>
<View>
<Text>Nested Text</Text>
</View>
</View>

✅ This is completely valid. Just like nesting <div>s or <section>s in HTML, you can nest Views in
React Native to create layout groups, sections, cards, etc.
🖼️4. More Core Components: The React Native Toolbox
React Native gives you only a few components, but they are powerful and composable.

Most Used:
Component Role
Text Display strings
View Group/position components
TextInput Form input fields
Button Interactivity
Image Show pictures
ScrollView Make a screen scrollable
FlatList Display long lists efficiently
TouchableOpacity, Pressable Tappable areas with custom feedback

🔘 5. Using the Button Component

🔗 Importing:
import { Button } from 'react-native';

Unlike web React (where <button> is globally available), React Native components must be
explicitly imported.

🧠 Native apps don’t have HTML tags — React Native provides platform-appropriate APIs instead.

✅ Syntax:
<Button title="Click Me" />

 title is a required prop – this is the label shown on the button.


 It is a self-closing component. You don’t place text inside it like in HTML.

❌ Invalid:
<Button>Click Me</Button>

✅ Cross-Platform Styling:

React Native renders the Button using platform-specific UI:

 On iOS → iOS-looking button


 On Android → Android-style button

This means your UI feels native on both platforms out of the box.

You don’t have to manually detect the OS to change button styles — React Native handles it.

⚠️6. Behavior, Not Just Appearance


While the Button looks right, it doesn't do anything until you add behavior.

To make it interactive:

<Button
title="Click Me"
onPress={() => {
console.log('Button was pressed!');
}}
/>

✅ The onPress prop:

 This is the equivalent of onClick in web.


 It is a callback function triggered when the button is tapped.

📦 7. Final Thoughts: Component-Based UI Architecture

🧠 Key Principle:

You build React Native apps by combining and nesting core components to make more complex,
reusable components.

Example:
function WelcomeScreen() {
return (
<View style={styles.screen}>
<Text style={styles.title}>Welcome to the App</Text>
<Button title="Get Started" onPress={startApp} />
</View>
);
}
This is exactly how real apps are built: from simple components → structured UIs → full screens →
full app.

🧠 Key Concepts Recap


Concept Description
Text Must wrap all string content
View Layout container for grouping and styling
Component Nesting You can nest View and other components to structure the UI
Must Import Components Unlike HTML tags, React Native components must be imported
Button Must be self-closing, uses title and onPress props
Platform-Aware UI Components render differently based on Android vs iOS
No Raw Text in View Text must be inside a <Text> component, or app will crash

🚀 Coming Next: Styling


The next logical step in your learning will be:

 How to style View, Text, and Button


 How layout and positioning work (using Flexbox)
 How to reuse styles and create responsive designs

Code Snippet
EX: npx create-expo-app --template blank 01-working-with-core-components
App.js
import { StyleSheet, Text, View, Button } from 'react-native';

export default function App() {


return (
<View style={styles.container}>
<View>
<Text>Another piece of text!</Text>
</View>
<Text>Hello World!</Text>
<Button title='Tap me!' />
</View>
);
}

const styles = StyleSheet.create({


container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

🧠 1. React Native Doesn't Use CSS


React Native does not support traditional CSS. That means:

 No external .css files


 No <style> tags
 No className or CSS selectors

Instead, React Native uses JavaScript objects to define styles.

🎨 2. Two Ways to Style in React Native

✅ A. Inline Styling

You pass a JavaScript object directly to the style prop:

<Text style={{ margin: 16, padding: 8 }}>Hello</Text>

 You define the style where you use it.


 Good for small, one-off styles.
 Not reusable. Repetition becomes an issue for larger projects.

✅ B. StyleSheet Object (Best Practice)

Use the built-in StyleSheet.create() method to centralize and reuse styles:

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({


dummyText: {
margin: 16,
padding: 8,
borderWidth: 2,
borderColor: 'blue',
},
});

Then use it in components:

<Text style={styles.dummyText}>Styled Text</Text>

Benefits of StyleSheet.create():

 Better organization
 Autocomplete and IntelliSense in VS Code
 Potential for internal optimizations by React Native in the future
 Easier maintenance — change in one place affects all uses

🧱 3. Style Properties in React Native


React Native style properties are inspired by CSS, but they are:

 A subset of CSS
 Use camelCase instead of kebab-case
 Some CSS features are not available (like media queries, pseudo-selectors)

Examples:
CSS React Native
margin-left marginLeft
border ❌ (not supported)
borderWidth ✅
padding ✅

Supported Properties:

 Layout: margin, padding, flex, alignItems, justifyContent


 Borders: borderWidth, borderColor, borderRadius
 Text: fontSize, fontWeight, color, textAlign
 Others: backgroundColor, height, width
🚫 4. What Happens if You Use Unsupported CSS?
If you try something like this:

<Text style={{ border: '1px solid red' }}>Invalid</Text>

You’ll get:

 A red screen error or console warning


 The app won’t render the component correctly

Always check the React Native style documentation to confirm what’s supported.

🧪 5. Numbers vs. Strings in Style Values


React Native styles expect different value types depending on the property:

Property Value Type Example


margin number margin: 16
padding number padding: 10
color string color: 'red'
borderColor string 'red', '#ff0000'
fontWeight string 'bold'

 Numeric values are automatically converted to density-independent pixels (dp) based on the
device.
 Strings are required for things like color, fontWeight, etc.

🧭 6. Why Avoid Inline Styles for Reuse


Imagine this situation:

<Text style={{ margin: 16, padding: 8, borderWidth: 1, borderColor: 'red' }}>Text


A</Text>
<Text style={{ margin: 16, padding: 8, borderWidth: 1, borderColor: 'red' }}>Text
B</Text>

 You're repeating the same style.


 If you need to update it, you have to change in multiple places.
 Instead, create a shared style in StyleSheet:
const styles = StyleSheet.create({
textBlock: {
margin: 16,
padding: 8,
borderWidth: 1,
borderColor: 'red',
},
});

Now you use:

<Text style={styles.textBlock}>Text A</Text>


<Text style={styles.textBlock}>Text B</Text>

Change once, update everywhere.

💡 7. Structure of a StyleSheet
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
titleText: {
fontSize: 24,
fontWeight: 'bold',
color: 'blue',
marginBottom: 12,
},
});

 This keeps style and structure separated


 Easier to maintain and reason about

🔚 Conclusion — Key Takeaways


Concept Summary
Styling Method Use StyleSheet.create() over inline styles for maintainability
Value Types Use numbers for spacing (margin, padding), strings for colors and fonts
Not CSS React Native uses JS objects, not actual CSS
Autocomplete StyleSheet.create() improves developer productivity with autocomplete
Reusability Styles can (and should) be reused via object references
Platform React Native styles are applied across iOS & Android, abstracting platform-
Differences specific details
Code Snippet
EX: npx create-expo-app --template blank 02-styling-react-native-apps
App.js
--
import { StyleSheet, Text, View, Button } from 'react-native';

export default function App() {


return (
<View style={styles.container}>
<View>
<Text
style={styles.dummyText}
>
Another piece of text!
</Text>
</View>
<Text
style={styles.dummyText}
>
Hello World!
</Text>
<Button title="Tap me!" />
</View>
);
}

const styles = StyleSheet.create({


container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
dummyText: {
margin: 16,
padding: 16,
borderWidth: 2,
borderColor: 'blue',
}
});
📘 React Native Layout System: Intro to Building Real Interfaces

🔹1. What’s the Goal Here?

We’re moving beyond just showing static text and buttons. The goal is to start building real layouts
— structured, interactive user interfaces.

The Example:

We’re building a "Goal Management App" with two main sections:

1. Input Section – Where the user types a goal and submits it.
2. List Section – Where the submitted goals will be shown.

🔹2. Initial Setup

a. Clean Slate

 Removed inline styles.


 Emptied the existing StyleSheet object (except the object itself).

b. Nested Views (Structure)

Why use multiple View components?

✅ Logical separation:

 One View for goal input (TextInput + Button)


 One View for the goals list

✅ Better control for styling and layout (e.g., spacing, sizing, flex).

<View style={styles.appContainer}>
<View>
<TextInput placeholder="Your course goal" />
<Button title="Add Goal" />
</View>
<View>
<Text>List of goals</Text>
</View>
</View>
🔹3. Components Used
Component Purpose
View Basic container block, similar to <div> in web
TextInput Input field for user to type a goal
Button Triggers action to add a goal
Text Displays static text (for now, “List of goals”)

All these are core components in React Native and must be imported from react-native.

🔹4. Dealing with the Status Bar

When rendered, the app UI overlaps the status bar, making elements hard to see.

🛠️Temporary fix:
Apply a padding to the outermost View.

const styles = StyleSheet.create({


appContainer: {
padding: 50,
}
});

✅ This pushes content below the status bar, improving visibility.

🧠 Pro Tip: Later, use components like SafeAreaView or libraries like expo-status-bar for better
handling.

🔹5. Current Problems With Layout

Despite having padding, the layout looks cluttered and vertically stacked.

Issues:

 Button appears below the input instead of next to it.


 Input area takes too much space.
 No separation or spacing between sections.
 Need to control how much screen each section uses.
🔹6. The Solution: Use Flexbox for Layouts

React Native uses Flexbox by default to handle component layouts — just like in web development,
but with some differences.

✅ Why Flexbox?

 Controls alignment (horizontal and vertical)


 Distributes space
 Helps create responsive designs
 Works across screen sizes and orientations

🔍 Flexbox Primer (Before You Go Deeper)


💡 Everything inside a View behaves like a flex container by default.

Key Flexbox Properties (React Native):


Property Description
flexDirection Row ('row') or Column ('column')
justifyContent Aligns children vertically (main axis)
alignItems Aligns children horizontally (cross axis)
flex Controls how much space a child should take
gap (React Native 0.71+) Adds space between children

We’ll use this to fix:

 Button and input alignment


 Vertical space distribution
 Overall layout control

✅ Summary of Key Learnings So Far:


Concept Explanation
Nested Views Helps split the app into manageable blocks
TextInput For user input; supports placeholder like web
Button Triggers actions (will add handler later)
Status Bar Can overlap content; fixed using padding or SafeAreaView
Flexbox Core layout engine of React Native
🔜 What’s Next?
Now that we have a structured view:

 We’ll use Flexbox to align the TextInput and Button side-by-side.


 We'll define flex values to allocate vertical space proportionally (e.g., 25% to input, 75% to
list).
 Start managing state and user input for dynamic interactivity.

🔧 React Native Flexbox: The Foundation of Layouts

🔍 What is Flexbox?

Flexbox (short for "Flexible Box Layout") is a layout system used to design responsive user interfaces.
React Native implements a subset of CSS Flexbox, allowing developers to:

 Control positioning of child elements inside a parent (container)


 Allocate space across screen sizes
 Align, stretch, or space components dynamically

📌 React Native’s Flexbox is almost the same as web CSS Flexbox, but tailored for mobile UI and
written in JavaScript object syntax.

🧱 Understanding Flexbox in React Native

🔹 Flexbox Works on Containers

Flexbox doesn’t apply to every element directly. It is applied to the parent container, which then
affects how its children (components) behave.

Example:

<View style={styles.container}>
<View style={styles.box1} />
<View style={styles.box2} />
<View style={styles.box3} />
</View>
Apply flexbox styles on styles.container to control how the boxes are arranged.

🔹 Axes in Flexbox

Flexbox operates on two axes:

Axis Direction (default) Controlled by


Main Axis Vertical (column) flexDirection
Cross Axis Horizontal Dependent on main axis

In React Native, the default flexDirection is "column" (unlike the web's default, which is "row").

🔹 Core Flexbox Properties in React Native


Property Use Example
Controls how much space an element takes
flex flex: 1 means take full space
relative to siblings
flexDirection Main axis direction: 'row' or 'column' flexDirection: 'row'
justifyContent: 'space-
justifyContent Aligns children on the main axis
between'
alignItems Aligns children on the cross axis alignItems: 'center'
alignSelf Overrides alignment for a single child alignSelf: 'flex-start'

🔹 flex: 1 Explained

When we write:

flex: 1

It tells React Native:

 "Expand this component to fill all available space within its parent."
 If multiple siblings have flex values, space is divided proportionally.

Example:

flex: 2 // gets twice as much space as a sibling with flex: 1


🔹 flexDirection

This property sets how children are laid out:

Value Layout Direction


column Top → Bottom (Default in React Native)
row Left → Right

🔹 justifyContent (Main Axis Alignment)

This controls how children are spaced on the main axis.

Value Description
flex-start All at the start
flex-end All at the end
center Centered
space-between Evenly spaced, first at start, last at end
space-around Equal space around each child
space-evenly Equal space between all children, including edges

Useful when you want vertical or horizontal spacing between items.

🔹 alignItems (Cross Axis Alignment)

This aligns children on the cross axis.

Value Description
stretch (default) Stretch to fill container (if no size set)
flex-start Align to start of cross axis
flex-end Align to end
center Center on cross axis

In a column layout, this affects horizontal alignment.


🧠 Visualizing Flexbox
If you have this structure:

<View style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ width: 50, height: 50, backgroundColor: 'red' }} />
<View style={{ width: 50, height: 50, backgroundColor: 'blue' }} />
<View style={{ width: 50, height: 50, backgroundColor: 'green' }} />
</View>

It means:

 Children are in a row (left to right)


 Equal spacing between each box
 Boxes are centered vertically

🎯 Summary: Why Flexbox is Crucial in React Native


Feature Benefit
Responsive Layouts Works across screen sizes and orientations
No Fixed Dimensions Needed Avoids hardcoding width/height
Dynamic Spacing Easily control spacing and alignment
Code Reusability Same logic applies across components
Cleaner Code Easier to maintain compared to absolute positioning
📘 Part: Using Flexbox To Create Layouts

🧱 Step 1: Understanding the Layout Structure

We’re working with a View container that holds two child components:

 TextInput → where users type their goal


 Button → to submit the goal

Initially, both are stacked vertically (default behavior).

Why?

Because the default value of flexDirection in React Native is 'column', meaning children are laid
out top-to-bottom.

🔧 Step 2: Making the Button Appear Next to Input (Horizontal)


We want them side by side, not stacked.

To do that:
flexDirection: 'row'

This is applied to the parent container.

🛠️Code Summary
<View style={styles.inputContainer}>
<TextInput style={styles.textInput} placeholder="Your goal" />
<Button title="Add Goal" />
</View>
inputContainer: {
flexDirection: 'row'
}

📌 Core Concept: Flexbox is Applied to Containers

 Flexbox only works when applied to parent containers.


 You do not apply flexDirection, justifyContent, etc., to child elements like TextInput or
Button.

🧠 Step 3: Using justifyContent to Control Spacing


Add justifyContent: 'space-between' to the container to control the spacing between children.

inputContainer: {
flexDirection: 'row',
justifyContent: 'space-between'
}
justifyContent Value Behavior
flex-start all items at start
center center items
space-between first and last at edges, rest evenly spaced
space-around equal space around items
space-evenly equal space between and around

In this case, since there are only two elements, space-between will push them to opposite ends.
🎨 Step 4: Styling the TextInput
Next, we make the input more readable and styled:

textInput: {
borderWidth: 1,
borderColor: '#cccccc', // light gray
width: '80%',
marginRight: 8,
padding: 8
}

🧩 Explanation:

 borderWidth: 1 → gives the input a visible border


 borderColor: '#cccccc' → light gray border
 width: '80%' → takes 80% of the parent container’s width
 marginRight: 8 → adds spacing between input and button
 padding: 8 → adds space inside the input, between text and border

📌 Note on Percentages

 When setting a width using a percentage, wrap the value in quotes: '80%'
 The percentage is relative to the parent container’s dimensions

🔎 Why the Button Fills the Rest?


React Native's <Button /> has a default flexShrink behavior (i.e., it compresses to fit content), so:

 When TextInput takes 80%,


 The Button adjusts to fit the remaining 20% or less.

However, since there is little or no margin between them, the layout looks tight — adding
marginRight helps visually.

🎯 Key Flexbox Concepts Demonstrated So Far


Concept Use Case
flexDirection: 'row' Arrange input and button side by side
justifyContent: 'space-between' Spread out the items with space in between
Concept Use Case
width: '80%' Control how much space an element takes
marginRight: 8 Add spacing between elements
padding: 8 Add internal spacing within the component

⚠️Real World Note


The <Button /> component in React Native is very basic and does not render beautifully on
Android. That’s why many developers prefer custom buttons using Pressable, TouchableOpacity,
or 3rd-party libraries (like react-native-paper or react-native-elements).

We’ll explore improving the button's styling later.

✅ What You Should Do to Master This


1. Build this layout yourself:
o One TextInput and one Button in a row
o Practice using flexDirection, width, marginRight, padding
2. Change the justifyContent values and observe behavior
3. Use different percentages for input (try 70%, 60%, etc.) and see how the layout adjusts
4. Experiment with vertical layouts (flexDirection: 'column') and alignment (alignItems)

🔧 Flexbox - A Deep Dive

✅ What is Flexbox?

 Flexbox is a layout system designed for arranging items in a single dimension — either row-
wise (horizontally) or column-wise (vertically).
 It helps you control:
o How children are placed inside a container (direction, spacing).
o How much space they take relative to one another.
o How they align with each other.
🧠 Key Differences: React Native vs Web Flexbox
Concept Web (CSS) React Native
Default layout model Not Flexbox Flexbox is default for all Views
Default direction row (horizontal) column (vertical)
Container type <div> <View>

⚠️Note: Even if you're not from a web development background, these differences help clarify why
things behave a certain way in React Native.

🧱 Structure of the Example


In the provided demo:

 A parent <View> holds three colored boxes (red, blue, green) as child Views.
 Each child has a <Text> element with numbers 1, 2, 3.

📐 Main Concepts in Flexbox

1. Axes

Flexbox operates using two axes:

Axis Type Depends on flexDirection value


Main Axis Direction in which elements flow
Cross Axis Perpendicular to the main axis

Examples:

 flexDirection: 'row' → Main = Left ↔ Right, Cross = Top ↕ Bottom


 flexDirection: 'column' (default) → Main = Top ↕ Bottom, Cross = Left ↔ Right
2. flexDirection

Used on the container to control the main axis.

flexDirection: 'row' // children are side by side


flexDirection: 'column' // children stacked vertically (default)
flexDirection: 'row-reverse' // side by side in reverse order
flexDirection: 'column-reverse' // stacked bottom to top

3. justifyContent

 Aligns children along the main axis.


 Common values:

Value Effect
'flex-start' Items are packed at the start of the main axis
'flex-end' Items at the end of the main axis
'center' Items centered on main axis
'space-between' Equal space between items
'space-around' Equal space around each item
'space-evenly' Equal space between + ends

4. alignItems

 Aligns children along the cross axis.


 Common values:

Value Effect
'stretch' Items stretch to fill cross axis (default)
'flex-start' Items align at start of cross axis
'flex-end' Items align at end of cross axis
'center' Items centered on cross axis

5. Child Sizing with flex

 The flex property defines how much space an element should take relative to its siblings.

flex: 1 // Takes one part of available space


flex: 2 // Takes two parts compared to others
Example:
// Three views inside a flex container
<View style={{ flex: 1 }} /> // takes 1 part
<View style={{ flex: 2 }} /> // takes 2 parts
<View style={{ flex: 3 }} /> // takes 3 parts

Total: 6 parts → Views take 1/6, 2/6, and 3/6 of the available space respectively.

🧪 Experiment: Box Width and Height


By default:

 Each child view only takes up space based on its own content (text, etc.).
 If no width/height is defined, the box will shrink to fit.

You can force dimensions with:

<View style={{ width: 300, height: 300 }} />

 When applied to the parent container, it limits the space that children can distribute.

🔄 Nested Flex Containers


You can nest Views, and each of them can act as a Flexbox container:

<View style={{ flexDirection: 'column' }}>


<View style={{ flexDirection: 'row' }}>
<Text>Item A</Text>
<Text>Item B</Text>
</View>
</View>

This enables complex UIs like grids, headers, footers, and so on.

🧲 Flexbox Summary in React Native


Property Role Used On
flexDirection Set layout direction Container View
justifyContent Align children on main axis Container View
Property Role Used On
alignItems Align children on cross axis Container View
flex Control space per item Child Views

💡 Pro Tips
 flex: 1 is extremely common. It's often used to make an element expand to fill remaining
space.
 Nesting multiple Views with different flex settings lets you build responsive layouts.
 Flexbox is always enabled in Views. You can’t disable it, but you control how it behaves.
 Use padding, margin, borderWidth, and borderColor for visual fine-tuning.

✅ Practical Application in React Native


<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems:
'center' }}>
<Text style={{ flex: 1 }}>Box 1</Text>
<Text style={{ flex: 2 }}>Box 2</Text>
</View>

 This arranges Box 1 and Box 2 in a row.


 Box 2 is twice the width of Box 1.
 They’re vertically centered and have space between them.

App.js
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {


return (
<View style={{padding: 50, flexDirection:'row', width:'80%', height:300,
justifyContent:'space-around', alignItems:'strech'}}>
<View
style={{
backgroundColor:'red',
// width:100,
// height:100,
flex:1,
justifyContent:'center',
alignItems:'center'
}}
>
<Text>1</Text>
</View>

<View
style={{
backgroundColor:'green',
// width:100,
// height:100,
flex:2,
justifyContent:'center',
alignItems:'center'
}}
>
<Text>2</Text>
</View>

<View
style={{
backgroundColor:'orange',
// width:100,
// height:100,
flex:1,
justifyContent:'center',
alignItems:'center'
}}
>
<Text>3</Text>
</View>

</View>
);
}

const styles = StyleSheet.create({


container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
📘 Section Overview: Improving The Layout
This part of the course applies the Flexbox fundamentals to practically improve UI layout in a
React Native app. The key goal is to structure the screen properly, control the sizing and spacing of
components using padding, margins, and flex properties, and build responsive layouts.

🎯 Step-by-Step Breakdown & Detailed Explanations

1. 📦 App Container Padding: Improving Top and Horizontal Spacing


paddingTop: 50,
paddingHorizontal: 16,

 paddingTop: 50 → adds vertical space from the top of the screen, preventing the app from
touching the status bar.
 paddingHorizontal: 16 → applies equal padding on the left and right side of the screen (16
pixels).

📌 Note: This is not directly related to Flexbox, but important for good layout design.

2. 🧠 Fixing Layout Overflow: TextInput + Button in a Row

Problem:

 The Button component was overlapping or going off-screen.

Fix:
width: '70%'

 Decreasing the TextInput’s width prevents it from hogging too much space, allowing room
for the button to fit.

📌 React Native uses percentage-based widths relative to the parent container, similar to CSS.
3. 📍 Centering Button Text Vertically

Problem:

 The text in the Button is not vertically centered, as the button is stretched to match the height
of the TextInput.

Why can't we just style the button?

 React Native Button does not support a style prop.


o Even if autocomplete suggests it, it’s not valid.
o Always cross-check with React Native Docs.

Solution:
alignItems: 'center'

 Add this to the parent View (input container) that holds both TextInput and Button.
 alignItems aligns children along the cross-axis (vertical in this case, because the
flexDirection is row).
 'center' makes sure the Button is vertically aligned with the TextInput.

4. ✏️Adding Space and Visual Separation Between Input and Goal List
paddingBottom: 24
borderBottomWidth: 1
borderBottomColor: '#ccc'

 Adds bottom padding to create space below the input area.


 Adds a thin bottom border for visual separation using a soft gray (#ccc).

📌 These are purely styling enhancements, not Flexbox-driven, but part of building professional UIs.

5. 📐 Distributing Vertical Space Using flex (Proportional Layout)

Objective:

 Divide screen vertically so that:


o Input section takes ¼ of screen
o Goals list takes ¾ of screen
Implementation:

Add styles:
inputContainer: {
flex: 1, // Takes 1 portion
},
goalsContainer: {
flex: 3, // Takes 3 portions
},

Logic:

 React Native sums up all flex values: 1 + 3 = 4


 Each container gets a proportional share:
o inputContainer → 1/4
o goalsContainer → 3/4

📌 These flex properties only work if both elements are siblings inside a common Flexbox parent.

6. ❗Important Note: Make Sure Parent Container Takes Full Height

Problem:

 Even though child views use flex, it won’t work if the parent (AppContainer) doesn’t
occupy the full screen.

Fix:
appContainer: {
flex: 1
}

 This ensures that the entire screen height is allocated to the appContainer.
 Now, its children (inputContainer & goalsContainer) can split the available space
proportionally.

📌 Without flex: 1 on the outermost container, child flex values have no effect because there’s no
space to divide.
7. 🔄 Fine-Tuning Proportions and Spacing

Adjustments:

 If 1:3 doesn't look great visually, try:


 inputContainer: {
 flex: 1,
 marginBottom: 24
 },
 goalsContainer: {
 flex: 4
 }

 marginBottom creates spacing below the input area (outside spacing), which doesn’t interfere
with flex-based height distribution.

📌 This stage is about visual balance and might vary based on content or screen size.

🔄 Summary: What You Learned and How Flexbox Was Used


Concept What It Did Key Takeaway
paddingTop, paddingHorizontal Added internal spacing Better spacing from edges
Made TextInput leave room for
width: '70%' Percent-based widths work
Button
Controls cross-axis
alignItems: 'center' Centered button vertically
alignment
flex: 1, flex: 3, etc. Distributed vertical space Based on proportion system
Always required on outer
flex: 1 on parent Enabled child flex to work
container
borderBottomWidth,
Visual separation Pure styling, not Flexbox
borderBottomColor
Adds outer space without Useful for spacing between
marginBottom
affecting layout views

💡 Expert-Level Tips
 ✅ Every View in React Native uses Flexbox by default — you don’t have to explicitly set
display: flex.
 ✅ If your layout isn’t reacting to flex values, check whether:
o Parent container has flex: 1
o All flex siblings are in the same level
 🔄 Flex values (flex: 1, flex: 2, etc.) are relative. They do not mean "take up 1 row" —
they mean "share this ratio of space".
 🧱 Nest views wisely. You can use nested Flexbox to create very complex and responsive
layouts.

EX:
App.js
--
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';

export default function App() {


return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput style={styles.textInput} placeholder="Your course goal!" />
<Button title="Add Goal" />
</View>
<View style={styles.goalsContainer}>
<Text>List of goals...</Text>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc'
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8
},
goalsContainer: {
flex: 5
}
});

🔹 Handling Events
In any app (web or mobile), you need to handle user interactions such as:

 Typing into an input field


 Clicking or tapping buttons

React Native apps are no different.

At this stage, we have a TextInput and a Button, but interacting with them does nothing because we
haven’t added event handlers yet.

🔹 Handling Events in React Native


React Native handles events just like React for the Web:

 Use the useState hook to manage state.


 Add event listener props like onChangeText, onPress, etc., to components.

So React Native is very similar to React:

 Instead of rendering to the DOM, it renders to native mobile components.


 The event handling logic remains exactly the same.

🔹 Goal: Make Input and Button Functional

✏️Step 1: Capture Text Input

Create a handler function to capture what the user types:


function goalInputHandler(enteredText) {
console.log(enteredText);
}

Then, connect this function to the TextInput using the onChangeText prop:

<TextInput onChangeText={goalInputHandler} />

⚠️Important Concept: Passing a Function Reference

When using onChangeText={goalInputHandler}:

 You are not calling the function immediately.


 You are passing a reference to React Native, which will call it at the right time (when the
user types).

If you wrote onChangeText={goalInputHandler()}, it would call the function immediately on


render, which is incorrect.

🔹 Where Does the Text Go?


If we only console.log() the input, we can see it in the terminal, but we can’t use it later (e.g., in
the button press).

This is why we need to store it in state.

🔹 Step 2: Manage the Input as State


To store and update the user input:

const [enteredGoalText, setEnteredGoalText] = useState('');

Now update the state whenever the user types:

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

So every keystroke updates the enteredGoalText variable, which we can use anywhere in the
component.
🔹 Step 3: Handle Button Press
Create another function that runs when the user presses the button:

function addGoalHandler() {
console.log(enteredGoalText);
}

Use onPress in React Native

React Native doesn't use onClick like in web React.


It uses onPress instead:

<Button title="Add Goal" onPress={addGoalHandler} />

Again, don’t call the function in JSX (addGoalHandler()) — just pass a reference
(addGoalHandler).

🔹 Putting It All Together

Final Working Flow:

1. User types something → goalInputHandler is triggered


2. enteredGoalText state is updated
3. User presses the button → addGoalHandler is triggered
4. enteredGoalText is read and logged (or later used to update a goal list)

🔍 Why We Use State Here


You might wonder:

Why not just use a local variable?

Because React components re-render often.

 If we stored the input in a normal variable, the data would be lost on re-render.
 useState ensures the data persists across re-renders and is reactive — it updates the UI or
triggers effects when changed.

🔸 React Native vs Web React: Differences and Similarities


Feature React (Web) React Native
Input Change Handler onChange onChangeText
Button Click onClick onPress
DOM Elements <input>, <div> <TextInput>, <View>
Styling CSS / styled-components JS objects
Layout System CSS Box Model + Flexbox Flexbox only

But state, event handlers, component structure — all are exactly the same.

✅ Testing the Functionality


 Type into the TextInput — see terminal logs from goalInputHandler.
 Press the Add Goal button — see final input logged from addGoalHandler.

This confirms:

 The text is correctly captured


 The text is stored in state
 The button triggers the correct logic
 We can use this input value to now manipulate a goal list

🔜 What’s Next?
Now that we:

 Can capture and store user input


 Can respond to button presses

The next goal will be to:

 Store multiple goals in an array state


 Render the goal list dynamically using components like FlatList
💡 Expert Tip: Code Readability and Best Practices
 Use descriptive names like goalInputHandler, addGoalHandler.
 Keep handler functions pure — let them do one thing.
 Format your code regularly (use Prettier / auto-format in editor).
 Don’t forget to import useState from react, not react-native.

App.js
--
import { useState } from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';

export default function App() {


const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
console.log(enteredGoalText);
}

return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.goalsContainer}>
<Text>List of goals...</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
goalsContainer: {
flex: 5,
},
});

📌 Section Goal: Managing A List Of Course Goals (in our Demo App)
We’re now focusing on managing and displaying a dynamic list of goals entered by the user.

🔹 Why Use State for the Goal List?


React (and React Native) apps re-render UI when state changes.

 A list of goals is dynamic — it changes as the user adds new goals.


 Thus, we must manage the list with the useState hook.

const [courseGoals, setCourseGoals] = useState([]);


 courseGoals: an array to store goal items.
 setCourseGoals: function used to update the array.

Initially, the list is empty.

🔹 Adding a New Goal to the List


When the user presses the Add Goal button:

1. The input text is available in a state variable (enteredGoalText).


2. We use setCourseGoals to update the list.

❌ Incorrect (But Common) Approach:


setCourseGoals([...courseGoals, enteredGoalText]);

 This spreads the old array and adds the new goal.
 It works, but may cause bugs if state updates are queued (happens often in React).

✅ Recommended Approach: Functional State Update

Use a function inside setCourseGoals to safely access the latest state:

setCourseGoals((currentGoals) => [...currentGoals, enteredGoalText]);

💡 Why this is best:

 React may batch state updates, so relying on the current courseGoals directly might be
outdated.
 Using a callback ensures you always operate on the most current and accurate state.

🔹 Rendering the Goal List


React Native doesn't have <ul> or <li> like HTML. Instead, we render a list using:

 Text components for each goal


 Inside a View or other layout container

Use the .map() method to render each goal:

{courseGoals.map((goal) => (
<Text>{goal}</Text>
))}

 This is standard React.


 .map() transforms your array into JSX elements that React Native can render.

⚠️Warning: "Each child in a list should have a unique 'key' prop"


React (web or native) uses the key prop to optimize rendering.

✅ Example fix:
{courseGoals.map((goal) => (
<Text key={goal}>{goal}</Text>
))}

 The key should uniquely identify the element in the list.


 In this case, we use the goal string itself.
 This works, but it's not ideal because:
o Users could enter the same text twice → duplicate keys!

We'll improve this in a later step using unique IDs.

📚 Summary: What You’ve Built

💡 React Native Concepts Practiced:


Feature Explanation
useState([]) To manage the list of goals
.map() in JSX To render each goal dynamically
key prop in lists To help React identify each item uniquely
Functional state update Safe way to update state based on previous value
🛠 Code Example (Simplified)
import { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';

function App() {
const [enteredGoalText, setEnteredGoalText] = useState('');
const [courseGoals, setCourseGoals] = useState([]);

function goalInputHandler(text) {
setEnteredGoalText(text);
}

function addGoalHandler() {
setCourseGoals((currentGoals) => [...currentGoals, enteredGoalText]);
}

return (
<View>
<TextInput onChangeText={goalInputHandler} />
<Button title="Add Goal" onPress={addGoalHandler} />
{courseGoals.map((goal) => (
<Text key={goal}>{goal}</Text>
))}
</View>
);
}

🧠 Deeper Insights: Expert-Level Understanding

🔄 Why React Re-renders

Whenever setState() is called:

 React triggers a re-render of the component.


 It compares the virtual DOM tree to find what needs to be updated.
 key helps it determine which list items changed, were added, or were removed.

✅ Best Practices

 Always use functional updates (prevState => newState) when updating arrays or objects.
 Never mutate state directly (e.g., never use array.push()).
 Add key props to all dynamic lists.
 Separate concerns: Keep input, update logic, and rendering clean and modular.
🧭 What’s Next?
Now that we:

 Can input a goal


 Add it to a list
 Render the list dynamically

The next step is:

 Assign unique IDs to goals


 Use FlatList (a performance-optimized component for long lists)
 Style individual items
 Handle goal deletion

EX:app.js
---
import { useState } from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';

export default function App() {


const [enteredGoalText, setEnteredGoalText] = useState('');
const [courseGoals, setCourseGoals] = useState([]);

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
enteredGoalText,
]);
}

return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.goalsContainer}>
{courseGoals.map((goal) => <Text key={goal}>{goal}</Text>)}
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
goalsContainer: {
flex: 5,
},
});

🔹 iOS & Android Styling Differences

🎯 Problem:

You're rendering a list of goals dynamically using .map(), but the list items are plain text. We want
them to look styled and visually distinct.
🧱 Step-by-Step Explanation:

1. Creating a Style for List Items

In React Native, styles are created using JavaScript objects, not CSS files.

const styles = StyleSheet.create({


goalItem: {
margin: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
padding: 8,
color: 'white', // Initially added here (but won't work on <View>)
},
});

 margin: External space around each goal item.


 borderRadius: Rounds the corners (like in CSS).
 backgroundColor: Adds background color using hex.
 padding: Internal spacing between text and container edge.
 color: Intended for text, but added to a View here initially.

⚠️Note: Unlike web development, this is not CSS, even if it looks like it. It’s JS-based styling.

2. Why Rounded Corners Didn’t Show on iOS

 The Text component does not support borderRadius on iOS.


 Android renders it fine because its native equivalent supports it.
 iOS's native text rendering widget ignores border styling on Text.

✅ Solution:

Wrap Text inside a View (which does support borderRadius across platforms).

return (
<View style={styles.goalItem} key={goal}>
<Text>{goal}</Text>
</View>
);
3. Fixing Text Color

Once we moved the style to the View, the color: 'white' doesn’t affect the Text anymore.

Why?

In React Native, styles don’t cascade like in CSS. There is no inheritance between parent and child
components.

✅ Solution:

Create a new style for the Text element specifically:

goalText: {
color: 'white',
}

Then apply it like this:

<Text style={styles.goalText}>{goal}</Text>

🧠 Key Learnings for Expert-Level React Native

1. React Native ≠ Web React

 React Native uses React’s component and state logic.


 But styling, components, and rendering use native mobile components, not HTML/CSS.

2. Styles Don’t Cascade

 In web, children inherit styles from parent elements.


 In React Native, each component's style is self-contained.

Example:

/* Web CSS */
.parent {
color: white;
}
.child {
/* This would inherit white color */
}
But in React Native, Text won't inherit color from View. You must apply it directly.

3. Cross-Platform Differences
Issue Cause Fix
borderRadius missing iOS Text doesn't support it Use View wrapper
Styles not applied Styles don’t cascade in RN Apply directly on target components

4. Design Pattern: Separating Text and Container Styles

Create separate styles for container (View) and text (Text):

goalItem: {
backgroundColor: '#5e0acc',
padding: 8,
margin: 8,
borderRadius: 6,
},
goalText: {
color: 'white',
}

5. General Best Practices

 Use View for layout/styling containers.


 Use Text for strings only. Text must go inside a Text component.
 Avoid relying on implicit style inheritance.
 Always use key when rendering lists to avoid warnings and ensure optimal rendering.

✅ Result
With proper styling and correct component usage:

 Your list looks better.


 It renders correctly on both Android and iOS.
 You understand platform differences and React Native’s non-CSS styling.
🧭 What’s Next?
Now that we have styled our goals list correctly and handled platform-specific quirks, the next step is
to learn:

 How to extract these list items into reusable components.


 How to handle list performance with FlatList.
 Advanced styling with StyleSheet.create() and responsive layouts.

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';

export default function App() {


const [enteredGoalText, setEnteredGoalText] = useState('');
const [courseGoals, setCourseGoals] = useState([]);

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
enteredGoalText,
]);
}

return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.goalsContainer}>
{courseGoals.map((goal) => (
<View key={goal} style={styles.goalItem}>
<Text style={styles.goalText}>{goal}</Text>
</View>
))}
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
goalsContainer: {
flex: 5,
},
goalItem: {
margin: 8,
padding: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
goalText: {
color: 'white',
}
});
📘 Topic: Making Lists Scrollable with ScrollView in React Native

⚠️Problem:

When adding many list items (e.g., goals), they eventually exceed the screen space. But surprisingly,
you can't scroll — even though that’s expected behavior on a webpage.

❓ Why Can't You Scroll in React Native?

🧾 Explanation:

In web development, overflowed content scrolls by default.

 The browser handles scrollbars automatically.


 Scrollbars appear when the content exceeds the viewport.

✅ But in React Native, scrolling is not automatic. You must explicitly use a scrollable container to
make it work.

✅ Solution: Use ScrollView


React Native provides a built-in component:

➤ ScrollView

 This component makes its content scrollable vertically or horizontally.


 It’s similar to a div with overflow: scroll in web dev.

🛠️Step-by-Step: Making a List Scrollable

1. Import ScrollView
import { ScrollView } from 'react-native';
2. Wrap List in ScrollView

Replace the View that wraps the list of goals with ScrollView:

<ScrollView>
{goals.map((goal) => (
<View key={goal} style={styles.goalItem}>
<Text style={styles.goalText}>{goal}</Text>
</View>
))}
</ScrollView>

Now the list can be scrolled when its content exceeds screen height.

🧱 Layout Considerations

⚠️Problem After Replacing View with ScrollView:

The proportions of the layout change — more space goes to input, less to the list.

🧾 Why?

 The ScrollView does not automatically take full height unless bounded by a parent View.
 It's the outer container (View) that controls layout size.
 If you apply layout styles (like flex: 1) to the wrong component, proportions shift
unexpectedly.

✅ Correct Layout Strategy

Use this structure:

<View style={styles.goalsContainer}>
<ScrollView>
{/* List Items Here */}
</ScrollView>
</View>

 goalsContainer (View) → Controls layout and height.


 ScrollView → Makes the list inside it scrollable.

This setup:
 Maintains layout proportions
 Enables smooth scrolling

📜 Bonus: Behavior of ScrollView


Even when no list item exceeds the screen, you can still scroll — but only if ScrollView allows it.

When there's not enough content:

 ScrollView still responds, but may bounce back.


 Especially noticeable on iOS (due to the "bounce effect").

🔧 Configuring ScrollView Props (Expert Tips)


ScrollView has many configurable props.

Explore them at React Native Docs, but here's a short list of important ones:

Prop Description Platforms


horizontal Enable horizontal scrolling Both
showsVerticalScrollIndicator Show/hide vertical scrollbar Both
bounces Enable/disable bouncing effect on scroll iOS
alwaysBounceVertical Always allow vertical bounce (even if content is short) iOS
contentContainerStyle Style for inner scrollable content Both

Example: Disabling iOS Bounce


<ScrollView alwaysBounceVertical={false}>
{/* content */}
</ScrollView>

Result:

 No bounce animation on iOS.


 Useful if you want tighter, non-bouncy UIs.
🧠 Key Learnings for Expert React Native Devs

1. Scrolling is not automatic like in browsers.

You must wrap content in a scrollable component like ScrollView.

2. ScrollView must be bounded by a parent view for proper layout control.

 Use a parent View with flex or height settings.


 This ensures your ScrollView takes up the correct amount of space.

3. ScrollView is great, but has limitations:

 All items are rendered at once, which is inefficient for very large lists.
 For large datasets, you'll learn to use FlatList (later in the course).

4. Platform Differences Matter

 iOS and Android behave differently.


 You may need platform-specific tweaks (e.g., disabling bounce on iOS).

✅ Summary (TL;DR)
Concept Explanation
Scrolling not default React Native needs explicit scroll containers
Use ScrollView To make lists or content scrollable
Wrap in parent View Control layout size with outer View
iOS bounce effect Can be disabled via alwaysBounceVertical={false}
No scroll when list is short ScrollView needs overflowed content to show scroll behavior
Platform differences Props like bounces, alwaysBounceVertical vary per OS

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, Text, View, Button, TextInput, ScrollView } from 'react-
native';

export default function App() {


const [enteredGoalText, setEnteredGoalText] = useState('');
const [courseGoals, setCourseGoals] = useState([]);

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
enteredGoalText,
]);
}

return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.goalsContainer}>
<ScrollView>
{courseGoals.map((goal) => (
<View key={goal} style={styles.goalItem}>
<Text style={styles.goalText}>{goal}</Text>
</View>
))}
</ScrollView>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
goalsContainer: {
flex: 5,
},
goalItem: {
margin: 8,
padding: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
goalText: {
color: 'white',
}
});

✅ Part 1: Why ScrollView Isn’t Always Ideal for Lists

🔹 ScrollView Basics:

 ScrollView is a component in React Native that makes content scrollable when it overflows
the screen.
 Good for static or limited content, like a long article or form.
 By default, it renders all children inside it, regardless of how many there are or whether
they're visible.
🔹 Performance Downside of ScrollView:

 All items in the list are rendered, even if they're off-screen.


 For a small list (10–20 items), this is fine.
 For very long lists (hundreds or thousands of items), this results in:
o Unnecessary memory usage
o Performance degradation
o Slow rendering
 This is not efficient and can lead to laggy UI and bad UX on low-end devices.

✅ Part 2: Introducing FlatList – Optimized for Long Lists

🔹 What is FlatList?

 A React Native built-in component optimized for rendering large lists efficiently.
 Automatically handles:
o Lazy rendering (only renders items when they come into view)
o Memory efficiency
o List virtualization (loads and removes items as needed)
 Great for:
o Infinite scrolling
o Dynamically loaded lists (e.g. APIs)
o Any long list where performance matters

✅ Part 3: Using FlatList in Practice

🔸 Step-by-Step Conversion (from ScrollView to FlatList):

1. Replace <ScrollView> with <FlatList>


<FlatList />

2. Remove manual .map() over the array.

 Let FlatList handle rendering of items.


 You pass the array to FlatList via the data prop.
3. Use renderItem Prop

 renderItem is a function that tells FlatList how to render each item.

<FlatList
data={courseGoals}
renderItem={(itemData) => {
return <Text>{itemData.item}</Text>;
}}
/>

 itemData is an object containing:


o item: the actual data item
o index: index in the list
o other metadata

✅ Part 4: Handling Keys in FlatList

🔸 Why Keys Matter:

 React uses key props to efficiently update/render lists.


 Without keys, you’ll get performance warnings and potentially buggy list behavior.

🔸 2 Ways to Add Keys:

🔹 Option 1: Use key property directly in your data

Instead of storing strings in your list, store objects with key and text:

{ key: 'g1', text: 'Learn React Native' }

Then, in your renderItem:

<Text>{itemData.item.text}</Text>

FlatList automatically uses the key property for internal optimizations.

🔹 Option 2: Use keyExtractor prop

If your data has a custom ID field (like id), and not key, you can manually tell FlatList how to extract
it:

<FlatList
data={courseGoals}
keyExtractor={(item, index) => item.id}
renderItem={(itemData) => {
return <Text>{itemData.item.text}</Text>;
}}
/>

 keyExtractor accepts a function that returns a unique string key.


 It receives item and index as parameters.

✅ Part 5: Summary – FlatList vs ScrollView


Feature ScrollView FlatList
Suitable for Static or small sets of content Dynamic or large lists
Renders all items? Yes (even off-screen) No (renders only visible items)
Optimized for performance ❌ ✅
Handles dynamic data ❌ (manual map) ✅ (with data & renderItem)
Auto key management ❌ (you must assign keys manually) ✅ (via key or keyExtractor)

✅ Additional Tips for FlatList

🔸 Additional Props (for customization):

 horizontal: For horizontal scrolling


 numColumns: For grid layout
 ListHeaderComponent, ListFooterComponent: Custom headers/footers
 onEndReached: Useful for infinite scrolling / pagination
 initialNumToRender: Number of items rendered initially

🔸 Lazy Loading:

 FlatList loads only visible items + a small buffer.


 It’s built-in and requires no additional setup.

✅ Final Thought
Always choose FlatList over ScrollView when:
 You’re dealing with dynamic, growing, or large data sets.
 Performance and memory usage are concerns.
 You want built-in optimizations and simplicity.

Use ScrollView when:

 Content is limited and static (like forms or articles).


 You need to scroll multiple components of varying type and layout inside one screen.

EX: app.js
--
import { useState } from 'react';
import {
StyleSheet,
Text,
View,
Button,
TextInput,
ScrollView,
FlatList,
} from 'react-native';

export default function App() {


const [enteredGoalText, setEnteredGoalText] = useState('');
const [courseGoals, setCourseGoals] = useState([]);

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}

return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return (
<View style={styles.goalItem}>
<Text style={styles.goalText}>{itemData.item.text}</Text>
</View>
);
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
goalsContainer: {
flex: 5,
},
goalItem: {
margin: 8,
padding: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
goalText: {
color: 'white',
},
});

🧠 Core Concept: Splitting a Large Component into Smaller Ones

📌 Problem

As apps grow, the main component file (like App.js) becomes large and harder to maintain. This
makes the code:

 Difficult to read.
 Hard to debug or enhance.
 Less reusable.

✅ Solution

Split the UI and logic into smaller, reusable components.


This is a common practice in all React-based environments (web or mobile).

📁 Step 1: Create a Folder for Components

➕ Why?

Organizing your components into a components/ folder keeps your project structured and scalable.
🔧 Action

Create a folder:

/components

Inside this folder:

 GoalItem.js: Component to render a single goal item.


 GoalInput.js: Component to manage the input form (not yet built in this section).

⚛️Step 2: Create a Functional Component - GoalItem.js

✅ Code Structure
function GoalItem() {
return (
// JSX for rendering a goal item
);
}
export default GoalItem;

✅ Note: You no longer need to import React from 'react' in newer React Native versions (React
17+).

🔁 Step 3: Move the JSX Code

🎯 Goal

Move the JSX code that renders a single item from App.js to GoalItem.js.

💡 Don’t move the list rendering logic (FlatList). Just move the part that renders one item.
🎨 Step 4: Move Styles with the Component

📌 Why?

Styles should be defined close to the component they belong to. This:

 Keeps concerns together (UI + styles).


 Increases reusability.
 Improves readability.

🔧 Implementation

In GoalItem.js, import StyleSheet from react-native:

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({


goalItem: {
// your styles here
},
goalText: {
// your styles here
}
});

Now the component and its styles live together in one place. ✅

⛓️Step 5: Import and Use the Component in App.js

📥 Import
import GoalItem from './components/GoalItem';

🧠 JSX Usage

Instead of returning the JSX directly inside FlatList.renderItem, return the custom component:

<FlatList
data={courseGoals}
renderItem={(itemData) => <GoalItem />}
keyExtractor={(item) => item.id}
/>
⚠️You will need to pass props like text, onDelete, etc. to GoalItem – not covered yet in the
transcript, but essential.

💡 Additional Expert Notes

📦 Component Reusability

Splitting components makes them reusable in other parts of your app (or even other projects).

🧩 Component Encapsulation

Each component should handle:

 Its own JSX.


 Its own styling.
 Its own behavior (e.g., touch actions, animations).

🎯 Functional Component vs Class Component

Today, React Native apps are almost always built using Functional Components with Hooks like
useState, useEffect, etc.

🧠 Summary - Key Takeaways


Concept Explanation
Large component file (App.js) Becomes hard to manage as features grow.
Component splitting Helps keep code clean, modular, and readable.
components/ folder Convention for organizing custom components.
Functional components Preferred in modern React Native apps.
Styles per component Keep styles close to JSX for maintainability.
Importing/exporting Use export default and import ... from ....
Component naming Use PascalCase (e.g., GoalItem) for components.

📚 Topic: Passing Data Between Components in React Native


🧩 Why Pass Data?
In the App.js, we render a list of items (goals), and the text of each goal is part of the data stored in
the parent (App) component.
To display each goal inside the GoalItem component, we need to pass this text to the child
component.

💡 Problem: GoalItem needs to display a piece of data (like a goal text), but this data exists in App.js.

✅ Solution: Use props

React Native, like React for the web, allows us to pass data from a parent component to a child
component via props.

🔧 Step-by-Step Breakdown

1. 🔽 Expect Props in the Child Component

In GoalItem.js:

function GoalItem(props) {
return (
<View style={styles.goalItem}>
<Text style={styles.goalText}>{props.text}</Text>
</View>
);
}

 props is an object that contains the data passed from the parent.
 You can access props.text, props.id, or any other field you choose to send.
 props.text holds the actual goal text (e.g., "Learn React Native").

2. 🔼 Pass Props from the Parent Component

In App.js, when rendering GoalItem:

<FlatList
data={courseGoals}
renderItem={(itemData) => (
<GoalItem text={itemData.item.text} />
)}
keyExtractor={(item) => item.id}
/>

 itemData.item.text is the actual value of the goal.


 This is passed as a prop named text to the GoalItem component.

❗ Common Pitfall: “Can't Find Variable View” Error


🛑 Error: Can't find variable: View

❓ Why This Happens

React Native uses custom UI components (like <View>, <Text>) instead of HTML tags (<div>, <p>).

These components are not globally available, unlike HTML tags in React for the web.

✅ Fix: Import the Required Components

In GoalItem.js, you must import each React Native UI component you use:

import { View, Text, StyleSheet } from 'react-native';

Without importing these, React Native won’t recognize View or Text, leading to a crash.

📊 Key Differences Between React (Web) and React Native


Feature React (Web) React Native
Base tags <div>, <span>, <h1> <View>, <Text>
CSS Standard CSS / CSS-in-JS StyleSheet or inline styles
Imports for base tags Not needed Mandatory (e.g., View, Text)
DOM rendering Browser DOM Native platform rendering

🧠 Expert-Level Concepts

🔁 Reusability

With props, you can reuse a component and pass different data to each instance.
<GoalItem text="Learn React Native" />
<GoalItem text="Practice UI layouts" />

This makes your code dynamic and efficient.

📦 Component Communication Direction

 Parent ➝ Child: via props (✅ What we're doing here).


 Child ➝ Parent: via callback functions passed in props (used later for actions like deleting a
goal).

🧠 Summary — What You’ve Learned


Concept Explanation
Props Allow passing data from parent to child components.
Component Importing React Native components like View and Text must be explicitly imported.
JSX Structure Children use props.<property> to access passed data.
Error Fixing Import missing components to avoid runtime crashes.
Component Structure Move logic into small, focused components to stay organized.

EX:app.js
--

📚 Topic: Passing Data Between Components in React Native

🧩 Why Pass Data?


In the App.js, we render a list of items (goals), and the text of each goal is part of the data stored in
the parent (App) component.
To display each goal inside the GoalItem component, we need to pass this text to the child
component.

💡 Problem: GoalItem needs to display a piece of data (like a goal text), but this data exists in App.js.
✅ Solution: Use props

React Native, like React for the web, allows us to pass data from a parent component to a child
component via props.

🔧 Step-by-Step Breakdown

1. 🔽 Expect Props in the Child Component

In GoalItem.js:

function GoalItem(props) {
return (
<View style={styles.goalItem}>
<Text style={styles.goalText}>{props.text}</Text>
</View>
);
}

 props is an object that contains the data passed from the parent.
 You can access props.text, props.id, or any other field you choose to send.
 props.text holds the actual goal text (e.g., "Learn React Native").

2. 🔼 Pass Props from the Parent Component

In App.js, when rendering GoalItem:

<FlatList
data={courseGoals}
renderItem={(itemData) => (
<GoalItem text={itemData.item.text} />
)}
keyExtractor={(item) => item.id}
/>

 itemData.item.text is the actual value of the goal.


 This is passed as a prop named text to the GoalItem component.
❗ Common Pitfall: “Can't Find Variable View” Error
🛑 Error: Can't find variable: View

❓ Why This Happens

React Native uses custom UI components (like <View>, <Text>) instead of HTML tags (<div>, <p>).

These components are not globally available, unlike HTML tags in React for the web.

✅ Fix: Import the Required Components

In GoalItem.js, you must import each React Native UI component you use:

import { View, Text, StyleSheet } from 'react-native';

Without importing these, React Native won’t recognize View or Text, leading to a crash.

📊 Key Differences Between React (Web) and React Native


Feature React (Web) React Native
Base tags <div>, <span>, <h1> <View>, <Text>
CSS Standard CSS / CSS-in-JS StyleSheet or inline styles
Imports for base tags Not needed Mandatory (e.g., View, Text)
DOM rendering Browser DOM Native platform rendering

🧠 Expert-Level Concepts

🔁 Reusability

With props, you can reuse a component and pass different data to each instance.

<GoalItem text="Learn React Native" />


<GoalItem text="Practice UI layouts" />

This makes your code dynamic and efficient.


📦 Component Communication Direction

 Parent ➝ Child: via props (✅ What we're doing here).


 Child ➝ Parent: via callback functions passed in props (used later for actions like deleting a
goal).

🧠 Summary — What You’ve Learned


Concept Explanation
Props Allow passing data from parent to child components.
Component Importing React Native components like View and Text must be explicitly imported.
JSX Structure Children use props.<property> to access passed data.
Error Fixing Import missing components to avoid runtime crashes.
Component Structure Move logic into small, focused components to stay organized.

📦 Goal: Splitting the Input Logic into a Separate Component


We are moving the input form (TextInput + Button) out of App.js and into a custom component
called GoalInput.js to keep the code modular and clean.

📁 Step 1: Create a New Component

🔨 GoalInput.js

Create a new file and define a functional component:

import { View, TextInput, Button, StyleSheet } from 'react-native';


import React, { useState } from 'react';

function GoalInput(props) {
// Will add logic soon
return (
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal"
onChangeText={goalInputHandler}
value={enteredGoalText} // For two-way binding
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
);
}

🧠 What Are We Doing?


We're:

 Moving all input-related JSX (View, TextInput, Button) from App.js to GoalInput.js.
 Copying relevant styles (inputContainer, textInput) from App.js.
 Creating a new component-specific state to manage input.

⚙️Step 2: Managing Local State in GoalInput.js


Since TextInput is now in GoalInput.js, we must move the state and its handler there.

🔄 From App.js → GoalInput.js


const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

🏗️Step 3: Communicating with Parent (App.js)

📤 The Problem

We want to pass the goal text from GoalInput to App, but the state (courseGoals) and its update
logic (addGoalHandler) live in App.

🧩 Solution: Pass a Function Prop from Parent to Child

In App.js:

<GoalInput onAddGoal={addGoalHandler} />

In GoalInput.js, call that function when the button is pressed:

function addGoalHandler() {
props.onAddGoal(enteredGoalText); // pass data to parent
setEnteredGoalText(''); // clear input
}

 onAddGoal is a function prop.


 It allows the child component to call a function defined in the parent, passing its data up.

🔁 Step 4: Handling Two-Way Binding


We reset the input after adding a goal with:

setEnteredGoalText('');

But this was not reflected in the UI because the TextInput did not know it was being reset.

🧪 Fix: Bind Value to State


<TextInput
style={styles.textInput}
placeholder="Your course goal"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>

This is two-way binding:

 The user can change the input field → state updates.


 When state is reset → input field updates.

🧹 Step 5: Clean Up App.js


After splitting logic:

 Remove state and functions related to enteredGoalText from App.js.


 Keep only:
 const [courseGoals, setCourseGoals] = useState([]);

 function addGoalHandler(enteredGoalText) {
 setCourseGoals((currentGoals) => [
 ...currentGoals,
 { text: enteredGoalText, id: Math.random().toString() },
 ]);
 }
📊 Final Data Flow Diagram
[App.js]
|
↓ (prop)
<GoalInput onAddGoal={addGoalHandler} />
|
↓ (event)
Button press → addGoalHandler() in GoalInput
|

props.onAddGoal(enteredGoalText)
|

App.js's addGoalHandler is executed
→ courseGoals state is updated

💡 Important Concepts Covered


Concept Explanation
Component Splitting Break logic into reusable parts for cleaner code
Props One-way data transfer from parent to child
Function as Prop Enables child to send data back to parent
State Management Keep local component state (enteredGoalText) in the relevant component
Two-Way Binding Bind value and onChangeText to keep UI and state in sync
JSX Extraction Move repetitive or logical UI into separate files

✅ Result
 Modular, reusable GoalInput component.
 Clear data flow: input (child) ➝ list (parent).
 Reset input after each submission.
 Two-way binding for accurate UI updates.

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, View, FlatList } from 'react-native';

import GoalItem from './components/GoalItem';


import GoalInput from './components/GoalInput';
export default function App() {
const [courseGoals, setCourseGoals] = useState([]);

function addGoalHandler(enteredGoalText) {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}

return (
<View style={styles.appContainer}>
<GoalInput onAddGoal={addGoalHandler} />
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return <GoalItem text={itemData.item.text} />;
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
goalsContainer: {
flex: 5,
},
});

Components/GoalItem.js
---
import { StyleSheet, View, Text } from 'react-native';
function GoalItem(props) {
return (
<View style={styles.goalItem}>
<Text style={styles.goalText}>{props.text}</Text>
</View>
);
}

export default GoalItem;

const styles = StyleSheet.create({


goalItem: {
margin: 8,
padding: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
goalText: {
color: 'white',
},
});

Components/Goalnput.js
---
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';

function GoalInput(props) {
const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
props.onAddGoal(enteredGoalText);
setEnteredGoalText('');
}

return (
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
);
}

export default GoalInput;

const styles = StyleSheet.create({


inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
});

📚 React Native Expert Notes — Making List Items


Tappable for Deletion

✅ What You Can Already Do


 Users can add items to a list.
 The list is managed efficiently using state management (useState) in the parent component
(App.js).
 Each list item is rendered via a reusable component (GoalItem.js).

❌ What You Can’t Do Yet


 You can’t delete items.
 User interaction with list items isn’t possible — tapping does nothing yet.

🎯 Goal
Allow users to tap on a list item to delete it from the list.

🖱 Web vs. React Native: Event Handling


Feature Web (React DOM) React Native
Tap/Click Event onClick prop onPress via Pressable
Interactive UI Any element can handle clicks Needs to be wrapped in an interactive component

👉 Web Example:
<div onClick={deleteItem}>Delete</div>

❌ This will NOT work in React Native:


<View onClick={...}> // Invalid

✅ React Native’s Way: Use Pressable


React Native provides special components for touch interaction:

Component Status Description


Pressable ✅ Recommended Modern way to handle press interactions
TouchableOpacity ⚠️Legacy Slight fade effect when tapped
TouchableHighlight ⚠️Legacy Background highlight on tap
TouchableNativeFeedback ⚠️Legacy Android-only ripple effect
Why Use Pressable?

 Introduced as the modern, more powerful alternative.


 Provides access to the pressed state (for styling on press).
 Consistent across platforms.

📦 Implementing Pressable in a Component

File: GoalItem.js

Wrap the component’s main view with a Pressable.

import { Pressable, View, Text } from 'react-native';

function GoalItem(props) {
return (
<Pressable onPress={props.onDeleteItem}>
<View style={styles.goalItem}>
<Text>{props.text}</Text>
</View>
</Pressable>
);
}

Key Concept:

The onPress function will run whenever the user taps that item.

🧠 Application Architecture and Data Flow

Where to Put the Logic for Deleting an Item?

Since the list is managed in App.js, the deletion logic must also live there.

File: App.js
function deleteGoalHandler() {
console.log("DELETE");
}

📝 For now, this is just a console log, but later we’ll update the actual list state using setCourseGoals.
🧩 Passing Down the Handler via Props
React uses one-way data flow, so the child (GoalItem) must receive the function from the parent
(App.js) through props.

In App.js render method (or FlatList):


<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>

In GoalItem.js, the child component:


<Pressable onPress={props.onDeleteItem}>
<View>
<Text>{props.text}</Text>
</View>
</Pressable>

📥 Summary of Prop Flow


From To Purpose
App.js GoalItem Pass deleteGoalHandler through prop named onDeleteItem
GoalItem.js Pressable Use that prop to define what happens on press

🔄 Flow Diagram
User taps GoalItem

GoalItem runs props.onDeleteItem()

App.js receives the call in deleteGoalHandler()

Logs "DELETE" to console (for now)

👀 Visual Feedback (Coming Soon)


Currently, tapping does not give any visible clue to the user.

This will be improved later using Pressable's pressed state or animation techniques.
🧠 Expert Concept Review
Concept Explanation
Component Touch interactions must be wrapped using a React Native interactive
Wrapping component (Pressable)
Event Handling Unlike the web, onClick doesn't exist. Use onPress.
State (like goal list) must be managed in the top-level component that controls
Lifting State
the data (App.js)
Functional Props Functions passed as props let children notify parents of user interactions

🧰 What’s Ready
✅ Items are tappable
✅ Tapping triggers a function
✅ Communication from child to parent is set up
✅ Scaffold for deletion is in place

🧠 React Native Notes — Deleting Items with Pressable and


filter()

🔄 Objective
You already built a tappable list using Pressable. Now, you’ll make those taps actually delete items
from the list.

To achieve this:

 We must identify which item to delete.


 Then, remove that item from the stateful list in App.js.

🧩 Step-by-Step Breakdown
🔧 Step 1: Handle Deletion by ID

In App.js, we already created:

function deleteGoalHandler() {
console.log("DELETE");
}

Now let’s improve it. We want this function to:

 Know which item was tapped.


 Remove that item from the list.

✅ How? Pass the id of the goal item to deleteGoalHandler.

💡 Why Use ID?

Every item in your list should have a unique identifier (usually assigned with uuid or
Math.random() during creation). This ID:

 Distinguishes each item.


 Allows deletion of the correct one — even if multiple items have the same text.

🧠 React State Update: Functional Approach

In React (and React Native), if you’re updating state based on the previous state, always use the
functional form of setState.

setCourseGoals((currentCourseGoals) => {
return currentCourseGoals.filter((goal) => goal.id !== id);
});

🚀 filter() Recap

 .filter() creates a new array by testing every element.


 The return value of the function inside filter determines:
o true → item is kept.
o false → item is removed.

Logic Used:
goal.id !== id

So the one that matches the tapped ID is filtered out (removed).

📬 Step 2: Ensure id is Passed to deleteGoalHandler


But wait — where do we get this id when the item is tapped?

Problem:

When onPress runs, it doesn’t automatically pass the ID.

🧠 Solution: Bind a Function with Predefined Parameter


onPress={props.onDeleteItem.bind(this, props.id)}

🔍 Explanation:

 bind() is a native JavaScript method.


 It pre-configures a function so that when it's called later, it's already "bound" with certain
arguments.
 props.id is pre-set as the first argument of deleteGoalHandler.

✅ When the item is pressed, deleteGoalHandler(id) is triggered — with the correct ID!

🧠 Alternative to .bind()

Instead of using .bind(), you could also use an inline arrow function:

onPress={() => props.onDeleteItem(props.id)}

This is more readable and preferred in modern React unless performance is a major concern (e.g., in
large lists).
🔗 Step 3: Ensure id is Actually Passed Down from App.js

In App.js, when rendering the GoalItem:


<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>

You must pass both text and id to the GoalItem component.

🧱 Final Code Summary


App.js
function deleteGoalHandler(id) {
setCourseGoals((currentGoals) => {
return currentGoals.filter((goal) => goal.id !== id);
});
}

Rendering:
<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>

GoalItem.js
<Pressable onPress={props.onDeleteItem.bind(this, props.id)}>
<View>
<Text>{props.text}</Text>
</View>
</Pressable>

🎯 Final Result
 You can now tap to delete any list item.
 Items are filtered out by their unique id.
 The list re-renders with the item removed.
 Everything is handled with clean one-way data flow and functional state updates.
💡 Pro Tips
Best Practice Why It Matters
Use unique IDs Prevent accidental deletion of duplicates
Use functional setState Ensures you always work with the latest state
Use .filter() Clean and efficient array manipulation
Always return new arrays (immutability = performance +
Avoid mutating state
reliability)
Use arrow functions instead of .bind()
Shorter and cleaner syntax
(optional)

⛔ Optional Additions (Not Covered Yet)


 ❌ No visual feedback on tap → Add styles using pressed state.
 ❌ No input validation yet → Prevent empty items from being added.
 ❌ No confirmation before delete → Can add modal/alert later.

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, View, FlatList } from 'react-native';

import GoalItem from './components/GoalItem';


import GoalInput from './components/GoalInput';

export default function App() {


const [courseGoals, setCourseGoals] = useState([]);

function addGoalHandler(enteredGoalText) {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}

function deleteGoalHandler(id) {
setCourseGoals((currentCourseGoals) => {
return currentCourseGoals.filter((goal) => goal.id !== id);
});
}

return (
<View style={styles.appContainer}>
<GoalInput onAddGoal={addGoalHandler} />
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return (
<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>
);
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
goalsContainer: {
flex: 5,
},
});

Components/GoalInput.js
--
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';

function GoalInput(props) {
const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
props.onAddGoal(enteredGoalText);
setEnteredGoalText('');
}

return (
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
);
}

export default GoalInput;

const styles = StyleSheet.create({


inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
});
Components/GoalInput.js
--
import { StyleSheet, View, Text, Pressable } from 'react-native';

function GoalItem(props) {
return (
<Pressable onPress={props.onDeleteItem.bind(this, props.id)}>
<View style={styles.goalItem}>
<Text style={styles.goalText}>{props.text}</Text>
</View>
</Pressable>
);
}

export default GoalItem;

const styles = StyleSheet.create({


goalItem: {
margin: 8,
padding: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
goalText: {
color: 'white',
},
});

🎨 React Native Pressable Styling Notes: Visual Feedback


on Press

🔍 Problem:
After implementing item deletion via Pressable, there is no visual feedback when a user taps on an
item. That feels unresponsive, especially on mobile apps.
✅ Goal:
Give press feedback for both Android and iOS platforms using platform-specific and cross-platform
techniques.

🧱 Background:
React Native’s Pressable component provides:

 android_ripple prop for native Android ripple effect.


 style prop with dynamic styling function for iOS and Android support.

🔧 Step-by-Step Breakdown

✅ 1. Add Ripple Effect on Android

Pressable supports a special prop:

<Pressable android_ripple={{ color: '#dddddd' }}>


<Text>Item</Text>
</Pressable>

 The ripple appears when the item is tapped and held briefly.
 You can use hex colors (#dddddd is a light gray ripple).
 The ripple is only visible on Android.
 It does not show on iOS.

💡 Tip: Move Ripple to Cover the Full Area

If your structure looks like this:

<Pressable android_ripple={{ color: '#dddddd' }}>


<View style={styles.goalItem}>
<Text>{props.text}</Text>
</View>
</Pressable>
Then the ripple affects the whole container.

But if you want the ripple to only appear within the text area, you can wrap the Text only:

<View style={styles.goalItem}>
<Pressable android_ripple={{ color: '#dddddd' }}>
<Text>{props.text}</Text>
</Pressable>
</View>

However, this might make the touch target smaller, so it's best to have Pressable around the full
item for better usability.

✅ 2. Adjust Padding to Improve Ripple Visibility

If the ripple only appears around the text and not the padding, it's likely due to padding being on the
outer View.

Solution:

Move the padding into the Text or adjust the layout like this:

<Pressable android_ripple={{ color: '#dddddd' }}>


<View>
<Text style={{ padding: 8 }}>{props.text}</Text>
</View>
</Pressable>

Now, the ripple respects the padding and gives better visual feedback.

🍎 iOS: No Native Ripple — Use style Prop Dynamically


Android ripple won’t work on iOS. But React Native provides an alternative:

<Pressable style={({ pressed }) => pressed ? styles.pressedItem : null}>


<View style={styles.goalItem}>
<Text>{props.text}</Text>
</View>
</Pressable>
💡 How it Works:

 The style prop accepts a function that receives a pressed boolean.


 You can return conditional styles based on whether the item is currently being pressed.

🔧 Define Styles:
const styles = StyleSheet.create({
goalItem: {
margin: 8,
padding: 8,
backgroundColor: '#5e0acc',
borderRadius: 6,
},
pressedItem: {
opacity: 0.5,
},
});

So when the item is being pressed, it becomes semi-transparent.

🧠 Expert Tips
Topic Tip
android_ripple Use for Android-only effects. Configure color and size using the style.
style={({ pressed }) Works on both platforms. Very flexible: change background, borders,
=> ...} shadows, etc.
Always add visual feedback for touchable elements — it's a core UX
UI Feedback
principle in mobile apps.
Prefer Pressable wrapping the full item for better accessibility and a
Component Structure
larger hit area.
Combine press feedback with animations (like using Animated.View)
Advanced Styling
for smoother UX.

🔁 Optional: Combine Both Methods


You can combine Android’s android_ripple and dynamic styling for full coverage:

<Pressable
android_ripple={{ color: '#dddddd' }}
style={({ pressed }) => pressed && styles.pressedItem}
>
<View style={styles.goalItem}>
<Text>{props.text}</Text>
</View>
</Pressable>

This works great because:

 On Android, users see the ripple.


 On iOS, users see the opacity change.

🧪 Result
✅ Smooth, consistent press feedback on both iOS and Android.

🔄 Recap: Core Logic


Concept Description
Pressable Wrapper that detects press, long press, and more
android_ripple Android-only ripple effect inside Pressable
style={({ pressed }) => ...} Cross-platform dynamic style based on press state
Padding inside Pressable Helps ripple and press effect show over the right area
pressed prop A boolean indicating if Pressable is currently being pressed

🧭 What’s Next?
Now that your app handles:

 Adding goals ✅
 Deleting goals ✅
 Visual press feedback ✅

You’re ready to:

 Add input validation


 Improve UI with custom components
 Maybe add persistent storage or animations

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, View, FlatList } from 'react-native';

import GoalItem from './components/GoalItem';


import GoalInput from './components/GoalInput';

export default function App() {


const [courseGoals, setCourseGoals] = useState([]);

function addGoalHandler(enteredGoalText) {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}

function deleteGoalHandler(id) {
setCourseGoals((currentCourseGoals) => {
return currentCourseGoals.filter((goal) => goal.id !== id);
});
}

return (
<View style={styles.appContainer}>
<GoalInput onAddGoal={addGoalHandler} />
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return (
<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>
);
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
goalsContainer: {
flex: 5,
},
});

Components/GoalInput.js
--
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';

function GoalInput(props) {
const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
props.onAddGoal(enteredGoalText);
setEnteredGoalText('');
}

return (
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
);
}
export default GoalInput;

const styles = StyleSheet.create({


inputContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '70%',
marginRight: 8,
padding: 8,
},
});

Components/GoalItem.js
--
import { StyleSheet, View, Text, Pressable } from 'react-native';

function GoalItem(props) {
return (
<View style={styles.goalItem}>
<Pressable
android_ripple={{ color: '#210644' }}
onPress={props.onDeleteItem.bind(this, props.id)}
style={({ pressed }) => pressed && styles.pressedItem}
>
<Text style={styles.goalText}>{props.text}</Text>
</Pressable>
</View>
);
}

export default GoalItem;

const styles = StyleSheet.create({


goalItem: {
margin: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
pressedItem: {
opacity: 0.5,
},
goalText: {
color: 'white',
padding: 8,
},
});

📱 React Native Modal Integration for User Input

🎯 Goal:
Improve the app's look and user experience by:

 Moving the input section into a modal overlay.


 Triggering modal visibility via a button.
 Ensuring proper animation and dismissal handling.

🧠 What is a Modal?
A Modal in React Native:

 Is a full-screen overlay.
 Displays temporary content like forms or confirmation dialogs.
 Blocks interaction with the underlying screen until dismissed.
 Comes from the built-in React Native component: Modal.

🧱 Step-by-Step Breakdown
✅ 1. Create a Modal with Input Fields

In your GoalInput component:

import { Modal } from 'react-native';

Wrap the input area (View containing TextInput and Button) with a <Modal> component:

<Modal>
<View>
<TextInput />
<Button title="Add Goal" />
</View>
</Modal>

🔍 Problem: The modal always shows by default, covering the screen and hiding other content.

✅ 2. Control Modal Visibility via State

In App.js:

const [modalIsVisible, setModalIsVisible] = useState(false);

Define a handler to show the modal:

function startAddGoalHandler() {
setModalIsVisible(true);
}

Add a button to open the modal:

<Button
title="Add New Goal"
color="#5e0acc"
onPress={startAddGoalHandler}
/>

Now, only render <GoalInput /> when modalIsVisible is true:

{modalIsVisible && <GoalInput />}

🔍 But this approach hides/shows the entire component, without using the Modal's built-in animation
system.
✅ 3. Use the Modal's visible and animationType Props

Instead of conditionally rendering the component, always mount the modal, and control its visibility
using props:

🧩 GoalInput.js:
<Modal visible={props.visible} animationType="slide">
<View>
<TextInput />
<Button title="Add Goal" />
</View>
</Modal>

 visible: Determines whether the modal is shown.


 animationType: Options include "slide", "fade", or "none".

🧠 Best Practice:

 Let the parent (App.js) manage visibility.


 Pass a visible prop down to GoalInput.

✅ 4. Pass visible Prop from App.js


<GoalInput visible={modalIsVisible} />

This creates a controlled component where the parent manages whether the modal is open.

✨ Result So Far:
 The modal slides up when the button is pressed.
 Input UI appears within a native-feeling modal.
 Reusable, cleanly separated input logic inside GoalInput.

🚫 Still Missing:
 Close button to dismiss the modal.
 Styling to center the input fields inside the modal.
 Optional: Prevent accidental closing via hardware back button.
✅ Additional Improvements (Next Steps)

1. Add a Dismiss Button

In GoalInput, create a function passed from the parent to hide the modal:

function endAddGoalHandler() {
setModalIsVisible(false);
}

Pass this function as a prop:

<GoalInput
visible={modalIsVisible}
onCancel={endAddGoalHandler}
/>

In GoalInput, add a "Cancel" button:

<Button title="Cancel" onPress={props.onCancel} />

Now users can close the modal after opening it.

2. Fix Styling Inside Modal

Since the modal fills the entire screen, we need to center the form:

<View style={styles.inputContainer}>
<TextInput ... />
<View style={styles.buttonContainer}>
<Button title="Add Goal" />
<Button title="Cancel" />
</View>
</View>

Example styles:

inputContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
backgroundColor: '#311b6b',
},
buttonContainer: {
flexDirection: 'row',
marginTop: 16,
gap: 8,
}

Use flex: 1 on the outer container of the modal to take up the full screen.

📚 Summary of Key Concepts


Concept Explanation
Modal Component Native overlay used for displaying temporary UI
visible prop Controls whether modal is shown (true) or hidden (false)
animationType prop Adds smooth transition (slide, fade, or none)
State Management Use useState() to control modal visibility from parent
Lifting State Up Parent controls the visibility and passes props to children
UI Structure Keep the modal UI in its own component (like GoalInput) for reusability
Cancel Button Allows users to exit modal without taking an action

💡 Expert Insights
 Use controlled components: the modal's visibility should be controlled by React state.
 Avoid hiding/unmounting modals directly with {modalIsVisible && <Modal />}; prefer
controlling visible.
 Modal is especially useful for:
o Form input
o Confirmation dialogs
o Temporary actions (filters, prompts, etc.)
 For more control, create custom animations with Animated API or libraries like react-
native-reanimated.

📱 React Native Modal UI Styling and Control – Expert


Notes
🎯 Objective:
 Polish the modal UI for input.
 Rearrange buttons.
 Implement button styling (within the limits of React Native’s built-in <Button>).
 Add Cancel functionality to close the modal.

🧩 Structure Overview
In the GoalInput.js component:

<Modal>
<View style={styles.inputContainer}>
<TextInput />
<View style={styles.buttonContainer}>
<View style={styles.button}><Button title="Add Goal" /></View>
<View style={styles.button}><Button title="Cancel" /></View>
</View>
</View>
</Modal>

This structure helps organize layout and enables style control over otherwise limited components like
<Button>.

✅ Step-by-Step Breakdown

1. 🛠 Organizing the Layout

✅ Add a Container View for Buttons

Place both "Add Goal" and "Cancel" buttons below the TextInput, inside a horizontal View.

<View style={styles.buttonContainer}>
<View style={styles.button}>
<Button title="Add Goal" />
</View>
<View style={styles.button}>
<Button title="Cancel" />
</View>
</View>
🔍 Why wrap buttons in Views?

Because React Native’s built-in <Button> doesn’t support style. Wrapping them in View lets you:

 Set width
 Add spacing (margins)
 Align them horizontally
 Control layout using Flexbox

2. 🎨 Style the Modal UI

✅ inputContainer (Root View inside Modal)

This is the main wrapper.

inputContainer: {
flex: 1, // Occupy full screen height
justifyContent: 'center', // Vertically center content
alignItems: 'center', // Horizontally center content
padding: 16, // Adds breathing room around content
backgroundColor: '#311b6b', // Optional: background color for modal
}

💡 flexDirection: 'column' is default – so no need to explicitly set it.

✅ buttonContainer (Holds both buttons side-by-side)


buttonContainer: {
flexDirection: 'row', // Places children horizontally
marginTop: 16, // Adds spacing between text input and buttons
}

✅ button (Each button wrapper)


button: {
width: 100, // Fixed width to make buttons equal
marginHorizontal: 8, // Adds spacing between buttons
}

💡 You could also use % width (like 30–40%), but fixed width provides more control and consistency
across screen sizes.
✅ TextInput Styling

Make TextInput take full width inside the centered View.

textInput: {
borderWidth: 1,
borderColor: '#e4d0ff',
backgroundColor: '#e4d0ff',
color: '#120438',
borderRadius: 6,
width: '100%', // Full width of parent container
padding: 8,
}

🧠 Design Considerations
Element Style Feature Purpose
flex: 1 Expand modal height Ensure the modal occupies full screen
Make the modal content appear in the center of
justifyContent: 'center' Center vertically
the screen
alignItems: 'center' Center horizontally Line up all input/buttons properly
marginTop on Space between
Prevent crowding
buttonContainer elements
padding on root container Overall spacing Prevent content from touching screen edges
Since Button doesn't support custom
Wrapping buttons in Views Styling workaround
width/margin directly

❌ Common Pitfalls Avoided


 ❌ Don’t apply styles directly to Button — it won’t work!
 ✅ Use wrapper Views to style button layout.
 ✅ Don't set flexDirection: 'column' unless overriding default.
 ✅ Ensure modal takes full height to allow consistent alignment.

✅ Final Touch – Closing the Modal


Until now, pressing Cancel or Add Goal doesn’t actually close the modal.
Step 1: Pass a function from App.js that hides the modal
function endAddGoalHandler() {
setModalIsVisible(false);
}

Step 2: Pass this function as a prop to GoalInput:


<GoalInput
visible={modalIsVisible}
onCancel={endAddGoalHandler}
/>

Step 3: In GoalInput, trigger the function when Cancel is pressed:


<Button title="Cancel" onPress={props.onCancel} />

For Add Goal, you may also want to hide the modal after adding the goal:

<Button
title="Add Goal"
onPress={() => {
props.onAddGoal(enteredGoalText); // Add goal
props.onCancel(); // Close modal
}}
/>

🧠 Expert Notes and Best Practices

✅ Use Built-in Modal Properly:

 Always use the visible prop for showing/hiding.


 Use animationType="slide" or "fade" for better UX.

✅ Avoid Styling Limitations:

 <Button> components are limited.


 Wrap them in styled Views for layout.
 If you need more control (colors, fonts, etc.), use Pressable.

✅ Clean, Maintainable Code

 Break layout into styled containers.


 Use descriptive style names like inputContainer, buttonContainer, etc.
 Move styles to StyleSheet.create() for performance and organization.
✅ Summary
Feature Purpose
Modal Overlay for user input
visible prop Show/hide modal
animationType Slide or fade modal
Flexbox + View Wrapping Create responsive button layouts
Prop Drilling Control modal from parent component
View Wrapping around Button Overcome Button's styling limitations

Ex:app.js
--
import { useState } from 'react';
import { StyleSheet, View, FlatList, Button } from 'react-native';

import GoalItem from './components/GoalItem';


import GoalInput from './components/GoalInput';

export default function App() {


const [modalIsVisible, setModalIsVisible] = useState(false);
const [courseGoals, setCourseGoals] = useState([]);

function startAddGoalHandler() {
setModalIsVisible(true);
}

function addGoalHandler(enteredGoalText) {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}

function deleteGoalHandler(id) {
setCourseGoals((currentCourseGoals) => {
return currentCourseGoals.filter((goal) => goal.id !== id);
});
}

return (
<View style={styles.appContainer}>
<Button
title="Add New Goal"
color="#5e0acc"
onPress={startAddGoalHandler}
/>
<GoalInput visible={modalIsVisible} onAddGoal={addGoalHandler} />
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return (
<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>
);
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
goalsContainer: {
flex: 5,
},
});

Components/GoalInput.js
--
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet, Modal } from 'react-native';
function GoalInput(props) {
const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
props.onAddGoal(enteredGoalText);
setEnteredGoalText('');
}

return (
<Modal visible={props.visible} animationType="slide">
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>
<View style={styles.buttonContainer}>
<View style={styles.button}>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.button}>
<Button title="Cancel" />
</View>
</View>
</View>
</Modal>
);
}

export default GoalInput;

const styles = StyleSheet.create({


inputContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '100%',
padding: 8,
},
buttonContainer: {
marginTop: 16,
flexDirection: 'row',
},
button: {
width: 100,
marginHorizontal: 8
}
});

🚀 Expert Notes: Controlling Modal Visibility in React


Native

📌 Objective:
Implement logic so that the modal closes when either:

 ✅ "Add Goal" button is pressed.


 ❌ "Cancel" button is pressed.

📚 Core Concepts You’ll Learn:


1. 🔄 Unidirectional data flow in React Native
2. 🧠 Props drilling between components
3. 📦 State lifting – managing UI state from a parent
4. 🧩 Event handling between components
🧩 High-Level Architecture Recap:
 App.js
Owns state like modalIsVisible.
 GoalInput.js
Displays a <Modal> and contains the "Add Goal" and "Cancel" buttons.

🎯 Goal: Close Modal from Both Buttons

🧠 Key Principle:

Only the parent (App.js) should control the modal’s visibility.


So any action in GoalInput must trigger a function passed from App.js to update the modal visibility.

🛠 Implementation: Step-by-Step

✅ Step 1: Declare a Close Function in App.js


const [modalIsVisible, setModalIsVisible] = useState(false);

function endAddGoalHandler() {
setModalIsVisible(false); // 🔴 This will hide the modal
}

 This function controls the visibility of the modal.


 Will be reused by both "Add Goal" and "Cancel" actions.

✅ Step 2: Pass endAddGoalHandler to GoalInput as a Prop


<GoalInput
visible={modalIsVisible}
onAddGoal={addGoalHandler}
onCancel={endAddGoalHandler}
/>

We are now "lifting state up" and passing down control functions via props.
✅ Step 3: Use onCancel Prop in GoalInput for Cancel Button

In GoalInput.js:

<Button title="Cancel" onPress={props.onCancel} />

 Now pressing "Cancel" triggers the function from App.js.


 That function sets modalIsVisible to false, hiding the modal.

✅ Step 4: Also Close Modal After Adding a Goal

In GoalInput.js, wherever you're calling the prop function to submit a goal:

props.onAddGoal(enteredGoalText); // adds goal


props.onCancel(); // closes modal

We reuse the same endAddGoalHandler for both cases to avoid repeating code.

🧪 Full Logic Flow Breakdown


Action Handler Triggered Function Called Result
onPress on Cancel props.onCancel() →
Tap "Cancel" Modal closes
Button endAddGoalHandler()
Tap "Add onPress on Add props.onAddGoal() to add goal → Modal closes, and
Goal" Button props.onCancel() goal is added

💡 Why It Works
React and React Native rely on unidirectional data flow:

 App.js owns state.


 GoalInput uses props to send events up to the parent.

So, any change in UI (like closing a modal) needs to be handled upstream in the parent component.
✅ Final Summary
Component Responsibility
App.js Manages modal state (modalIsVisible)
GoalInput.js Shows modal UI and handles local input
endAddGoalHandler Closes modal – called from both buttons
onAddGoal Adds the goal and calls onCancel() to close modal

🧠 Expert Tips
 🔁 Reuse functions like endAddGoalHandler across events (DRY principle).
 🎛 Always control modal visibility from one place (usually parent).
 🎯 Keep components dumb where possible (GoalInput shouldn't hold modal state).

EX:app.js
--
import { useState } from 'react';
import { StyleSheet, View, FlatList, Button } from 'react-native';

import GoalItem from './components/GoalItem';


import GoalInput from './components/GoalInput';

export default function App() {


const [modalIsVisible, setModalIsVisible] = useState(false);
const [courseGoals, setCourseGoals] = useState([]);

function startAddGoalHandler() {
setModalIsVisible(true);
}

function endAddGoalHandler() {
setModalIsVisible(false);
}

function addGoalHandler(enteredGoalText) {
setCourseGoals((currentCourseGoals) => [
...currentCourseGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
endAddGoalHandler();
}
function deleteGoalHandler(id) {
setCourseGoals((currentCourseGoals) => {
return currentCourseGoals.filter((goal) => goal.id !== id);
});
}

return (
<View style={styles.appContainer}>
<Button
title="Add New Goal"
color="#5e0acc"
onPress={startAddGoalHandler}
/>
<GoalInput
visible={modalIsVisible}
onAddGoal={addGoalHandler}
onCancel={endAddGoalHandler}
/>
<View style={styles.goalsContainer}>
<FlatList
data={courseGoals}
renderItem={(itemData) => {
return (
<GoalItem
text={itemData.item.text}
id={itemData.item.id}
onDeleteItem={deleteGoalHandler}
/>
);
}}
keyExtractor={(item, index) => {
return item.id;
}}
alwaysBounceVertical={false}
/>
</View>
</View>
);
}

const styles = StyleSheet.create({


appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
goalsContainer: {
flex: 5,
},
});

Components/GoalInput.js
--
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet, Modal } from 'react-native';

function GoalInput(props) {
const [enteredGoalText, setEnteredGoalText] = useState('');

function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}

function addGoalHandler() {
props.onAddGoal(enteredGoalText);
setEnteredGoalText('');
}

return (
<Modal visible={props.visible} animationType="slide">
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your course goal!"
onChangeText={goalInputHandler}
value={enteredGoalText}
/>
<View style={styles.buttonContainer}>
<View style={styles.button}>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.button}>
<Button title="Cancel" onPress={props.onCancel} />
</View>
</View>
</View>
</Modal>
);
}
export default GoalInput;

const styles = StyleSheet.create({


inputContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#cccccc',
},
textInput: {
borderWidth: 1,
borderColor: '#cccccc',
width: '100%',
padding: 8,
},
buttonContainer: {
marginTop: 16,
flexDirection: 'row',
},
button: {
width: 100,
marginHorizontal: 8
}
});

Components/GoalInput.js
--
import { StyleSheet, View, Text, Pressable } from 'react-native';

function GoalItem(props) {
return (
<View style={styles.goalItem}>
<Pressable
android_ripple={{ color: '#210644' }}
onPress={props.onDeleteItem.bind(this, props.id)}
style={({ pressed }) => pressed && styles.pressedItem}
>
<Text style={styles.goalText}>{props.text}</Text>
</Pressable>
</View>
);
}

export default GoalItem;

const styles = StyleSheet.create({


goalItem: {
margin: 8,
borderRadius: 6,
backgroundColor: '#5e0acc',
},
pressedItem: {
opacity: 0.5,
},
goalText: {
color: 'white',
padding: 8,
},
});

📌 Objective:
Enhance the visual design of the app by:

 ✅ Adding and displaying a local image


 🎨 Changing background colors
 🧼 Refining component layout and spacing

📚 Concepts You'll Master:


Concept Description
Image Component Display static images from local assets
require() Method to link local files into JSX
Relative Pathing Correct use of ../ to reference files
Styling Image Using style prop to control size and spacing
Layout Fixes Enhancing the overall UI and background contrast
Modal Styling Nesting views for custom-styled modal content
🧩 Key Component: Image

🧠 Why We Use It:

React Native provides an <Image /> component (like <img> in HTML) to display both:

 Static assets (local)


 Network images (URLs)

🛠 Step-by-Step Implementation Guide:

✅ Step 1: Create an Image Folder Structure

Create a new directory for images:

project-root/

├── assets/
│ └── images/
│ └── goal.png ← Add your image here

This keeps static assets organized and scalable.

✅ Step 2: Import Image in JSX with require()

React Native requires local image assets to be imported using require() (not just string paths like
HTML).

In your GoalInput.js:

<Image source={require('../assets/images/goal.png')} style={styles.image} />

../ means: "go up one folder" → from components/ into assets/images/goal.png.

❗ Why require() and Not a String?


<Image source="assets/images/goal.png" /> ❌ Won’t work
Unlike HTML/React (which uses URLs or imports), React Native doesn't parse paths as strings for
static resources.

✅ Step 3: Style the Image

Add a style object in your StyleSheet:

image: {
width: 100,
height: 100,
margin: 20,
},

Attach this to the image:

<Image source={require(...)} style={styles.image} />

✅ Step 4: Update Modal Background to Show the Image

Initially, you might not see the image because the image background and modal background are
both white.

Solution: change the modal's main container background color.

Update your root view style in GoalInput.js:

inputContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
backgroundColor: '#311b6b', // Dark purple
},

Now the image will stand out.

🔧 Clean Up Extra Styles (Optional but Recommended)


 🧽 Remove borderBottomWidth from input styles (it looks out of place in a modal).
 🎯 Remove marginBottom if there's no content below it.
 🧼 Result: Clean, centered modal UI.
✅ Optional Improvements

🎨 Improve Text Input Text Color:

To match the dark background:

textInput: {
borderWidth: 1,
borderColor: '#cccccc',
backgroundColor: '#e4d0ff',
color: '#120438', // dark text on light background
padding: 8,
width: '100%',
},

🎨 Improve Button Style Compatibility

Since default buttons don't allow deep styling:

 Wrap them in styled View containers (as shown in previous notes)


 Or switch to custom buttons with Pressable if full control is needed

🧪 Troubleshooting Image Not Showing


Problem Cause Fix
Image not visible White image on white background Change background color
Error loading image Path is incorrect Ensure correct relative path in require()
Image too big/small No width or height Add style with fixed dimensions

🧠 Pro Tips for React Native Assets


1. ✅ Always use require() for local images.
2. 🗂 Store all static files in an organized /assets/images/ folder.
3. 🧩 Image dimensions are required – unlike web, React Native won't auto-scale.
4. 🎨 Contrast is key – always test visibility of images on backgrounds.
5. 🔄 Image is just another View – treat it like any other component for layout.
✅ Summary: What You Learned
Feature Implementation
Display image <Image source={require(...)} />
Image location assets/images/goal.png
Styling style prop with width, height, margin
Background color Set in View wrapping the modal
Path rules Must use require() with correct relative path

You might also like