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/pickerto collect user selections - Control status bar style and background color with React Native's
StatusBarAPI - Persist and retrieve data across sessions using
@react-native-async-storage/async-storage - Compose all three components together inside a single
ScrollViewapp
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;itemIndexis available if you need positional logic.Picker.Item— each child represents one row. The four options here arejava,javascript,python, andcpp.
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.backgroundColoronly 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.barStyleaccepts"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')— returnsnullif the key has never been set, so theif (value !== null)guard prevents overwriting the display with nothing.AsyncStorage.setItem('@storage_key', value)— both arguments must be strings. Stringify objects withJSON.stringifybefore storing; parse them back withJSON.parseon retrieval.- Both calls are
async/awaitand wrapped intry/catchbecause storage I/O can fail (disk full, permissions, etc.). - The key
@storage_keyis 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.
- Add a second button labeled
"Clear Value". - On press, call
AsyncStorage.removeItem('@storage_key')and reset bothvalueandstoredValuestate to''. - 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/pickerand use<Picker>with<Picker.Item>children to render a native selection control driven by React state. - React Native's built-in
StatusBarcomponent controls bar style declaratively on mount; callStatusBar.setBarStyle()imperatively to change it at runtime. - Install
@react-native-async-storage/async-storagefor persistent key-value storage; always wrapgetItem/setItem/removeItemcalls intry/catchand check fornullon reads. - Serialize JavaScript objects with
JSON.stringifybefore storing andJSON.parseafter reading, because AsyncStorage values must be strings. - Compose all three components inside a
ScrollViewusingcontentContainerStylewithflexGrow: 1so the layout adapts to any screen height.
📚 Further Reading
- Official React Native StatusBar docs — the source of truth on all props and imperative methods
- @react-native-picker/picker GitHub — installation, platform-specific props, and changelog
- @react-native-async-storage/async-storage docs — full API reference including
mergeItem,multiGet, andgetAllKeys - AsyncStorage migration guide — covers moving from the deprecated built-in
AsyncStorageto the community package - ⬅️ Previous: Reading Post Data
- ➡️ Next: CRUD Operations