React Hooks & Redux: Essential Concepts for Developers

Posted by Anonymous and classified in Computers

Written on in English with a size of 10.31 KB

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

  1. store.getState(): Returns the current state.
  2. store.dispatch(action): Dispatches an action to trigger state changes.
  3. 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.'] }

Related entries: