Understanding Local and Global State in React Native
Course: React Native Training
Topic: Understanding Local and Global State
Course Link: React Native Training Course
Demo Link: Register for a Course Demo
JustAcademy: Visit JustAcademy
1. Introduction to State Management in React Native
State management is an essential concept in React Native development. It determines how an application stores, updates, and shares information while users interact with the interface. Examples of state include a text entered in a form, a selected product, a logged-in user's information, a shopping cart, a theme preference, and data received from an API.
In React Native, components can use state to display changing information. When state changes, React can render the component again so that the user interface reflects the latest data.
State is commonly divided into two categories:
- Local state: Data managed within a component or a small part of the component tree.
- Global state: Data shared across multiple components or screens of an application.
Choosing the right type of state helps keep an application maintainable, predictable, and efficient. Not every value needs to be global. A simple input field may need only local state, while authentication information or a shared cart may need to be accessible across several screens.
Learning Objectives
- Understand the meaning of state and state management.
- Differentiate local state from global state.
- Use the
useState Hook to manage local state.
- Share state through props and React Context.
- Understand common global state management tools.
- Update objects and arrays without mutating existing state.
- Choose an appropriate state management strategy for a React Native application.
2. What Is State in React Native?
State is data that a component remembers between renders. It can change because of user interactions, network responses, timers, navigation events, or other application actions.
For example, a counter application stores the current count in state. When a user presses the increment button, the count changes and the displayed number updates.
Example: Counter Using Local State
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
export default function CounterScreen() {
const [count, setCount] = useState(0);
return (
<View>
<Text>Count: {count}</Text>
<Button
title="Increase"
onPress={() => setCount(previous => previous + 1)}
/>
</View>
);
}
Explanation
useState(0) creates state with an initial value of zero.
count represents the current state value.
setCount is the function used to request a state update.
- The functional update receives the previous value and returns the next value.
- React renders the component again when the state update is processed.
State should be updated using its setter rather than by directly assigning a new value to the state variable.
3. Understanding Local State
Local state is data managed by an individual component, usually with Hooks such as useState or useReducer. It is appropriate when the data is primarily relevant to one component or one small section of the interface.
Examples of Local State
- Whether a password is visible or hidden.
- Text entered in a search field.
- Whether a dropdown menu is open.
- A selected tab within a component.
- A temporary loading indicator for one screen.
- Validation messages for a registration form.
- The current quantity selected in a product card.
Example: Toggle Password Visibility
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
export default function PasswordVisibility() {
const [visible, setVisible] = useState(false);
return (
<View>
<Text>{visible ? 'Password is visible' : 'Password is hidden'}</Text>
<Button
title={visible ? 'Hide Password' : 'Show Password'}
onPress={() => setVisible(previous => !previous)}
/>
</View>
);
}
The visible value is used only by this component, so local state is a suitable choice. There is no need to introduce a global state library for such a simple interaction.
Advantages of Local State
- Simplicity: It is easy to create and update state within one component.
- Isolation: Unrelated screens do not need to know about the value.
- Maintainability: The logic remains close to the interface that uses it.
- Less configuration: No shared store or provider is needed for simple state.
- Clear ownership: It is easier to identify which component controls the value.
Limitations of Local State
- Other components cannot automatically access the state directly.
- Sharing a value between distant components may require passing props through multiple levels.
- Duplicating the same information in multiple components can create inconsistent values.
- State may reset when its owning component is unmounted and later mounted again.
4. Managing Local State with useState
The useState Hook is the most common way to manage simple component state in functional React Native components.
Syntax
const [state, setState] = useState(initialValue);
Parameters and values:
state: The current value.
setState: The setter function used to update the value.
initialValue: The initial state value used when the component is first initialized.
Example: Form Input State
import React, { useState } from 'react';
import { View, TextInput, Text, Button } from 'react-native';
export default function NameForm() {
const [name, setName] = useState('');
return (
<View>
<TextInput
placeholder="Enter your name"
value={name}
onChangeText={setName}
/>
<Text>Hello, {name || 'Guest'}!</Text>
<Button title="Clear" onPress={() => setName('')} />
</View>
);
}
The input value is controlled by the name state. When the user types, onChangeText calls setName, updating the value displayed in the input and greeting.
State with Objects
When related values belong together, they can be stored in an object. Since React state should be treated as immutable, create a new object when updating a property.
const [profile, setProfile] = useState({
name: '',
email: ''
});
function updateName(value) {
setProfile(previous => ({
...previous,
name: value
}));
}
The spread operator copies the existing properties, while the new name value replaces the previous one. Without the spread operator, the update could accidentally discard other properties such as email.
State with Arrays
const [tasks, setTasks] = useState([]);
function addTask(task) {
setTasks(previous => [...previous, task]);
}
function removeTask(taskId) {
setTasks(previous =>
previous.filter(task => task.id !== taskId)
);
}
The array methods above create new arrays rather than modifying the existing state array. This allows React to detect the update correctly.
5. Understanding Global State
Global state is information that needs to be accessed or updated by multiple components, screens, or feature areas. It is managed at a shared level rather than being owned exclusively by one small component.
Global state can be implemented with React Context, a state management library, or a shared store. The best choice depends on the complexity of the application and how frequently the data changes.
Examples of Global State
- Authenticated user information.
- Shopping cart items shared between product and checkout screens.
- Application theme, such as light mode or dark mode.
- Language or localization preferences.
- Shared notification settings.
- Application-wide feature preferences.
- Data used by multiple screens that must remain consistent.
Advantages of Global State
- Shared access: Multiple components can read the same state.
- Consistency: Components can reflect updates to the same shared value.
- Reduced prop passing: Deeply nested components may not need every intermediate component to pass the data.
- Centralized logic: Related updates can be managed in one shared location.
- Better coordination: Features such as a cart badge and checkout screen can use the same cart data.
Limitations of Global State
- Overusing global state makes application behavior harder to understand.
- Shared updates can cause multiple consumers to render again.
- Global state may create dependencies between unrelated components.
- Large stores require clear organization and testing.
- Sensitive or persistent data requires appropriate security and storage decisions.
6. Local State vs Global State
| Feature |
Local State |
Global State |
| Scope |
One component or a small subtree |
Multiple components or screens |
| Typical tools |
useState, useReducer |
Context, reducers, shared stores, state libraries |
| Setup |
Usually minimal |
May require providers or store configuration |
| Examples |
Input text, modal visibility |
Cart, theme, shared user profile |
| Sharing |
Through props or component composition |
Through a shared context or store |
| Best use |
Values with a narrow ownership scope |
Values needed by several parts of the app |
| Main concern |
Prop drilling when sharing widely |
Unnecessary coupling and broad updates |
How to Decide Which State to Use
- Identify which components need the data.
- If only one component needs it, start with local state.
- If a parent and its children need the value, consider lifting the state to their nearest common parent.
- If many distant components need the same value, consider Context or a shared store.
- Keep server-fetched data, temporary UI state, and persistent user preferences conceptually separate.
- Choose the simplest approach that meets the application's requirements.
7. Sharing State with Props and Lifting State Up
Before introducing global state, React developers often share state by moving it to the nearest common parent component. This technique is called lifting state up.
The parent owns the state and passes the current value and update function to its children through props.
Example: Parent and Child Components
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
function CounterDisplay({ count }) {
return <Text>Current count: {count}</Text>;
}
function CounterButton({ onIncrease }) {
return <Button title="Increase" onPress={onIncrease} />;
}
export default function SharedCounter() {
const [count, setCount] = useState(0);
return (
<View>
<CounterDisplay count={count} />
<CounterButton
onIncrease={() => setCount(previous => previous + 1)}
/>
</View>
);
}
How It Works
- The parent component stores the count.
- The display component receives the count as a prop.
- The button component receives a callback as a prop.
- Pressing the button calls the parent's update function.
- The parent updates the state, and the child receives the new value.
This is often the best solution when a small group of related components needs shared state.
8. Managing Global State with React Context
React Context allows a parent component to provide a value to components below it without passing that value through every intermediate component. It is useful for shared information such as a theme, locale, or current user.
Context itself is a React feature, not a complete replacement for every state management library. A context value can contain state and update functions, but its structure and update behavior should be planned carefully.
Step 1: Create a Context
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
function toggleTheme() {
setTheme(previous =>
previous === 'light' ? 'dark' : 'light'
);
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === null) {
throw new Error('useTheme must be used inside ThemeProvider');
}
return context;
}
Step 2: Wrap the Application
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import HomeScreen from './HomeScreen';
export default function App() {
return (
<ThemeProvider>
<HomeScreen />
</ThemeProvider>
);
}
Step 3: Read and Update the Context
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useTheme } from './ThemeContext';
export default function HomeScreen() {
const { theme, toggleTheme } = useTheme();
return (
<View>
<Text>Current theme: {theme}</Text>
<Button title="Toggle Theme" onPress={toggleTheme} />
</View>
);
}
When the user toggles the theme, the shared context value changes and consuming components can render using the new theme.
Important Context Best Practices
- Place providers at the lowest level that includes all required consumers.
- Keep unrelated state in separate contexts when it improves organization.
- Avoid placing rapidly changing values into a very broad context without considering render performance.
- Use a custom Hook to make context access easier and detect missing providers.
- Do not assume that Context automatically persists values after the app closes.
9. Overview of Common Global State Management Tools
React Native applications can use different state management approaches. There is no single tool that is best for every application.
| Approach |
Description |
Suitable Use Cases |
| React Context |
Shares values through a provider and its descendants. |
Theme, locale, relatively simple shared state. |
| Context with useReducer |
Combines shared context with reducer-based updates. |
Moderately complex state with several actions. |
| Redux Toolkit |
Provides a structured store, reducers, actions, and developer tooling. |
Applications needing organized, predictable shared state. |
| Zustand |
Offers a lightweight store-based state management approach. |
Apps wanting a small API for shared state. |
| MobX |
Uses observable state and reactive updates. |
Applications that prefer a reactive state model. |
| TanStack Query |
Manages server-state fetching, caching, synchronization, and related workflows. |
API data that needs caching and refresh behavior. |
These tools serve different purposes. A global client-side store is not automatically the best place for all API responses, and a server-state library does not necessarily replace local UI state.
10. Using useReducer for More Complex State
useReducer is useful when state updates involve multiple related actions or when update logic becomes difficult to manage with several setter functions.
Example: Task List Reducer
import React, { useReducer, useState } from 'react';
import { View, Text, TextInput, Button } from 'react-native';
function taskReducer(state, action) {
switch (action.type) {
case 'add':
return [
...state,
{ id: action.id, title: action.title }
];
case 'remove':
return state.filter(task => task.id !== action.id);
default:
return state;
}
}
export default function TaskScreen() {
const [title, setTitle] = useState('');
const [tasks, dispatch] = useReducer(taskReducer, []);
function addTask() {
const trimmed = title.trim();
if (!trimmed) return;
dispatch({
type: 'add',
id: `${Date.now()}-${Math.random()}`,
title: trimmed
});
setTitle('');
}
return (
<View>
<TextInput
value={title}
onChangeText={setTitle}
placeholder="Enter a task"
/>
<Button title="Add Task" onPress={addTask} />
{tasks.map(task => (
<View key={task.id}>
<Text>{task.title}</Text>
<Button
title="Remove"
onPress={() => dispatch({
type: 'remove',
id: task.id
})}
/>
</View>
))}
</View>
);
}
The reducer receives the previous state and an action, then returns the next state. In this example, the state remains local to the screen. A reducer can also be used inside a context provider when multiple components need access to the same state and actions.
For production applications, prefer a reliable unique ID generator when IDs must remain unique across sessions or devices. The timestamp-based example is intended to demonstrate the reducer pattern.
11. Understanding State Updates and Immutability
React state should be treated as immutable. Instead of modifying an existing object or array, create a new one with the intended changes. This makes updates easier to reason about and helps React detect changes.
Incorrect Object Update
profile.name = 'Amit';
setProfile(profile);
This mutates the existing object. React may not detect the intended change because the reference remains the same.
Correct Object Update
setProfile(previous => ({
...previous,
name: 'Amit'
}));
Incorrect Array Update
tasks.push(newTask);
setTasks(tasks);
Correct Array Update
setTasks(previous => [...previous, newTask]);
When to Use Functional Updates
Use the functional form of a state setter when the next value depends on the previous value. This is particularly important for counters, toggles, and repeated updates.
setCount(previous => previous + 1);
setItems(previous => [...previous, newItem]);
12. Local State, Global State, and Server State
It is helpful to distinguish three related concepts rather than placing every value in one state system.
- Local UI state: Temporary interface values such as whether a modal is open or what is typed in a search field.
- Global client state: Shared client-side values such as the current theme, cart contents, or application preferences.
- Server state: Data owned by a backend, such as product listings, account details, or order history, which must be fetched and may become stale.
Server state often needs loading indicators, error handling, caching, refetching, and invalidation. A tool such as TanStack Query can help manage those concerns. The choice depends on the application and is separate from the question of whether a UI value is local or global.
Example: Product Screen
- Search text: usually local state.
- Selected filter panel visibility: local state.
- Cart shared with checkout: potentially global state.
- Product data from an API: server state.
- Saved theme preference: shared client state, with persistence if required.
13. Persisting State Between App Launches
Ordinary React state and Context values generally exist in memory. They should not be assumed to survive an application restart. If a preference or other value must remain available after the app is closed, the application needs a persistence mechanism.
For example, a theme preference may be saved with a suitable storage solution such as AsyncStorage. Sensitive credentials require more careful storage decisions and should not be stored as ordinary unprotected text.
Example: Saving a Theme Preference with AsyncStorage
Install and configure AsyncStorage according to the official package instructions before using this example.
import React, { useEffect, useState } from 'react';
import { View, Text, Button } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
const THEME_KEY = 'app-theme';
export default function ThemePreferenceScreen() {
const [theme, setTheme] = useState('light');
const [loaded, setLoaded] = useState(false);
useEffect(() => {
let active = true;
async function loadTheme() {
try {
const savedTheme = await AsyncStorage.getItem(THEME_KEY);
if (active && (savedTheme === 'light' || savedTheme === 'dark')) {
setTheme(savedTheme);
}
} catch (error) {
console.warn('Unable to load theme preference');
} finally {
if (active) setLoaded(true);
}
}
loadTheme();
return () => {
active = false;
};
}, []);
async function changeTheme() {
const nextTheme = theme === 'light' ? 'dark' : 'light';
setTheme(nextTheme);
try {
await AsyncStorage.setItem(THEME_KEY, nextTheme);
} catch (error) {
console.warn('Unable to save theme preference');
}
}
if (!loaded) {
return <Text>Loading preference...</Text>;
}
return (
<View>
<Text>Theme: {theme}</Text>
<Button title="Change Theme" onPress={changeTheme} />
</View>
);
}
This is a simplified example. In a larger application, consider keeping storage synchronization inside a dedicated Hook or provider and handling persistence failures according to the product requirements. If multiple updates can occur quickly, ensure that saved values remain consistent with the latest state.
14. State Management with Navigation
React Native applications frequently contain multiple screens. State should be stored according to how long it needs to live and which screens need it.
- Input text for a single screen can usually remain local.
- A multi-step form may keep its state in a common parent or a feature-level store if values must survive navigation between steps.
- A shared cart can live in a context or global store so product and checkout screens use the same data.
- Route parameters are suitable for screen-specific navigation information, such as an item ID, but are not a substitute for every shared state requirement.
Before deciding where to store state, consider whether a screen can unmount when the user navigates away. If state must survive that lifecycle, move it to an appropriate longer-lived owner or persist it when necessary.
15. Common State Management Mistakes
| Mistake |
Why It Is a Problem |
Better Approach |
| Making every value global |
Creates unnecessary dependencies and complexity. |
Keep component-specific values local. |
| Duplicating shared data |
Different screens may display inconsistent values. |
Use one shared source of truth when appropriate. |
| Mutating arrays or objects |
React may not recognize the intended change. |
Create new objects or arrays for updates. |
| Passing props through many unrelated layers |
Intermediate components become unnecessarily coupled. |
Consider lifting state, composition, Context, or a store. |
| Using Context for every API response |
May complicate caching, refetching, and request lifecycle logic. |
Consider a server-state solution where appropriate. |
| Assuming state persists automatically |
Values may reset when the component or app restarts. |
Choose explicit persistence where required. |
| Updating based on a stale value |
Rapid updates can produce unexpected results. |
Use functional state updates when depending on previous state. |
| Putting secrets in ordinary client state |
Client-side values are not a secure vault. |
Use appropriate secure storage and backend authorization. |
16. Debugging Local and Global State
Problem 1: UI Does Not Update
- Check that the setter function is being called.
- Ensure objects and arrays are replaced rather than mutated.
- Check that the displayed component reads the current state or context value.
- Confirm that the correct component owns the state.
Problem 2: Context Value Is Undefined or Missing
- Verify that the consuming component is inside the correct provider.
- Check the provider's exported value and the consumer's property names.
- Use a custom Hook to detect when a provider is missing.
Problem 3: State Resets After Navigation
- Check whether the screen is unmounted when navigating away.
- Move state to a parent or shared provider if it must outlive the screen.
- Persist the value if it must survive an app restart.
Problem 4: Several Screens Show Different Values
- Check whether each screen keeps its own duplicate copy of the same information.
- Choose one shared source of truth for data that must remain consistent.
- Ensure all consumers read from the intended context or store.
Problem 5: Too Many Components Re-render
- Keep local state close to the component that uses it.
- Split unrelated context values when it makes sense.
- Keep provider values stable where appropriate and measure performance before optimizing.
- Use memoization selectively after identifying an actual performance issue.
17. Best Practices for State Management
- Start local: Keep state within the smallest component scope that satisfies the requirement.
- Lift state when needed: Share state through a common parent when related components need the same value.
- Use global state selectively: Promote data to a shared store only when several parts of the app need it.
- Avoid duplication: Maintain a clear source of truth for shared data.
- Preserve immutability: Create new arrays and objects during updates.
- Use descriptive names: Names such as
isLoading, cartItems, and selectedCategory clarify intent.
- Separate concerns: Keep UI state, server data, and persistent preferences conceptually distinct.
- Handle errors: Include appropriate error states for storage operations and API requests.
- Test state transitions: Verify behavior before and after actions, including edge cases.
- Protect sensitive information: Do not assume that values stored in client-side state are private or secure.
18. Practical Project: Shopping Cart with Shared State
Project objective: Create a small React Native shopping cart in which product information is displayed on a product screen and the cart count is shared across the application.
Project Requirements
- Create a product list with product names and prices.
- Add an Add to Cart button for each product.
- Store cart items in shared state using React Context.
- Display the cart item count in a separate component.
- Allow users to remove products from the cart.
- Display an empty-cart message when no items remain.
- Calculate the total price from the current cart items.
Suggested Folder Structure
src/
context/
CartContext.js
components/
ProductCard.js
CartSummary.js
screens/
ProductsScreen.js
CartScreen.js
App.js
Example: Cart Context
import React, { createContext, useContext, useState } from 'react';
const CartContext = createContext(null);
export function CartProvider({ children }) {
const [cartItems, setCartItems] = useState([]);
function addToCart(product) {
setCartItems(previous => [...previous, product]);
}
function removeFromCart(productId) {
setCartItems(previous => {
const index = previous.findIndex(item => item.id === productId);
if (index === -1) return previous;
return previous.filter((item, itemIndex) => itemIndex !== index);
});
}
const total = cartItems.reduce(
(sum, item) => sum + item.price,
0
);
return (
<CartContext.Provider
value={{ cartItems, addToCart, removeFromCart, total }}
>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const context = useContext(CartContext);
if (context === null) {
throw new Error('useCart must be used inside CartProvider');
}
return context;
}
This simple example treats each addition as a separate cart entry. A production cart may instead combine identical products and maintain a quantity for each item. If product IDs can appear multiple times, use a cart-line identifier or a clear quantity-based data model so that removal targets the intended entry.
Example: Cart Summary Component
import React from 'react';
import { View, Text } from 'react-native';
import { useCart } from '../context/CartContext';
export default function CartSummary() {
const { cartItems, total } = useCart();
return (
<View>
<Text>Cart Items: {cartItems.length}</Text>
<Text>Total: ₹{total.toFixed(2)}</Text>
</View>
);
}
Project Learning Outcomes
- Understand when shared state is useful.
- Create and consume a React Context provider.
- Keep cart data in one source of truth.
- Update arrays immutably.
- Calculate derived values from state instead of storing unnecessary duplicate totals.
- Organize shared logic into a dedicated context module.
19. Practice Exercises
- Create a counter with Increase, Decrease, and Reset buttons using local state.
- Build a registration form that stores name, email, and password input locally.
- Create a theme context that shares light and dark mode between two screens.
- Build a favorite-products feature that shares selected product IDs between a product list and favorites screen.
- Create a task manager with
useReducer supporting add, complete, and delete actions.
- Persist a non-sensitive user preference and restore it when the app starts.
- Identify which values in a shopping application should be local, global, or server state.
- Refactor a component that passes the same props through several intermediate layers.
20. Interview Questions and Answers
Q1. What is state in React Native?
Answer: State is data maintained by a component or shared state system that can change over time. State changes can cause React to render updated UI.
Q2. What is local state?
Answer: Local state is managed within an individual component or a small component subtree. It is suitable for input values, modal visibility, and temporary UI interactions.
Q3. What is global state?
Answer: Global state is shared information that multiple components or screens need to access or update, such as a theme or shopping cart.
Q4. What is the difference between local and global state?
Answer: Local state has a narrow ownership scope, while global state is managed at a shared level so multiple parts of the application can access it.
Q5. What is the useState Hook?
Answer: useState adds a state variable to a functional component and provides a setter function to request updates to that value.
Q6. What is React Context?
Answer: Context allows a parent to provide a value to descendant components without passing it through every intermediate component as props.
Q7. What does lifting state up mean?
Answer: Lifting state up means moving state to the nearest common parent of components that need to share the value.
Q8. What is prop drilling?
Answer: Prop drilling is passing props through several intermediate components that may not use the values themselves. Lifting state, component composition, Context, or a store can help when this becomes difficult to maintain.
Q9. Why should state not be mutated directly?
Answer: Direct mutation changes an existing object or array rather than creating a new state value. This can prevent React from detecting the intended update and makes behavior harder to reason about.
Q10. When should a developer use useReducer?
Answer: useReducer is useful when a component has several related state transitions or when the update logic is easier to express as actions handled by a reducer.
Q11. Is React Context always better than props?
Answer: No. Props are often clearer for direct parent-child communication. Context is useful when many descendants need the same value and passing it through every intermediate component becomes cumbersome.
Q12. Does global state automatically persist after an app restarts?
Answer: No. In-memory state generally resets when the application process restarts. Persistence requires an appropriate storage mechanism and restoration logic.
Q13. What is server state?
Answer: Server state is data owned by a backend and fetched by the client, such as products or orders. It often requires caching, loading and error handling, and synchronization with the server.
Q14. How can shared state be tested?
Answer: Test initial values, state transitions, consumer rendering, edge cases, and whether all components that rely on shared state reflect updates correctly.
21. Quick Revision Checklist
- State stores information that can change during an application's lifetime.
- Use
useState for straightforward local state.
- Use
useReducer when state transitions involve related actions.
- Use props and lifting state up to share values between related components.
- Use Context or a global state library when several parts of the app need shared data.
- Keep component-specific UI state local where possible.
- Update arrays and objects immutably.
- Separate global client state from server state and persistent preferences.
- Use explicit storage when a value must survive app restarts.
- Test state updates, provider setup, navigation behavior, and error cases.
22. Conclusion
Understanding local and global state is fundamental to building reliable React Native applications. Local state is usually the simplest choice for values used by one component, while global state helps multiple screens share information consistently. Props and lifting state up are effective for smaller component groups, React Context supports broader sharing, and dedicated state libraries can help as an application grows.
The key is to choose state ownership based on the data's scope and lifecycle, avoid unnecessary duplication, update state immutably, and distinguish temporary UI values from server-owned data and persistent preferences. Applying these principles makes React Native applications easier to test, maintain, and expand.
Further Learning: React Documentation: Managing State | React Documentation: Passing Data Deeply with Context