Introduction
React is a JavaScript library for building user interfaces, designed around composable components and predictable data flow. Instead of manipulating the DOM directly, you describe UI as a function of state, and React efficiently updates the DOM when state changes.
Key ideas behind React:
- Declarative UI: describe what the UI should look like for a given state
- Component composition: build complex screens from small, reusable pieces
- Virtual DOM and reconciliation: React minimizes real DOM changes for performance
- Unidirectional data flow: state moves down, events move up
JSX
{`const element = Hello, React!
;`}
JSX is a syntax extension that lets you describe UI using HTML-like tags inside JavaScript. The JSX compiler converts it to React.createElement calls.
Advanced JSX patterns:
- Expressions inside braces:
{}can contain any JS expression - Fragments to avoid extra DOM nodes:
<>...</> - Conditional rendering via ternaries and short-circuit operators
{`const name = "Nafees";
const ui = (
<>
Welcome, {name}
{name && Signed in
}
>
);`}
Components
{`function Welcome(props) {
return Hello, {props.name}
;
}
const element = ;`}
Components encapsulate UI, behavior, and state. Functional components are preferred in modern React because they are simpler and work seamlessly with hooks.
Component best practices:
- Keep components small and focused on a single responsibility
- Prefer composition over inheritance
- Use props for configuration and callbacks for events
{`function Card({ title, onSelect }) {
return (
{title}
);
}`}
Props & State
{`function Counter() {
const [count, setCount] = React.useState(0);
return (
Count: {count}
);
}`}
Props are read-only inputs passed from parent to child. State is local, mutable data that drives re-renders when it changes.
Advanced concepts:
- Derived state: compute values from props instead of duplicating state
- Lifting state up: move shared state to a common parent
- State updates are async; use the functional form when needed
{`setCount(prev => prev + 1);
// Lifted state example
`}
Events in React
{`function Button() {
function handleClick() {
alert("Clicked!");
}
return ;
}`}
React uses a synthetic event system that normalizes events across browsers. Event handlers are camelCase and use functions, not strings.
Event tips:
- Pass event data via
onClick={() => handler(id)} - Use
e.preventDefault()to stop default browser behavior - Stop propagation with
e.stopPropagation()when needed
{` { e.preventDefault(); logout(); }}>
Sign out
`}
Conditional Rendering
{`function Greeting({isLoggedIn}) {
return (
{isLoggedIn ? Welcome back!
: Please sign in.
}
);
}`}
Conditional rendering decides which UI to show based on state or props. Keep conditions readable and avoid deeply nested ternaries.
Common patterns:
- Ternaries for small branches
- Logical AND for optional content
- Switch statements or helper functions for complex cases
{`{isAdmin && }
{status === "loading" ? : }`}
Lists & Keys
{`const items = ['Apple','Banana','Cherry'];
const listItems = items.map((item, index) => Lists are rendered by mapping arrays to elements. Keys help React identify which items changed, were added, or were removed.
Key rules:
- Use stable, unique ids from data, not array indexes
- Keys should be consistent across renders
- Avoid random keys; they cause full re-renders
{`items.map(item => Forms & Controlled Components
{`function NameForm() {
const [name, setName] = React.useState('');
return (
setName(e.target.value)} />
);
}`}
Controlled components keep form values in React state. This enables validation, conditional UI, and consistent data handling.
Advanced form patterns:
- Single state object for large forms
- Validation on change or on submit
- Use libraries like React Hook Form for complex cases
{`const [form, setForm] = React.useState({ email: "", age: "" });
function handleChange(e) {
setForm(prev => ({ ...prev, [e.target.name]: e.target.value }));
}`}
Component Lifecycle
{`// Functional components: useEffect
React.useEffect(() => {
console.log("Component mounted or updated");
return () => console.log("Cleanup on unmount");
}, [dependencies]);`}
Lifecycle methods let you run side effects at specific phases. Hooks replace class lifecycle methods but map to the same concepts.
Common lifecycle patterns:
- Fetch data on mount
- Subscribe and clean up on unmount
- Respond to prop changes in dependency arrays
{`// Equivalent to componentDidMount
React.useEffect(() => { fetchData(); }, []);`}
Class components have methods: componentDidMount, componentDidUpdate, componentWillUnmount.
React Hooks
{`// useState, useEffect
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = \`Count: \${count}\`;
}, [count]);`}
Hooks let functional components manage state, side effects, refs, and context. They must be called at the top level of a component or custom hook.
Common hooks:
useStatefor local stateuseEffectfor side effectsuseMemoanduseCallbackfor performanceuseReffor mutable values or DOM nodes
{`const value = React.useMemo(() => expensiveCalc(data), [data]);
const handleClick = React.useCallback(() => doSomething(id), [id]);`}
Context API
{`const ThemeContext = React.createContext('light');
function App() {
return (
);
}`}
Context provides a way to pass data through the component tree without prop drilling. It is best for global or semi-global data like themes, locale, or auth state.
Context best practices:
- Split contexts to avoid re-rendering large trees
- Memoize provider values to prevent unnecessary updates
- Prefer context + reducer for predictable updates
{`const AuthContext = React.createContext(null);
function useAuth() { return React.useContext(AuthContext); }`}
React Router
{`import { BrowserRouter, Routes, Route } from 'react-router-dom';
} />
} />
`}
React Router enables client-side routing, nested layouts, dynamic routes, and URL-based state. It works by rendering different components based on the URL.
Advanced routing features:
- Dynamic routes with parameters:
/users/:id - Nested routes for shared layouts
- Lazy-loaded routes for code splitting
{` } />
}>
} />
`}
Redux / State Management
{`import { createStore } from 'redux';
function counter(state = 0, action) {
switch(action.type){
case 'INCREMENT': return state + 1;
default: return state;
}
}
const store = createStore(counter);
store.dispatch({type:'INCREMENT'});`}
Redux is a predictable state container. It centralizes state updates into pure reducer functions and makes changes traceable.
State management tips:
- Use Redux Toolkit for concise, safer reducers
- Keep state normalized to avoid duplication
- Use selectors to encapsulate state shape
{`// Redux Toolkit slice
const slice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: { increment: state => { state.value += 1; } }
});`}