React Hooks & Redux: Essential Concepts for Developers
React Hooks and Essential Libraries
useState Hook
Purpose
Purpose: Adds state to functional components.
Import
import { useState } from 'react';
Syntax
const [state, setState] = useState(initialValue);
Update Methods
setState(newValue);
setState(prevState => ...);
(for updates based on previous state)
Example
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useEffect Hook
Purpose
Purpose: Performs side effects such as data fetching, subscriptions, or direct DOM manipulations.
Import
import { useEffect } from 'react';
Syntax
useEffect(() => { /* effect */ return () => { /* cleanup (optional) */ }; }, [dependencies]);
Dependencies Explained
- Omitted: Runs after every render.
[]
: Runs once after the initial render. The optional return function performs cleanup on component unmount.[dep1, dep2]
: Runs on initial render and whenever any of the specified dependencies change.
Example: Simple Data Fetching
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function DataLoader({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
axios.get(`/api/data/${id}`)
.then(res => setData(res.data));
}, [id]);
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
useMemo Hook
Purpose
Purpose: Memoizes a computed value, preventing its re-calculation on every render unless its dependencies have changed. This optimizes performance for expensive computations.
Import
import { useMemo } from 'react';
Syntax
const memoizedValue = useMemo(() => computeExpensiveValue(dep1), [dep1]);
Example
import React, { useMemo } from 'react';
function HeavyCalculation({ a, b }) {
const result = useMemo(() => {
console.log('Calculating...');
return a * b * Math.random();
}, [a, b]);
return <p>Result: {result}</p>;
}
useCallback Hook
Purpose
Purpose: Memoizes a callback function itself. This is particularly useful for performance optimization when passing callbacks to child components that are optimized with React.memo
.
Import
import { useCallback } from 'react';
Syntax
const memoizedCallback = useCallback((args) => { /* ... */ }, [dependencies]);
Example
import React, { useState, useCallback } from 'react';
const MyButton = React.memo(({ onClick, children }) => {
console.log('Button Rendered:', children);
return <button onClick={onClick}>{children}</button>;
});
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <MyButton onClick={handleClick}>Increment: {count}</MyButton>;
}
useRef Hook
Purpose
- Access DOM elements directly.
- Store mutable values that persist across renders without triggering re-renders.
Import
import { useRef } from 'react';
Syntax
const myRef = useRef(initialValue);
Access with myRef.current
Example (DOM Focus)
import React, { useRef, useEffect } from 'react';
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => { inputRef.current.focus(); }, []);
return <input ref={inputRef} type="text" />;
}
useNavigate Hook (React Router DOM)
Purpose
Purpose: Enables programmatic navigation between routes within a React application using React Router DOM.
Import
import { useNavigate } from 'react-router-dom';
Syntax
const navigate = useNavigate();
Usage
navigate('/path');
navigate(-1);
navigate('/path', { replace: true, state: { data } });
Example
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
const handleLogin = () => navigate('/dashboard');
return <button onClick={handleLogin}>Login</button>;
}
Axios Library for HTTP Requests
Purpose
Purpose: A popular promise-based HTTP client used for making API requests from the browser or Node.js.
Installation
npm install axios
Import
import axios from 'axios';
Common Methods
axios.get(url),
axios.post(url, data),
axios.put(),
axios.delete()
Example (GET with async/await)
import axios from 'axios';
async function fetchData(url) {
try {
const response = await axios.get(url);
console.log(response.data);
return response.data;
} catch (error) {
console.error('API Error:', error);
}
}
fetchData('https://fakestoreapi.com/products/1');
Instance for Defaults
const apiClient = axios.create({ baseURL: 'https://api.example.com' });
apiClient.get('/users');
Core Redux Concepts
Installation
npm i redux # Installs the Redux core library
Actions & Action Creators
Actions
- Plain JavaScript objects describing "what happened".
- Must have a
type
property. - Example:
{ type: 'INCREMENT_COUNTER' }
- Example:
{ type: 'ADD_TODO', text: 'Learn Redux' }
Action Creators
- Functions that create and return action objects.
- Example:
function incrementCounter() { return { type: 'INCREMENT_COUNTER' }; } function addTodo(text) { return { type: 'ADD_TODO', text: text }; }
Reducers
- Pure functions:
(previousState, action) => newState
. - Describe how the application's state changes in response to actions.
- Never mutate
previousState
directly. Always return a new state object. - Handle unrecognized actions by returning the current state.
Example: Counter Reducer
const initialCounterState = 0;
function counterReducer(state = initialCounterState, action) {
switch (action.type) {
case 'INCREMENT_COUNTER':
return state + 1;
case 'DECREMENT_COUNTER':
return state - 1;
default:
return state;
}
}
Example: Todos Reducer
const initialTodosState = [];
function todosReducer(state = initialTodosState, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { text: action.text, completed: false }];
case 'TOGGLE_TODO':
return state.map((todo, index) =>
index === action.index ? { ...todo, completed: !todo.completed } : todo
);
default:
return state;
}
}
Combining Reducers
combineReducers
utility turns an object of reducer functions into a single reducer function.- Each key in the input object becomes a key in the state object.
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counter: counterReducer,
todos: todosReducer
});
State shape would be: { counter: 0, todos: [] }
The Redux Store
The single object that holds the application's state tree.
Main Store Methods
store.getState()
: Returns the current state.store.dispatch(action)
: Dispatches an action to trigger state changes.store.subscribe(listener)
: Registers a callback to be invoked whenever the state changes. Returns an unsubscribe function to stop listening.
Creating a Store
import { createStore } from 'redux';
// const store = createStore(counterReducer); // For a single reducer
const store = createStore(rootReducer); // Using combined reducers
Store API Usage Example (Vanilla JS)
import { createStore, combineReducers } from 'redux';
// --- Action Types & Creators ---
const INCREMENT = 'INCREMENT';
const ADD_MESSAGE = 'ADD_MESSAGE';
const increment = () => ({ type: INCREMENT });
const addMessage = (text) => ({ type: ADD_MESSAGE, text });
// --- Reducers ---
function countReducer(state = 0, action) {
if (action.type === INCREMENT) return state + 1;
return state;
}
function messagesReducer(state = [], action) {
if (action.type === ADD_MESSAGE) return [...state, action.text];
return state;
}
const appReducer = combineReducers({ count: countReducer, messages: messagesReducer });
// --- Store ---
const store = createStore(appReducer);
// --- Subscribing to state changes ---
const unsubscribe = store.subscribe(() => {
console.log('State changed!', store.getState());
// Here you would typically update your UI based on the new state
});
// --- Dispatching actions ---
console.log('Initial state:', store.getState()); // Output: { count: 0, messages: [] }
store.dispatch(increment()); // Listener logs: { count: 1, messages: [] }
store.dispatch(addMessage('Hello Redux!')); // Listener logs: { count: 1, messages: ['Hello Redux!'] }
store.dispatch(increment()); // Listener logs: { count: 2, messages: ['Hello Redux!'] }
unsubscribe(); // Stop listening to state changes
store.dispatch(addMessage('This change won\'t be logged by the listener.'));
console.log('Final state:', store.getState()); // Output: { count: 2, messages: ['Hello Redux!', 'This change won\'t be logged by the listener.'] }