Optimal Structure and Components for React Applications


Hooks are special functions introduced in React v16.8 that enable you to use state and other React features in functional components (previously, these features were only available in class components).
Key Points
Why Hooks?
Avoid using class components just for state or lifecycle methods.
Make code cleaner, reusable, and easier to manage.
Allow sharing logic between components through custom hooks.
Rules of Hooks
Only call hooks at the top level of a component or custom hook (not inside loops, conditions, or nested functions).
Only call hooks from React functions (functional components or custom hooks).
Common Built-in Hooks
useState
– Adds state to a functional component.const [count, setCount] = useState(0);
useEffect
– Handles side effects (API calls, timers, subscriptions).useEffect(() => { console.log("Component mounted or updated"); }, []);
useContext
– Accesses values from React Context without prop drilling.useRef
– Accesses DOM elements or stores mutable values without causing re-renders.useReducer
– Manages complex state logic using reducers.useMemo
&useCallback
– Optimize performance by memoizing values or functions.Custom Hooks - You can create your own hooks (functions starting with
use
) to reuse logic across components.
Why Hooks Are Important
Eliminate the need for class components.
Improve code readability and logic reuse.
Enable powerful features (state, lifecycle, refs) in functional components.
useState
What it is:
useState
is a React Hook that allows you to add state (data that can change) to functional components.Syntax:
const [state, setState] = useState(initialValue);
How it works:
When
setState
is called, React re-renders the component with the updated value.State is preserved between renders.
Without useState
:
function App() {
let count = 0;
const Increment = () => {
count += 1;
document.querySelector('h1').innerText = `Count: ${count}`;
}
const Decrement = () => {
count -= 1;
document.querySelector('h1').innerText = `Count: ${count}`;
}
return (
<div>
<h1>Count: {count} </h1>
<button onClick={()=>{Increment()}}>Increment</button>
<button onClick={()=>{Decrement()}}>Decrement</button>
</div>
);
}
This code is bad because:
Direct DOM manipulation: Using
document.querySelector
and manually updatinginnerText
bypasses React’s virtual DOM, which breaks React’s rendering flow.No reactivity:
count
is a local variable, so React doesn’t know it changed and won’t re-render the component.Unpredictable UI updates: If React re-renders for any reason,
count
resets to0
and the manual DOM changes can get overwritten. re-renders
💡 What’s the Real Solution?
You need a way to tell React:
“Hey, something changed. Please re-render the UI with updated values.”
That’s where React’s hooks
— especially useState
— come in. 😉
Basic Structure of useState
const [state, setState] = useState(initialValue);
state
: The current value of the statesetState
: A function to update the stateinitialValue
: The starting value for the state
How It Works in Your Counter Component
const [count, setCount] = useState(0);
Initialization: When the component first renders,
count
is set to0
(the initial value passed touseState
)State Update: When
setCount
is called it:Updates the state value
Triggers a re-render of the component
Provides the new value during the next render
Key Characteristics of useState
State Preservation: Unlike regular variables (which reset on each render), React preserves the state between renders
✅ What does it mean:
In React, every time a component re-renders, all variables declared inside the function get reset, except those stored in
useState
.This is why React uses hooks like
useState
to preserve values between renders.
⚠️ Example Without
useState
(Regular Variable):function Counter() { let count = 0; function handleClick() { count++; console.log("Clicked:", count); } return <button onClick={handleClick}>Click Me</button>; }
❌ Problem:
Every time you click the button, the component re-renders.
The
count
variable resets to0
on every render.Even though
count++
occurs, the count never increases consistently in the UI or in logs.
✅ Example With useState
:
function App() {
let[count, setCount] = useState(0);
const Increment = () => {
count += 1;
setCount(count);
}
function Decrement () {
count -= 1;
setCount(count);
}
return (
<div>
<h1>Count: {count} </h1>
<button onClick={()=>{Increment()}}>Increment</button>
<button onClick={Decrement}>Decrement</button>
</div>
);
}
✅ What Happens Now:
useState(0)
initializescount
once.Even after the component re-renders, React remembers the previous
count
value.Each time you click the button,
setCount()
updates the state, which causes a re-render with the new value preserved.Initial Render: count = 0 (initialized) → Display "Count is: 0" [User clicks Increment] setCount(0 + 1) → Re-render with count = 1 → Display "Count is: 1" [User clicks Increment again] setCount(1 + 1) → Re-render with count = 2 → Display "Count is: 2" [User clicks Decrement] setCount(2 - 1) → Re-render with count = 1 → Display "Count is: 1"
Why This is Better Than the Previous Approaches
Automatic UI Updates: React handles DOM updates when state changes
Predictable State Management: Single source of truth for your data
Optimized Performance: React batches updates and only updates necessary DOM elements
Cleaner Code: No manual DOM manipulation needed
React's Full Power: Works seamlessly with other React features like effects, context, etc.
Common Usage Patterns
Multiple State Variables:
const [count, setCount] = useState(0); const [name, setName] = useState('');
Object State:
const [user, setUser] = useState({ name: '', age: 0 });
Functional Initial State (for expensive calculations):
const [data, setData] = useState(() => expensiveCalculation());
Best Practices
Don't Modify State Directly: Always use the setter function
Use Functional Updates when the new state depends on the previous state
Split Complex State into multiple
useState
calls when possibleConsider Custom Hooks for reusable state logic
useState
is the foundation of state management in React functional components, enabling them to be as powerful as class components with simpler syntax and better organization.
function App() {
let [color, setColor] = useState('black');
console.log('render');
document.body.style.backgroundColor = color;
return (
<div>
<button onClick={() => setColor('red')}>Red</button>
<button onClick={() => setColor('green')}>Green</button>
<button onClick={() => setColor('blue')}>Blue</button>
<button onClick={() => setColor('pink')}>Pink</button>
<button onClick={() => setColor('orange')}>Orange</button>
</div>
);
}
How React behaves here
When you click a button:
setColor(newColor)
is called.React schedules a re-render of the
App
component.
Primitive values vs. Objects:
Here,
color
is a string (a primitive data type).When you click the same button multiple times with the same color value, React will still re-render, because:
React doesn't compare primitive values deeply; it sees a state update call and assumes something might have changed.
In Strict Mode, React also deliberately renders twice (in development) to detect side effects.
If the state is an object:
React re-renders only if the reference of the object changes.
Example:
setState({ theme: 'dark' }); // new object → triggers re-render setState(sameObjectReference); // same object → React may skip re-render
React uses shallow comparison for objects and arrays. If the reference doesn't change, React treats it as no update.
Key Points
Primitives (string, number, boolean):
- A primitive data type will only trigger one extra render in React (Strict Mode)
Objects and arrays:
React only checks the reference (memory address).
If you update the state with the same reference, React will not re-render.
Example with Object (Reference Matters)
function App() {
const [theme, setTheme] = useState({ color: 'black' });
console.log('render');
return (
<div>
<button onClick={() => setTheme({ color: 'red' })}>Red</button>
<button onClick={() => setTheme(theme)}>Same Reference</button>
</div>
);
}
Clicking Red →
setTheme({ color: 'red' })
creates a new object, React re-renders.Clicking Same Reference →
setTheme(theme)
passes the same object reference, React skips re-render.
Summary
Hooks let functional components use state and other React features.
useState
adds state:const [value, setValue] = useState(initial);
setValue
updates state and triggers a re-render.Primitives: Always re-render (1 extra in Strict Mode).
Objects/Arrays: React checks references; same reference → no re-render.
Avoid manual DOM updates; always use React state.
Subscribe to my newsletter
Read articles from Hridoy Chowdhury directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Hridoy Chowdhury
Hridoy Chowdhury
I'm a Computer Science & Engineering student exploring the world of Blockchain, Web Development (MERN), and coding interview prep. I write to reinforce my learning and share insights that make complex ideas simpler. Always building, always learning.