React's component architecture revolves around data flow and state management. Understanding how data travels from parent to child components, and how to manage it with hooks and context, is essential for building scalable apps.
1. Props – Passing Data Down
Props are the simplest way to pass data from a parent component to its children.
1// Button.jsx
2export default function Button({ label, onClick }) {
3 return <button onClick={onClick}>{label}</button>;
4}
5
6// App.jsx
7import Button from './Button';
8
9export default function App() {
10 const handleClick = () => alert('Button clicked!');
11
12 return <Button label="Click Me" onClick={handleClick} />;
13}
14Props are read-only: a child cannot directly modify the parent’s data.
2. State – Managing Local Component Data
State allows components to manage dynamic data internally using useState.
1import { useState } from 'react';
2
3export default function Counter() {
4 const [count, setCount] = useState(0);
5
6 return (
7 <div>
8 <p>Count: {count}</p>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 </div>
11 );
12}
13useState triggers a re-render when the state changes.
3. Context – Sharing Data Across Components
React Context allows you to share state without prop drilling.
1
2import { createContext, useContext, useState } from 'react';
3
4const ThemeContext = createContext();
5
6export function ThemeProvider({ children }) {
7 const [theme, setTheme] = useState('light');
8 return (
9 <ThemeContext.Provider value={{ theme, setTheme }}>
10 {children}
11 </ThemeContext.Provider>
12 );
13}
14
15export function ThemeToggle() {
16 const { theme, setTheme } = useContext(ThemeContext);
17
18 return (
19 <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
20 Current Theme: {theme}
21 </button>
22 );
23}
24
25// Usage in App.jsx
26import { ThemeProvider, ThemeToggle } from './ThemeContext';
27
28export default function App() {
29 return (
30 <ThemeProvider>
31 <h1>Hello React</h1>
32 <ThemeToggle />
33 </ThemeProvider>
34 );
35}
36Context is best for global data like theme, authentication, or user settings.
4. Combining Hooks and Context
Hooks like useState, useEffect, and useReducer often work hand-in-hand with context to manage complex state across your app.
1import { useReducer, createContext, useContext } from 'react';
2
3const CounterContext = createContext();
4
5const initialState = { count: 0 };
6function reducer(state, action) {
7 switch(action.type) {
8 case 'increment': return { count: state.count + 1 };
9 case 'decrement': return { count: state.count - 1 };
10 default: return state;
11 }
12}
13
14export function CounterProvider({ children }) {
15 const [state, dispatch] = useReducer(reducer, initialState);
16 return (
17 <CounterContext.Provider value={{ state, dispatch }}>
18 {children}
19 </CounterContext.Provider>
20 );
21}
22
23export function CounterControls() {
24 const { state, dispatch } = useContext(CounterContext);
25 return (
26 <div>
27 <p>Count: {state.count}</p>
28 <button onClick={() => dispatch({ type: 'increment' })}>+</button>
29 <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
30 </div>
31 );
32}
33
34// App.jsx
35import { CounterProvider, CounterControls } from './CounterContext';
36
37export default function App() {
38 return (
39 <CounterProvider>
40 <h1>Global Counter</h1>
41 <CounterControls />
42 </CounterProvider>
43 );
44}
45- Best Practices
-
Keep state local until you need it globally.
-
Use Context for cross-cutting concerns, not everything.
-
Use custom hooks to encapsulate reusable logic.
-
Keep components pure and declarative.
React 18 and Gatsby v5 work seamlessly together, allowing you to combine server-side rendering, static site generation, and modern React features like concurrent mode.
React’s data flow may seem simple at first, but mastering props, state, hooks, and context is key to building scalable, maintainable frontend applications.
Happy Coding journey!