Topic 7: Basics (Picker, Status Bar, Async Storage)
📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: state-and-props, basics-stylescomponents-text-inputs-buttons-scrollview-activity-indicator-images-modals
Why this matters
Up until now, your app forgets everything the moment the user closes it. Pick a language, choose a theme, log in — gone. And you've probably never thought about controlling that tiny strip at the top of the phone screen showing the time and battery. These feel like small things, until your app ships without them and it looks unfinished. In this lesson, we cover three essentials: @react-native-picker/picker for dropdown-style selection menus, the StatusBar API to style that top strip, and @react-native-async-storage/async-storage to save data so your app actually remembers the user.
What You'll Learn
- Install and use
@react-native-picker/pickerto render a native dropdown selection menu - Control the status bar's style and background color using the
StatusBarcomponent - Install and use
@react-native-async-storage/async-storageto read and write persistent key-value data - Combine Picker, StatusBar, and AsyncStorage into a single cohesive
App.js
The Analogy
Think of your mobile app as a well-organized café. The Picker is the laminated menu on every table — it shows customers a fixed list of choices and remembers which one they pointed to. The StatusBar is the neon sign in the window: you control whether it glows light or dark to match the mood of the room. And AsyncStorage is the barista's notebook tucked under the counter — even when the café closes and reopens the next morning, the notebook still holds the regulars' orders exactly as they were written. Together, these three tools give your app memory, identity, and a polished interface.
Chapter 1: Using Picker for Selections
The Picker component renders a native dropdown (a spin-wheel on iOS, a dialog/dropdown on Android). Because it was removed from React Native core, you install it from the community package @react-native-picker/picker.
Step 1: Install the package
npm install @react-native-picker/picker
Step 2: Build a PickerComponent
The component holds the currently selected value in state and updates it via onValueChange. Each Picker.Item maps a human-readable label to a machine-friendly 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 prop summary:
| Prop | Purpose |
|---|---|
selectedValue | Controlled value — drives which item appears selected |
onValueChange | Callback (itemValue, itemIndex) => void |
style | Standard RN style object applied to the picker widget |
<Picker.Item> | One option — requires label (display) and value (data) |
Chapter 2: Managing the Status Bar
The StatusBar component is built into React Native core — no install needed. It controls the thin strip at the very top of the screen that shows time, battery, and network indicators.
Key props and methods
barStyle—"dark-content"(dark icons, for light backgrounds) or"light-content"(white icons, for dark backgrounds)backgroundColor— Android only; sets the background color of the status bar stripStatusBar.setBarStyle(style)— imperative call to change style at runtime
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;
The StatusBar JSX element acts as a declarative configuration: React Native applies barStyle and backgroundColor when the component mounts. The imperative StatusBar.setBarStyle() call in toggleStatusBar overrides it at runtime, letting you respond to user actions or theme changes.
Chapter 3: Storing Data with AsyncStorage
AsyncStorage is a simple, unencrypted, asynchronous, persistent key-value store. It was removed from React Native core and lives in the community package @react-native-async-storage/async-storage.
Step 1: Install the package
npm install @react-native-async-storage/async-storage
Step 2: Core API
| Method | Signature | Description |
|---|---|---|
setItem | (key: string, value: string) => Promise<void> | Write a string under a key |
getItem | (key: string) => Promise<string | null> | Read a value; null if absent |
removeItem | (key: string) => Promise<void> | Delete a key |
clear | () => Promise<void> | Wipe all stored data |
Keys are conventionally prefixed with @ (e.g., @storage_key) to namespace your app's data.
Step 3: Build an AsyncStorageComponent
The component loads any previously saved value on mount via useEffect, then provides a TextInput and button to overwrite it.
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;
The useEffect with an empty dependency array [] runs once after first render — perfect for a "load from storage on mount" pattern. Both setItem and getItem return Promises, so async/await with try/catch is the idiomatic error-handling approach.
Chapter 4: Combining All Components in the App
With all three components ready, the class wired them into App.js inside a ScrollView so nothing gets clipped on smaller devices.
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,
},
});
flexGrow: 1 on contentContainerStyle ensures the inner content stretches to fill the screen when there isn't enough content to scroll, while still enabling scrolling when there is.
flowchart TD
A[App.js / ScrollView] --> B[StatusBarComponent]
A --> C[PickerComponent]
A --> D[AsyncStorageComponent]
B -->|StatusBar API| E[Native Status Bar]
C -->|@react-native-picker/picker| F[Native Dropdown]
D -->|@react-native-async-storage/async-storage| G[Persistent Key-Value Store]
🧪 Try It Yourself
Task: Extend AsyncStorageComponent so it also saves the user's selected language from the Picker to AsyncStorage under the key @selected_language, and reloads it on mount.
Success criterion: Kill and relaunch the app. The Picker should restore to whichever language was last selected — not reset to 'java'.
Starter snippet — store the Picker value:
// Inside your combined component, after Picker's onValueChange:
const saveLanguage = async (lang) => {
setSelectedValue(lang);
try {
await AsyncStorage.setItem('@selected_language', lang);
} catch (e) {
console.error('Failed to save language:', e);
}
};
// Inside useEffect, alongside loading @storage_key:
const savedLang = await AsyncStorage.getItem('@selected_language');
if (savedLang !== null) setSelectedValue(savedLang);
🔍 Checkpoint Quiz
Q1. Why was Picker moved out of React Native core into @react-native-picker/picker?
A) It was deprecated and no longer works
B) The React Native team moved platform-specific components to community packages to keep the core lean
C) It only works on Android, not iOS
D) It requires a paid license
Q2. Given this snippet, what will storedValue display the first time the app launches (no previous data saved)?
useEffect(() => {
const load = async () => {
const val = await AsyncStorage.getItem('@storage_key');
if (val !== null) setStoredValue(val);
};
load();
}, []);
A) undefined
B) An empty string, because setStoredValue('') is called
C) Nothing changes — setStoredValue is never called because getItem returns null
D) It throws an error
Q3. What is the bug in this StatusBar usage?
<StatusBar barStyle="dark_content" backgroundColor="#ecf0f1" />
A) backgroundColor is not a valid prop
B) The barStyle value should be "dark-content" with a hyphen, not an underscore
C) StatusBar must be rendered outside the View
D) There is no bug
Q4. You want to store a JavaScript object { name: "Alice", score: 42 } in AsyncStorage. How do you do it correctly?
A) AsyncStorage.setItem('@key', { name: "Alice", score: 42 })
B) AsyncStorage.setItem('@key', JSON.stringify({ name: "Alice", score: 42 })) and retrieve with JSON.parse(await AsyncStorage.getItem('@key'))
C) AsyncStorage supports objects natively; no conversion needed
D) Use AsyncStorage.setObject() instead of setItem()
A1. B — The React Native team extracted platform-specific and non-universal components into community packages to reduce core bundle size and allow faster independent iteration.
A2. C — getItem returns null when the key doesn't exist; the if (val !== null) guard prevents setStoredValue from running, so the initial state ('') is preserved.
A3. B — Valid barStyle values are "dark-content" and "light-content" (hyphen-separated). Using an underscore produces no visible error on some platforms but the style is silently ignored.
A4. B — AsyncStorage only stores strings. You must JSON.stringify before writing and JSON.parse after reading to work with objects.
🪞 Recap
- Install
@react-native-picker/pickerand use<Picker>withselectedValue+onValueChangeto build controlled dropdown selections. - The built-in
StatusBarcomponent controls bar style ("dark-content"/"light-content") and background color; callStatusBar.setBarStyle()for runtime toggling. - Install
@react-native-async-storage/async-storageand usesetItem/getItem(both return Promises) to persist string data across app sessions. - Keys in AsyncStorage are plain strings — prefix with
@by convention; serialize objects withJSON.stringify/JSON.parse. - Compose multiple feature components inside a
ScrollViewwithflexGrow: 1oncontentContainerStyleto handle varying screen sizes.
📚 Further Reading
- Official @react-native-picker/picker docs — props reference, iOS/Android behavior differences
- React Native StatusBar API — full prop and method list from the official docs
- Official @react-native-async-storage/async-storage docs — multi-get, multi-set, merge, and storage limits
- AsyncStorage migration guide — how to migrate from the deprecated core
AsyncStorageto the community package - ⬅️ Previous: Basics (Styles, Components, Text Inputs, Buttons, ScrollView, ActivityIndicator, Images, Modals)
- ➡️ Next: HTTP