React Fundamentals & Lifecycle
what is react?
react is a library, not a framework
- library: provides specific functionality (UI rendering)
- framework: provides entire application structure
core concepts:
- declarative UI - describe what the UI should look like, react handles the how
- components as pure functions - given the same inputs (props), return the same output
- one-way data flow - data flows down from parent to child via props
props
what props are
- immutable inputs to a component
- passed from parent to child
- read-only by design
props immutability
❌ NEVER reassign props:
export const Component = ({ title }) => {
title = title || 'Default'; // BAD - mutates props
// ...
}
issue:
- mutates props (breaks react assumptions)
- violates one-way data flow
- makes components harder to reason about
||treats0,false,''as falsy
✅ derive a new variable instead:
export const Component = ({ title }) => {
const displayTitle = title ?? 'Default'; // GOOD
// ...
}
default values: ?? vs ||
|| - fallback on ANY falsy value
- triggers on:
0,false,'',null,undefined - use when: only want to exclude falsy values
?? - fallback ONLY on null or undefined
- triggers on:
null,undefined - use when:
0,false,''are valid values
examples:
const count = 0;
count || 10 // → 10 (0 is falsy)
count ?? 10 // → 0 (0 is not null/undefined)
const name = '';
name || 'Guest' // → 'Guest' ('' is falsy)
name ?? 'Guest' // → '' ('' is not null/undefined)
rule of thumb: use ?? when 0, false, or '' are valid values.
state
what state is
- data that changes over time
- triggers re-renders when updated
- local to the component
when to use useState
use state when:
- ✅ value changes over time
- ✅ change should trigger a re-render
- ✅ value is not derived from props
when NOT to use state
don't use state for:
- ❌ values derived from props
- ❌ initialization-only values
- ❌ constants
example:
// ❌ BAD - startTime never changes
const [startTime, setStartTime] = useState(Date.now());
// ✅ GOOD - just a constant
const startTime = Date.now();
// ✅ GOOD - date changes over time
const [date, setDate] = useState(Date.now());
component lifecycle
a react function component goes through three phases:
- mount - component appears for the first time
- update - component re-renders due to state or prop changes
- unmount - component is removed from the UI
how lifecycle maps to useEffect
in function components, side effects are controlled with useEffect:
useEffect(() => {
// side effect runs AFTER render
return () => {
// cleanup runs BEFORE next effect or on unmount
};
}, [dependencies]);
how useEffect works internally
mount → effect runs after DOM paint
update → cleanup runs → effect runs again (if deps changed)
unmount → cleanup runs once
useEffect
what useEffect is
a hook for performing side effects in function components.
why side effects don't belong in render
render must be pure:
- ❌ no timers
- ❌ no subscriptions
- ❌ no mutations
- ❌ no side effects
why?
- react can render multiple times
- render may be interrupted or retried
- side effects during render cause memory leaks & duplication
dependency array behavior
useEffect(() => {
// ...
}, []); // empty array → runs once on mount
useEffect(() => {
// ...
}, [count]); // runs when count changes
useEffect(() => {
// ...
}); // no array → runs after EVERY render (usually wrong)
effect cleanup function
useEffect(() => {
const id = setInterval(() => {
console.log('tick');
}, 1000);
// cleanup function
return () => {
clearInterval(id);
};
}, []);
when cleanup runs
- before the effect runs again (on update)
- when the component unmounts
why cleanup matters
- ✅ prevents memory leaks
- ✅ prevents duplicate logic after re-renders
- ✅ ensures predictable lifecycle behavior
side effects
what counts as a side effect
- network requests (fetch, axios)
- timers (
setTimeout,setInterval) - subscriptions (WebSocket, EventSource)
- event listeners (window, document)
- DOM manipulation
- logging
- localStorage/sessionStorage
why side effects must be isolated
side effects during render can:
- create memory leaks
- cause duplicate operations
- make components unpredictable
- break react's rendering model
setInterval in react (deep dive)
what setInterval is (JavaScript)
- a browser API (not react)
- takes a callback and a delay (ms)
- returns an interval ID
const id = setInterval(callback, delay);
clearInterval(id);
why setInterval inside render is dangerous
// ❌ DANGEROUS
function Component() {
setInterval(() => {
console.log('tick');
}, 1000);
return <div />;
}
what happens:
- render runs → interval created
- state update → render again → another interval
- leads to:
- multiple intervals running
- memory leaks
- performance degradation
using setInterval inside useEffect
// ✅ CORRECT
useEffect(() => {
const intervalId = setInterval(() => {
setDate(Date.now());
}, 1000);
return () => {
clearInterval(intervalId);
};
}, []); // run once on mount
re-rendering
what causes a re-render
- state changes (
setState) - props change (parent re-renders)
- context changes
- force update (rare, avoid)
avoiding unnecessary re-renders
- keep state close to where it's used
- split large components
- use proper dependency arrays
- memoization (when needed)
JSX & imports
JSX transformation
JSX is transformed to React.createElement calls.
before react 17:
import React from 'react'; // required
react 17+:
// no react import needed for JSX
import { useState } from 'react';
why import react is no longer required
react 17+ uses a new JSX transform that doesn't require React in scope.
when react import is still needed
import React from 'react';
// when using React.Something:
React.memo(Component);
React.forwardRef(...);
React.lazy(...);
modern JavaScript features
Date.now() vs new Date().getTime()
old way (pre-ES5, before 2009):
var timestamp = new Date().getTime(); // ❌ legacy
how it works:
- creates Date object in memory
- calls
getTime()method - returns number
- garbage collector cleans up the Date object
performance: slower - more memory allocation, GC cleanup required
modern way (ES5+):
const timestamp = Date.now(); // ✅
performance: faster - returns timestamp directly, no object allocation
common react mistakes
❌ side effects in render
// BAD
function Component() {
setInterval(...); // creates new interval every render
return <div />;
}
❌ reassigning props
// BAD
const Component = ({ title }) => {
title = title || 'Default';
}
❌ using state for constants
// BAD
const [startTime, setStartTime] = useState(Date.now());
// startTime never changes - shouldn't be state
❌ forgetting effect cleanup
// BAD
useEffect(() => {
setInterval(...);
// missing cleanup!
}, []);
key mental models
render is not lifecycle
- render is just a function call
- it calculates what UI should look like
- it doesn't DO anything
effects run after render
render → commit to DOM → paint → effects
cleanup prevents leaks
always clean up:
- timers
- subscriptions
- event listeners
- network requests (abort controllers)
react may render more than you expect
- strict mode renders twice in development
- react may discard renders
- write resilient code
think in data flow, not events
- don't think "when button clicks, update state"
- think "state determines UI"
summary checklist
- understand react is a library, not a framework
- never reassign props
- use
??for default values when0,'',falseare valid - only use state for values that change over time
- keep side effects in
useEffect, not render - always provide cleanup functions for timers/subscriptions
- understand the three lifecycle phases: mount, update, unmount
- know when cleanup runs (before next effect, on unmount)
- use
Date.now()instead ofnew Date().getTime() - think in data flow, not events
- render must be pure
- effects run after render and commit