# useReducer, useMemo, and useCallback

React hooks provide an elegant way to manage state and lifecycle in functional components. Among these hooks, `useReducer`, `useMemo`, and `useCallback` offer powerful tools for handling complex state logic, optimizing performance, and managing dependencies. This blog dives deep into these hooks, explaining their usage with detailed examples.

### Table of Contents

1. **Introduction to Hooks**
2. **`useReducer` Hook**
   * Introduction
   * Basic Usage
   * Advanced Usage
   * Best Practices
3. **`useMemo` Hook**
   * Introduction
   * Basic Usage
   * Advanced Usage
   * Best Practices
4. **`useCallback` Hook**
   * Introduction
   * Basic Usage
   * Advanced Usage
   * Best Practices
5. **Combining Hooks**
6. **Conclusion**

### 1. Introduction to Hooks

React hooks were introduced in React 16.8, bringing state and lifecycle management to functional components. Among the various hooks, `useReducer`, `useMemo`, and `useCallback` are essential for managing complex state logic and optimizing performance.

### 2. `useReducer` Hook

#### Introduction

`useReducer` is a hook used for managing state in a React component, similar to `useState`. It is particularly useful for complex state logic where multiple sub-values or a state transition logic is involved. It also makes state management more predictable and easier to debug.

#### Basic Usage

The `useReducer` hook accepts a reducer function and an initial state, and returns an array containing the current state and a dispatch function. The reducer function takes the current state and an action, and returns the new state.

```jsx
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

export default Counter;
```

#### Advanced Usage

`useReducer` shines with complex state logic. Let's consider a more advanced example: a form with multiple input fields.

```jsx
import React, { useReducer } from 'react';

const initialState = {
  username: '',
  email: '',
  password: ''
};

function reducer(state, action) {
  switch (action.type) {
    case 'setField':
      return {
        ...state,
        [action.field]: action.value
      };
    case 'reset':
      return initialState;
    default:
      throw new Error();
  }
}

function SignupForm() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleChange = (e) => {
    dispatch({ type: 'setField', field: e.target.name, value: e.target.value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(state);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="username"
        value={state.username}
        onChange={handleChange}
        placeholder="Username"
      />
      <input
        type="email"
        name="email"
        value={state.email}
        onChange={handleChange}
        placeholder="Email"
      />
      <input
        type="password"
        name="password"
        value={state.password}
        onChange={handleChange}
        placeholder="Password"
      />
      <button type="submit">Submit</button>
      <button type="button" onClick={() => dispatch({ type: 'reset' })}>
        Reset
      </button>
    </form>
  );
}

export default SignupForm;
```

#### Best Practices

* **Keep the reducer function pure**: The reducer function should not have side effects. It should only compute and return the new state.
* **Use action types and payloads**: Define action types as constants to avoid typos and make the code more readable.
* **Consider performance**: For performance-critical applications, consider using `useMemo` and `useCallback` to prevent unnecessary re-renders.

### 3. `useMemo` Hook

#### Introduction

`useMemo` is a hook used to memoize the result of a computation. It returns a memoized value, and recomputes it only when one of its dependencies changes. This can optimize performance by preventing expensive calculations on every render.

#### Basic Usage

```jsx
import React, { useState, useMemo } from 'react';

function ExpensiveComponent({ count }) {
  const expensiveCalculation = (num) => {
    console.log('Calculating...');
    for (let i = 0; i < 1000000000; i++) {} // simulate expensive calculation
    return num * 2;
  };

  const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);

  return <div>Calculated Value: {memoizedValue}</div>;
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <ExpensiveComponent count={count} />
    </div>
  );
}

export default App;
```

#### Advanced Usage

In more complex scenarios, you might have multiple dependencies and need to ensure that the memoized value is recalculated only when necessary.

```jsx
import React, { useState, useMemo } from 'react';

function ProductList({ products }) {
  const sortedProducts = useMemo(() => {
    console.log('Sorting products...');
    return [...products].sort((a, b) => a.name.localeCompare(b.name));
  }, [products]);

  return (
    <ul>
      {sortedProducts.map((product) => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

function App() {
  const [products, setProducts] = useState([
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Orange' },
    { id: 3, name: 'Banana' },
  ]);

  const addProduct = () => {
    setProducts([...products, { id: products.length + 1, name: 'Mango' }]);
  };

  return (
    <div>
      <button onClick={addProduct}>Add Product</button>
      <ProductList products={products} />
    </div>
  );
}

export default App;
```

#### Best Practices

* **Use only when necessary**: `useMemo` should be used for expensive calculations that could cause performance issues if re-computed on every render.
* **Manage dependencies correctly**: Ensure all dependencies that affect the memoized value are included in the dependency array.
* **Combine with `useCallback`**: Often used together with `useCallback` to optimize both values and functions.

### 4. `useCallback` Hook

#### Introduction

`useCallback` is a hook used to memoize a function. It returns a memoized version of the callback that only changes if one of its dependencies changes. This can prevent unnecessary re-renders, particularly when passing callbacks to child components.

#### Basic Usage

```jsx
import React, { useState, useCallback } from 'react';

function Child({ onClick }) {
  console.log('Child render');
  return <button onClick={onClick}>Click me</button>;
}

function App() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <Child onClick={increment} />
    </div>
  );
}

export default App;
```

#### Advanced Usage

For more advanced usage, consider a scenario where you need to handle multiple callbacks with different dependencies.

```jsx
import React, { useState, useCallback } from 'react';

function List({ items, onItemClick }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index} onClick={() => onItemClick(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

function App() {
  const [items] = useState(['Apple', 'Orange', 'Banana']);
  const [selectedItem, setSelectedItem] = useState(null);

  const handleItemClick = useCallback((item) => {
    setSelectedItem(item);
  }, []);

  return (
    <div>
      <List items={items} onItemClick={handleItemClick} />
      {selectedItem && <p>Selected Item: {selectedItem}</p>}
    </div>
  );
}

export default App;
```

#### Best Practices

* **Use for stable references**: Use `useCallback` when passing callbacks to optimized child components that rely on reference equality to avoid unnecessary renders.
* **Manage dependencies correctly**: Ensure all dependencies that affect the callback are included in the dependency array.
* **useMemo**: Often used together with `useMemo` to optimize both values and functions.

### 5. Combining Hooks

Combining `useReducer`, `useMemo`, and `useCallback` can lead to highly optimized and maintainable code, especially in complex applications.

#### Example: Todo List Application

Let's build a Todo List application that demonstrates the combined usage of `useReducer`, `useMemo`, and `useCallback`.

```jsx
import React, { useReducer, useMemo, useCallback } from 'react';

// Initial State
const initialState = {
  todos: [],
  filter: 'all',
};

// Reducer Function
function reducer(state, action) {
  switch (action.type) {
    case 'addTodo':
      return { ...state, todos: [...state.todos, action.todo] };
    case 'toggleTodo':
      return {
        ...state,
        todos: state.todos.map((todo, index) =>
          index === action.index ? { ...todo, completed: !todo.completed } : todo
        ),
      };
    case 'setFilter':
      return { ...state, filter: action.filter };
    default:
      throw new Error();
  }
}

function TodoApp() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const filteredTodos = useMemo(() => {
    switch (state.filter) {
      case 'completed':
        return state.todos.filter((todo) => todo.completed);
      case 'active':
        return state.todos.filter((todo) => !todo.completed);
      default:
        return state.todos;
    }
  }, [state.todos, state.filter]);

  const addTodo = useCallback((text) => {
    dispatch({ type: 'addTodo', todo: { text, completed: false } });
  }, []);

  const toggleTodo = useCallback((index) => {
    dispatch({ type: 'toggleTodo', index });
  }, []);

  const setFilter = useCallback((filter) => {
    dispatch({ type: 'setFilter', filter });
  }, []);

  return (
    <div>
      <TodoList todos={filteredTodos} onToggle={toggleTodo} />
      <AddTodoForm onAddTodo={addTodo} />
      <FilterButtons filter={state.filter} onSetFilter={setFilter} />
    </div>
  );
}

function TodoList({ todos, onToggle }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index} onClick={() => onToggle(index)}>
          {todo.text} {todo.completed ? '(Completed)' : ''}
        </li>
      ))}
    </ul>
  );
}

function AddTodoForm({ onAddTodo }) {
  const [text, setText] = React.useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    onAddTodo(text);
    setText('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a new todo"
      />
      <button type="submit">Add</button>
    </form>
  );
}

function FilterButtons({ filter, onSetFilter }) {
  return (
    <div>
      <button disabled={filter === 'all'} onClick={() => onSetFilter('all')}>
        All
      </button>
      <button disabled={filter === 'active'} onClick={() => onSetFilter('active')}>
        Active
      </button>
      <button disabled={filter === 'completed'} onClick={() => onSetFilter('completed')}>
        Completed
      </button>
    </div>
  );
}

export default TodoApp;
```

In this example:

* **State Management**: `useReducer` manages the state of todos and the current filter.
* **Performance Optimization**: `useMemo` memoizes the filtered todos list to avoid unnecessary recalculations.
* **Callback Optimization**: `useCallback` memoizes the functions to prevent unnecessary re-renders of child components.

### 6. Conclusion

Understanding and effectively using `useReducer`, `useMemo`, and `useCallback` can significantly enhance your ability to manage complex state logic and optimize performance in React applications. These hooks provide powerful tools for creating more efficient, maintainable, and predictable React components.

* **`useReducer`**: Ideal for managing complex state logic with multiple actions and state transitions.
* **`useMemo`**: Useful for memoizing expensive calculations to prevent unnecessary re-renders.
* **`useCallback`**: Helps in memoizing functions to maintain stable references, avoiding unnecessary re-renders of child components.

By combining these hooks, you can build sophisticated and performant React applications.
