Topic 48 of 56 · Full Stack Advanced

Topic 7 : Basics (Picker, Status bar, Async Storage)

Lesson TL;DRTopic 7: Basics (Picker, Status Bar, Async Storage) 📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: jointquerynestedqueryfilteringdata, readingpostdata Why this matters Up until now, you've been b...
7 min read·intermediate·react-native · picker · status-bar · async-storage

Topic 7: Basics (Picker, Status Bar, Async Storage)

📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: joint-query-nested-query-filtering-data, reading-post-data

Why this matters

Up until now, you've been building screens that look fine but feel incomplete — no dropdown to pick an option, no control over that thin bar at the top of the phone, and data that vanishes the moment the app closes. That's frustrating for any user. In this lesson, we're fixing all three at once. You'll learn how the Picker lets users choose from a list, how StatusBar lets you style that top strip on the device, and how AsyncStorage keeps your data alive between sessions — even after the app is closed and reopened.

What You'll Learn

  • Install and render @react-native-picker/picker to collect user selections
  • Control status bar style and background color with React Native's StatusBar API
  • Persist and retrieve data across sessions using @react-native-async-storage/async-storage
  • Compose all three components together inside a single ScrollView app

The Analogy

Think of a React Native app screen like a city kiosk. The Picker is the rotating dial on the kiosk that lets a citizen choose a service from a fixed menu — it handles the interaction and reports back which option was chosen. The StatusBar is the illuminated header strip at the top of the kiosk that changes color to signal open hours or an alert state. AsyncStorage is the kiosk's built-in notepad locked behind a small drawer: you write a note, the kiosk closes for the night, and when it reopens the next morning the note is exactly where you left it — no server required.

Chapter 1: Using Picker for Selections

The Picker component gives users a native dropdown (spinner on Android, wheel on iOS) to pick one value from a list. Because it ships as a separate community package, the first step is installation.

Step 1: Install the Picker package

npm install @react-native-picker/picker

Step 2: Build the PickerComponent

The component holds the currently selected value in local state and updates it through the onValueChange callback. Each selectable option is declared as a <Picker.Item> child with a human-readable label and a machine-readable value.

PickerComponent.js

import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Picker } from '@react-native-picker/picker';

const PickerComponent = () => {
  const [selectedValue, setSelectedValue] = useState('java');

  return (
    <View style={styles.container}>
      <Text style={styles.label}>Select a language:</Text>
      <Picker
        selectedValue={selectedValue}
        style={styles.picker}
        onValueChange={(itemValue, itemIndex) => setSelectedValue(itemValue)}
      >
        <Picker.Item label="Java" value="java" />
        <Picker.Item label="JavaScript" value="javascript" />
        <Picker.Item label="Python" value="python" />
        <Picker.Item label="C++" value="cpp" />
      </Picker>
      <Text style={styles.selectedValue}>Selected: {selectedValue}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
  },
  label: {
    fontSize: 18,
    marginBottom: 10,
  },
  picker: {
    height: 50,
    width: 200,
  },
  selectedValue: {
    marginTop: 20,
    fontSize: 16,
  },
});

export default PickerComponent;

Key points:

  • selectedValue — keeps the picker in sync with React state; without it the picker is uncontrolled.
  • onValueChange((itemValue, itemIndex) => ...) — fires whenever the user picks a new item; itemIndex is available if you need positional logic.
  • Picker.Item — each child represents one row. The four options here are java, javascript, python, and cpp.

Chapter 2: Managing the Status Bar

The status bar is the thin system strip at the very top of a mobile screen showing time, battery, and signal. React Native exposes the StatusBar component (no extra install needed — it ships with react-native) to let you control its appearance imperatively or declaratively.

StatusBarComponent.js

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

const StatusBarComponent = () => {
  const toggleStatusBar = () => {
    const currentStyle = StatusBar._currentValues.style.value;
    StatusBar.setBarStyle(
      currentStyle === 'dark-content' ? 'light-content' : 'dark-content'
    );
  };

  return (
    <View style={styles.container}>
      <StatusBar barStyle="dark-content" backgroundColor="#ecf0f1" />
      <Text style={styles.text}>Status Bar Example</Text>
      <Button title="Toggle Status Bar" onPress={toggleStatusBar} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ecf0f1',
  },
  text: {
    fontSize: 18,
    marginBottom: 20,
  },
});

export default StatusBarComponent;

Key points:

  • Declarative (<StatusBar barStyle="dark-content" backgroundColor="#ecf0f1" />) — sets the initial style when the component mounts. backgroundColor only takes effect on Android.
  • Imperative (StatusBar.setBarStyle(...)) — lets you change the bar style at runtime, e.g. when a button is pressed or a theme changes.
  • StatusBar._currentValues.style.value — reads the live bar style so the toggle knows which direction to flip.
  • barStyle accepts "default", "dark-content" (dark icons, use on light backgrounds), or "light-content" (light icons, use on dark backgrounds).

Chapter 3: Storing Data with AsyncStorage

AsyncStorage is a simple, unencrypted, asynchronous key-value store for React Native. It persists data to disk, so it survives app restarts — unlike component state or Redux store. Because it moved to a community package, you must install it separately.

Step 1: Install AsyncStorage

npm install @react-native-async-storage/async-storage

Step 2: Build the AsyncStorageComponent

The component does two things: it reads the stored value on mount (via useEffect) and writes a new value when the user presses Save.

AsyncStorageComponent.js

import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const AsyncStorageComponent = () => {
  const [value, setValue] = useState('');
  const [storedValue, setStoredValue] = useState('');

  useEffect(() => {
    const getStoredValue = async () => {
      try {
        const value = await AsyncStorage.getItem('@storage_key');
        if (value !== null) {
          setStoredValue(value);
        }
      } catch (e) {
        console.error('Failed to load value:', e);
      }
    };
    getStoredValue();
  }, []);

  const storeValue = async () => {
    try {
      await AsyncStorage.setItem('@storage_key', value);
      setStoredValue(value);
    } catch (e) {
      console.error('Failed to save value:', e);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Stored Value: {storedValue}</Text>
      <TextInput
        style={styles.input}
        placeholder="Enter value"
        value={value}
        onChangeText={setValue}
      />
      <Button title="Save Value" onPress={storeValue} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
  },
  text: {
    fontSize: 18,
    marginBottom: 20,
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    width: '100%',
    marginBottom: 20,
    paddingHorizontal: 8,
  },
});

export default AsyncStorageComponent;

Key points:

  • AsyncStorage.getItem('@storage_key') — returns null if the key has never been set, so the if (value !== null) guard prevents overwriting the display with nothing.
  • AsyncStorage.setItem('@storage_key', value) — both arguments must be strings. Stringify objects with JSON.stringify before storing; parse them back with JSON.parse on retrieval.
  • Both calls are async/await and wrapped in try/catch because storage I/O can fail (disk full, permissions, etc.).
  • The key @storage_key is a convention. The @ prefix is community-recommended to namespace keys and avoid collisions with other libraries.

Chapter 4: Combining All Components in the App

With all three components built, the class assembled them inside a single ScrollView in App.js. ScrollView with flexGrow: 1 ensures the layout handles overflow on smaller screens without clipping any component.

App.js

import React from 'react';
import { StyleSheet, ScrollView, View } from 'react-native';
import PickerComponent from './PickerComponent';
import StatusBarComponent from './StatusBarComponent';
import AsyncStorageComponent from './AsyncStorageComponent';

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

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

contentContainerStyle (not style) is the correct prop for styling the inner scroll container. Using flexGrow: 1 instead of flex: 1 lets the container expand beyond the viewport when content is tall while still filling the screen when it is short.

graph TD
  A[App.js ScrollView] --> B[StatusBarComponent]
  A --> C[PickerComponent]
  A --> D[AsyncStorageComponent]
  B -->|StatusBar API| E[Device Status Bar]
  C -->|@react-native-picker/picker| F[Dropdown Selection]
  D -->|@react-native-async-storage/async-storage| G[On-Disk Key-Value Store]

🧪 Try It Yourself

Task: Extend AsyncStorageComponent so it also supports deleting the stored value.

  1. Add a second button labeled "Clear Value".
  2. On press, call AsyncStorage.removeItem('@storage_key') and reset both value and storedValue state to ''.
  3. Success criterion: After pressing Clear Value, the "Stored Value:" label should go blank, and reloading the app should still show nothing stored.

Starter snippet — add this function and button to AsyncStorageComponent.js:

const clearValue = async () => {
  try {
    await AsyncStorage.removeItem('@storage_key');
    setValue('');
    setStoredValue('');
  } catch (e) {
    console.error('Failed to clear value:', e);
  }
};

// Inside the return, below the existing <Button>:
<Button title="Clear Value" onPress={clearValue} color="red" />

🔍 Checkpoint Quiz

Q1. Why does AsyncStorage.getItem return null instead of throwing an error when a key does not exist?

A) It throws a silent exception caught internally
B) null is the conventional signal that a key was never written, allowing callers to distinguish "missing" from an empty string
C) The function always returns null on first run regardless
D) It is a bug in the library that was never fixed

Q2. Given the following snippet, what does the Picker display to the user when selectedValue is "cpp"?

<Picker.Item label="C++" value="cpp" />

A) "cpp"
B) "C++"
C) Nothing — value is for internal use only and the label must match it exactly
D) An error is thrown because "cpp" is not a valid value

Q3. What is wrong with the following status bar toggle logic on a screen with a dark background?

StatusBar.setBarStyle('dark-content');

A) setBarStyle is not a valid method
B) dark-content renders dark icons, which would be invisible against a dark background — light-content should be used instead
C) The method requires a second animated boolean argument or it does nothing
D) Nothing is wrong

Q4. You want to store a JavaScript object { name: "Alice", score: 42 } in AsyncStorage. How do you write and later read it correctly?

A1. B — null is the deliberate signal for a missing key. This lets you distinguish "never set" (null) from "set to empty string" (""), which would be indistinguishable if the function threw an error.

A2. B — label is the human-readable string rendered in the picker UI. value is the machine value passed to onValueChange. When selectedValue is "cpp", the picker highlights the item whose value matches and displays its label, which is "C++".

A3. B — dark-content produces dark (black) icons. On a dark background they become invisible or hard to read. Use light-content for dark backgrounds so the icons appear white.

A4. Write: await AsyncStorage.setItem('@my_key', JSON.stringify({ name: "Alice", score: 42 })). Read: const raw = await AsyncStorage.getItem('@my_key'); const obj = raw !== null ? JSON.parse(raw) : null;. AsyncStorage only stores strings, so serialization with JSON.stringify and deserialization with JSON.parse are required for objects.

🪞 Recap

  • Install @react-native-picker/picker and use <Picker> with <Picker.Item> children to render a native selection control driven by React state.
  • React Native's built-in StatusBar component controls bar style declaratively on mount; call StatusBar.setBarStyle() imperatively to change it at runtime.
  • Install @react-native-async-storage/async-storage for persistent key-value storage; always wrap getItem/setItem/removeItem calls in try/catch and check for null on reads.
  • Serialize JavaScript objects with JSON.stringify before storing and JSON.parse after reading, because AsyncStorage values must be strings.
  • Compose all three components inside a ScrollView using contentContainerStyle with flexGrow: 1 so the layout adapts to any screen height.

📚 Further Reading

Like this topic? It’s one of 56 in Full Stack Advanced.

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