Redux for Beginners: A Practical Guide with Examples

abhinav bussa
3 min readFeb 3, 2025

--

Redux is one of the most popular state management libraries in the React ecosystem. While it might seem intimidating at first, understanding its core concepts will help you manage complex application states with ease. In this guide, we’ll break down Redux into digestible pieces and build a simple todo application to demonstrate its usage.

What is Redux?

Redux is a predictable state container for JavaScript applications. It helps you write applications that behave consistently, run in different environments, and are easy to test. Think of it as a central store that holds all your application’s data.

Core Concepts

1. Store
The store is like a brain that holds your entire application’s state. There can only be a single store in a Redux application.

2. Actions
Actions are plain JavaScript objects that describe WHAT happened in your application. They must have a `type` property.

3. Reducers
Reducers specify HOW your application’s state changes in response to actions. They are pure functions that take the previous state and an action and return a new state.

Building a Todo Application with Redux

Let’s create a simple todo application to understand these concepts better.

Step 1: Setting Up Redux

First, install the necessary dependencies:

npm install redux react-redux

Creating Actions


// actions.js
export const ADD_TODO = ‘ADD_TODO’;
export const TOGGLE_TODO = ‘TOGGLE_TODO’;
export const addTodo = (text) => ({
type: ADD_TODO,
payload: {
text,
id: Date.now(),
completed: false
}
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
payload: { id }
});
```

Step 3: Creating a Reducer


// reducer.js
import { ADD_TODO, TOGGLE_TODO } from ‘./actions’;
const initialState = {
todos: []
};

const todoReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
…state,
todos: […state.todos, action.payload]
};

case TOGGLE_TODO:
return {
…state,
todos: state.todos.map(todo =>
todo.id === action.payload.id
? { …todo, completed: !todo.completed }
: todo
)
};

default:
return state;
}
};

export default todoReducer;

Creating the Store


// store.js
import { createStore } from ‘redux’;
import todoReducer from ‘./reducer’;

const store = createStore(todoReducer);

export default store;

Connecting Redux to React


// App.js
import React from ‘react’;
import { Provider } from ‘react-redux’;
import store from ‘./store’;
import TodoList from ‘./TodoList’;

function App() {
return (
<Provider store={store}>
<div className=”App”>
<h1>Todo List with Redux</h1>
<TodoList />
</div>
</Provider>
);
}

export default App;

Creating the Todo List Component


// TodoList.js
import React, { useState } from ‘react’;
import { useDispatch, useSelector } from ‘react-redux’;
import { addTodo, toggleTodo } from ‘./actions’;

function TodoList() {
const [input, setInput] = useState(‘’);
const dispatch = useDispatch();
const todos = useSelector(state => state.todos);
const handleSubmit = (e) => {
e.preventDefault();
if (!input.trim()) return;
dispatch(addTodo(input));
setInput(‘’);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder=”Add a todo”
/>
<button type=”submit”>Add</button>
</form>
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => dispatch(toggleTodo(todo.id))}
style={{ textDecoration: todo.completed ? ‘line-through’ : ‘none’ }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}

export default TodoList;

Redux Data Flow

Let’s understand how data flows in our todo application:

1. User types in the input field and clicks “Add”
2. The `handleSubmit` function dispatches an `ADD_TODO` action
3. The reducer processes the action and adds a new todo to the state
4. The store updates with the new state
5. React re-renders the components that are connected to the updated state

Best Practices

1. **Keep Redux State Minimal**: Only store what you really need in Redux. Local component state is fine for UI state.

2. **Use Action Creators**: They help standardize how actions are created and make code more maintainable.

3. **Immutable Updates**: Always return new state objects in reducers, never modify existing state.

4. **Meaningful Action Types**: Use descriptive strings for action types, often in the format `domain/eventName`.

When to Use Redux

Redux is great for:
- Large applications with complex state management needs
- Cases where many components need to access the same state
- Applications with frequent state updates
- When you need to implement undo/redo functionality

However, for smaller applications, Redux might be overkill. Consider using React’s built-in Context API or local state for simpler use cases.

Conclusion

Redux might seem complex at first, but its core concepts are straightforward. By following the pattern of actions, reducers, and store, you can build predictable and maintainable applications. Start small, understand the basics, and gradually incorporate more advanced concepts as needed.

Remember: Redux is just a tool. Use it when it solves your problems, not because it’s popular.

Additional Resources

- [Official Redux Documentation](https://redux.js.org/)
- [Redux Toolkit](https://redux-toolkit.js.org/) — The official, opinionated toolset for Redux
- [Redux DevTools](https://github.com/reduxjs/redux-devtools) — For debugging Redux applications

Happy coding!

--

--

abhinav bussa
abhinav bussa

Written by abhinav bussa

Javascript Developer | Creative | Energetic | Enthusiast

No responses yet