Topic 5 of 15 · React Native

Topic 5 : state and props

Lesson TL;DRTopic 5: State and Props 📖 7 min read · 🎯 beginner · 🧭 Prerequisites: windowsinstallation, runningemulatorandsimulator Why this matters Here's the thing — most apps you use every day aren't just sh...
7 min read·beginner·react-native · state · props · hooks

Topic 5: State and Props

📖 7 min read · 🎯 beginner · 🧭 Prerequisites: windows-installation, running-emulator-and-simulator

Why this matters

Here's the thing — most apps you use every day aren't just showing you fixed text. A counter goes up when you tap it. A username shows your name, not someone else's. A cart total updates the moment you add something. All of that living, breathing behavior in React Native comes down to exactly two ideas: state and props. State is data that belongs to a component and can change. Props is data passed in from outside. Understand both, and you'll understand how every dynamic screen in React Native actually works.

What You'll Learn

  • How to use the useState hook to track and update changing data inside a component
  • How to pass data between components using props
  • How to lift state into a parent component and share it with children
  • How to pass functions as props so child components can trigger parent logic
  • How to manage complex, object-shaped state safely with functional updates

The Analogy

Think of a React Native component like a vending machine. The state is the internal inventory — how many cans of each drink are left. It lives inside the machine and changes every time someone makes a selection. Props are the labels on the buttons on the outside — they tell the machine what options to display and are set by whoever manufactured it. You can read the labels, but you can't rewrite them from inside the machine. The machine owner (the parent component) decides what labels to put on, while the machine itself (the child component) manages its own internal stock. When the inventory hits zero, the display updates — just like a component re-renders when state changes.

Chapter 1: Introduction to State

State is a built-in mechanism that lets a component keep track of data that changes over time. When state is updated, React Native automatically re-renders the component so the UI reflects the new value.

In functional components, state is managed with the useState hook. It returns a tuple: the current value and a setter function.

const [value, setValue] = useState(initialValue);

Example: Counter Component

CounterComponent.js demonstrates state tracking with increment and decrement buttons.

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

const CounterComponent = () => {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Count: {count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
      <Button title="Decrement" onPress={() => setCount(count - 1)} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 8,
    alignItems: 'center',
  },
  text: {
    fontSize: 24,
    marginBottom: 10,
  },
});

export default CounterComponent;

Key observations:

  • useState(0) initializes count to 0
  • Calling setCount(count + 1) schedules a re-render with the updated value
  • The Text component automatically displays the latest count on every render

Chapter 2: Introduction to Props

Props (short for properties) are read-only values passed from a parent component to a child. They make components reusable — the same component can display different content depending on what props it receives.

Props are passed like HTML attributes in JSX and destructured in the function signature.

// Parent passes a prop
<GreetingComponent name="Vizag Developer" />

// Child receives it
const GreetingComponent = ({ name }) => { ... };

Example: Greeting Component

GreetingComponent.js accepts a name prop and renders a personalized message.

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

const GreetingComponent = ({ name }) => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Hello, {name}!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#e0e0e0',
    borderRadius: 8,
  },
  text: {
    fontSize: 18,
    color: '#555',
  },
});

export default GreetingComponent;

Because name is a prop, GreetingComponent has no knowledge of where it came from — it just renders whatever it receives. Swap the name value in the parent and the greeting changes instantly.

Chapter 3: Combining State and Props

The real power emerges when a parent component manages state and passes pieces of that state down to child components as props. This is called "lifting state up."

Step 1: Create a Parent Component

ParentComponent.js owns a name value in state and passes it as a prop to GreetingComponent. A button lets the parent update that state, which propagates down automatically.

import React, { useState } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import CounterComponent from './CounterComponent';
import GreetingComponent from './GreetingComponent';

const ParentComponent = () => {
  const [name, setName] = useState('Vizag Developer');

  return (
    <View style={styles.container}>
      <GreetingComponent name={name} />
      <Button title="Change Name" onPress={() => setName('React Native Developer')} />
      <CounterComponent />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#f0f0f0',
    borderRadius: 8,
  },
});

export default ParentComponent;

Step 2: Integrate the Parent Component in App

App.js imports ParentComponent and renders it inside a ScrollView.

import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import ParentComponent from './ParentComponent';

export default function App() {
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <ParentComponent />
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
  },
});

The data flow is strictly one-directional: ParentComponentGreetingComponent. The child never mutates the prop directly; it just displays whatever the parent sends.

Chapter 4: Advanced Usage — Functions as Props and Complex State

Passing Functions as Props

Props aren't limited to data — you can pass functions as props too. This gives child components a way to communicate back up to the parent without breaking the one-way data flow.

ChildComponent.js receives an onButtonPress handler and calls it when the button is tapped.

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

const ChildComponent = ({ onButtonPress }) => {
  return (
    <View style={styles.container}>
      <Button title="Press Me" onPress={onButtonPress} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#d0d0d0',
    borderRadius: 8,
  },
});

export default ChildComponent;

The child doesn't know what onButtonPress does — it just calls it. The parent defines the behavior. This separation keeps components focused and reusable.

Managing Complex State

When state is an object with multiple properties, always use a functional update with the spread operator to preserve existing fields while updating only the ones that changed.

ComplexStateComponent.js tracks a user object with name and age fields. The increaseAge function uses prevUser to ensure the update is based on the latest state snapshot.

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

const ComplexStateComponent = () => {
  const [user, setUser] = useState({ name: 'John', age: 30 });

  const increaseAge = () => {
    setUser((prevUser) => ({ ...prevUser, age: prevUser.age + 1 }));
  };

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Name: {user.name}</Text>
      <Text style={styles.text}>Age: {user.age}</Text>
      <Button title="Increase Age" onPress={increaseAge} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 8,
    alignItems: 'center',
  },
  text: {
    fontSize: 18,
    marginBottom: 10,
  },
});

export default ComplexStateComponent;

Why use the functional form setUser((prevUser) => ...)? React batches state updates; using the previous state argument guarantees you're working from the latest value even if multiple updates are queued.

Chapter 5: Putting It All Together

The final App.js brings all four components together — ParentComponent, ChildComponent, and ComplexStateComponent — inside a single scrollable screen.

import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import ParentComponent from './ParentComponent';
import ChildComponent from './ChildComponent';
import ComplexStateComponent from './ComplexStateComponent';

export default function App() {
  const handleButtonPress = () => {
    alert('Button pressed in Child Component!');
  };

  return (
    <ScrollView contentContainerStyle={styles.container}>
      <ParentComponent />
      <ChildComponent onButtonPress={handleButtonPress} />
      <ComplexStateComponent />
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
  },
});

The handleButtonPress function is defined in App and passed down to ChildComponent as a prop — a clean example of functions-as-props in action. Each component focuses on its own rendering responsibility; App owns the coordination.

graph TD
  App["App.js"]
  PC["ParentComponent"]
  GC["GreetingComponent\n(prop: name)"]
  CC["CounterComponent\n(state: count)"]
  CH["ChildComponent\n(prop: onButtonPress)"]
  CS["ComplexStateComponent\n(state: user)"]

  App -->|renders| PC
  App -->|renders + passes onButtonPress| CH
  App -->|renders| CS
  PC -->|passes name prop| GC
  PC -->|renders| CC

🧪 Try It Yourself

Task: Build a ProfileCard component that displays a user's name and score. Place it inside a parent that holds both values in state. Add a "Level Up" button in the parent that increases the score by 10 and passes the updated values down as props.

Success criterion: Tapping "Level Up" should visibly increment the score displayed inside ProfileCard without the card managing any state of its own.

Starter snippet:

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

const ProfileCard = ({ name, score }) => (
  <View style={{ padding: 16, backgroundColor: '#eef', borderRadius: 8 }}>
    <Text style={{ fontSize: 20 }}>{name}</Text>
    <Text style={{ fontSize: 16 }}>Score: {score}</Text>
  </View>
);

export default function App() {
  const [score, setScore] = useState(0);

  return (
    <View style={{ padding: 24 }}>
      <ProfileCard name="the trainer" score={score} />
      <Button title="Level Up" onPress={() => setScore(score + 10)} />
    </View>
  );
}

🔍 Checkpoint Quiz

Q1. What happens to a React Native component when you call its state setter function (e.g., setCount(5))?

A) Nothing until the next user interaction
B) The component re-renders with the new state value
C) All components in the app re-render
D) The state is saved to device storage

Q2. Given the following snippet, what will the Text element display after the button is tapped once?

const [count, setCount] = useState(3);
// ...
<Text>{count}</Text>
<Button title="Go" onPress={() => setCount(count + 2)} />

A) 3
B) 4
C) 5
D) 2

Q3. A ChildComponent receives a prop called onPress. Inside the child, is it allowed to reassign that prop directly — e.g., onPress = someOtherFunction? Why or why not?

A) Yes — props are mutable inside the component that receives them
B) No — props are read-only; to change behavior, the parent must pass a new value
C) Yes — but only if the prop is a function
D) No — props cannot be functions at all

Q4. You have a state object { name: 'Ada', score: 100 }. You want to update only score to 150 without losing name. Which setter call is correct?

A) setUser({ score: 150 })
B) setUser((prev) => ({ ...prev, score: 150 }))
C) setUser({ name: 'Ada' }); setUser({ score: 150 })
D) user.score = 150; setUser(user)

A1. B — calling the setter schedules a re-render; React reconciles the virtual DOM and updates only the parts that changed.

A2. C — count starts at 3; pressing the button calls setCount(3 + 2), so the next render shows 5.

A3. B — props flow one way (parent → child) and are read-only inside the receiving component. If the child needs to signal something upward, the parent passes a callback function as a prop.

A4. B — the spread operator copies all existing fields, then score: 150 overwrites only the score. Option A drops name; option D mutates state directly, which React won't detect as a change.

🪞 Recap

  • useState gives a functional component a piece of mutable state; calling the setter triggers a re-render with the new value.
  • Props are read-only values passed from parent to child — they make components reusable and data-driven.
  • Lift state to the nearest common ancestor when multiple children need to read or react to the same value.
  • Functions can be passed as props, giving child components a clean way to invoke parent-level logic.
  • When state is an object, use the functional updater form setState((prev) => ({ ...prev, field: value })) to safely merge changes.

📚 Further Reading

Like this topic? It’s one of 15 in React Native.

Block your seat for ₹2,500 and join the next cohort.