Mastering Redux Toolkit: Building a Task Management App | React & Redux

WHAT TO KNOW - Sep 13 - - Dev Community

<!DOCTYPE html>





Mastering Redux Toolkit: Building a Task Management App | React & Redux

<br> body {<br> font-family: sans-serif;<br> margin: 0;<br> padding: 20px;<br> }<br> h1, h2, h3 {<br> margin-bottom: 10px;<br> }<br> code {<br> background-color: #f0f0f0;<br> padding: 5px;<br> border-radius: 3px;<br> }<br> pre {<br> background-color: #f0f0f0;<br> padding: 10px;<br> border-radius: 3px;<br> overflow-x: auto;<br> }<br> .image-container {<br> text-align: center;<br> margin-bottom: 20px;<br> }<br> img {<br> max-width: 100%;<br> }<br>



Mastering Redux Toolkit: Building a Task Management App | React & Redux



In the realm of modern web development, building complex and interactive applications often necessitates a robust state management solution. Redux, with its predictable state mutations and centralized data store, has emerged as a popular choice for this purpose. However, setting up and managing Redux can be cumbersome, often requiring boilerplate code. Thankfully, Redux Toolkit simplifies this process by offering a streamlined API and conventions for building efficient Redux applications.



This article dives into the world of Redux Toolkit, guiding you through building a practical task management app using React and Redux. We'll cover the core concepts, explore essential techniques, and provide step-by-step instructions for creating a functional and user-friendly application.



Why Redux Toolkit?



Before diving into the details, let's understand why Redux Toolkit is a game-changer for Redux development:


  • Simplified Setup: Redux Toolkit eliminates the need for tedious configurations, providing a streamlined and intuitive way to create Redux stores.
  • Pre-built Logic: It offers pre-built reducers and middleware that handle common Redux tasks, reducing boilerplate code and simplifying development.
  • Best Practices Enforced: By promoting best practices through conventions and utilities, Redux Toolkit ensures that your Redux logic remains clean, predictable, and maintainable.
  • Improved Developer Experience: It provides a more efficient and enjoyable development experience by streamlining the Redux workflow and reducing cognitive overhead.


With Redux Toolkit, you can focus on building your application's features while the toolkit takes care of the underlying Redux mechanics.



Setting Up the Project



Let's begin by setting up the project environment. We'll use Create React App for a quick and easy starting point.


npx create-react-app task-manager
cd task-manager


Next, install Redux Toolkit and any necessary dependencies:


npm install @reduxjs/toolkit react-redux


Creating the Redux Store



Let's define our Redux store and initial state. Create a file named store.js within the src directory:


import { configureStore } from '@reduxjs/toolkit';
import tasksReducer from './features/tasks/tasksSlice';

export const store = configureStore({
reducer: {
tasks: tasksReducer,
},
});



In this snippet, we use configureStore to create the Redux store. We then define the tasksReducer, which will handle the state related to our tasks. We'll create this reducer in the next step.



Defining the Tasks Slice



Now, create a folder named features within src and a file named tasksSlice.js inside it. This is where we'll define our tasks reducer using Redux Toolkit's createSlice function:


import { createSlice } from '@reduxjs/toolkit';

const initialState = {
tasks: [],
};

const tasksSlice = createSlice({
name: 'tasks',
initialState,
reducers: {
addTask: (state, action) => {
state.tasks.push(action.payload);
},
removeTask: (state, action) => {
const index = state.tasks.findIndex(task => task.id === action.payload);
state.tasks.splice(index, 1);
},
toggleComplete: (state, action) => {
const task = state.tasks.find(task => task.id === action.payload);
task.completed = !task.completed;
},
},
});

export const { addTask, removeTask, toggleComplete } = tasksSlice.actions;

export default tasksSlice.reducer;



In this slice, we define the initial state with an empty tasks array. We also define three reducers: addTask, removeTask, and toggleComplete. Each reducer modifies the state based on the specific action triggered. We export the reducer and the actions to be used in our React components.



Connecting the Store to React



Now, let's connect our Redux store to our React application. Wrap the root component of your application with the Provider component from react-redux:


import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(



);



This ensures that all components within the App component have access to the Redux store and its state.



Building the Task Management UI



Let's create a simple task management UI. Update the App.js file as follows:


import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addTask, removeTask, toggleComplete } from './features/tasks/tasksSlice';

function App() {
const [newTask, setNewTask] = useState('');
const tasks = useSelector(state => state.tasks.tasks);
const dispatch = useDispatch();

const handleInputChange = (event) => {
setNewTask(event.target.value);
};

const handleAddTask = () => {
if (newTask.trim() !== '') {
dispatch(addTask({ id: Date.now(), text: newTask, completed: false }));
setNewTask('');
}
};

const handleRemoveTask = (taskId) => {
dispatch(removeTask(taskId));
};

const handleToggleComplete = (taskId) => {
dispatch(toggleComplete(taskId));
};

return (


Task Manager




Add Task

    {tasks.map((task) => (
  • handleToggleComplete(task.id)} /> {task.text} handleRemoveTask(task.id)}>Delete
  • ))}


);
}

export default App;



In this code, we use useSelector to access the tasks from the Redux store and useDispatch to dispatch actions to update the store. We handle user input, add new tasks, remove tasks, and toggle task completion using the dispatched actions.



Now, when you run the application (npm start), you should see a simple task management UI where you can add, delete, and mark tasks as complete.



Task Manager App Screenshot


Advanced Concepts



Let's explore some advanced concepts that can further enhance your Redux Toolkit application:


  1. Async Operations

Redux Toolkit includes the createAsyncThunk function for handling asynchronous operations. It simplifies the process of handling side effects like fetching data from an API.

Let's say we want to fetch tasks from a server. We can modify the tasksSlice.js file to include an async thunk:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';


const initialState = {
tasks: [],
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
};

export const fetchTasks = createAsyncThunk('tasks/fetchTasks', async () => {
const response = await axios.get('/api/tasks');
return response.data;
});

const tasksSlice = createSlice({
name: 'tasks',
initialState,
reducers: {
addTask: (state, action) => {
state.tasks.push(action.payload);
},
removeTask: (state, action) => {
const index = state.tasks.findIndex(task => task.id === action.payload);
state.tasks.splice(index, 1);
},
toggleComplete: (state, action) => {
const task = state.tasks.find(task => task.id === action.payload);
task.completed = !task.completed;
},
},
extraReducers(builder) {
builder
.addCase(fetchTasks.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchTasks.fulfilled, (state, action) => {
state.status = 'succeeded';
state.tasks = action.payload;
})
.addCase(fetchTasks.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});

export const { addTask, removeTask, toggleComplete } = tasksSlice.actions;

export default tasksSlice.reducer;



In this example, we define an extraReducers section to handle the different states of the async thunk: pending, fulfilled, and rejected. We update the status and error properties in the state to track the fetch operation's progress.



In your App.js file, you can dispatch the fetchTasks action and display the results based on the status:


import React, { useEffect } from 'react';
// ... other imports

function App() {
// ... other code

const tasks = useSelector(state => state.tasks.tasks);
const status = useSelector(state => state.tasks.status);
const error = useSelector(state => state.tasks.error);

const dispatch = useDispatch();

useEffect(() => {
dispatch(fetchTasks());
}, [dispatch]);

// ... rest of the code

return (


Task Manager


{status === 'loading' &&

Loading tasks...

}
{status === 'failed' &&

{error}

}
{/* ... rest of the UI code */}

);
}

  1. Selectors

Selectors are functions that derive data from the Redux store. They provide a way to encapsulate complex logic and promote code reusability.

Let's say we want to get the number of incomplete tasks. We can create a selector function like this:

const selectIncompleteTasks = state => state.tasks.tasks.filter(task => !task.completed).length;

In your component, you can access this selector using useSelector:

const incompleteTasksCount = useSelector(selectIncompleteTasks);


  • Middleware

    Middleware provides a way to intercept actions before they reach the reducer. This allows for custom logic like logging, error handling, or network requests.

    Redux Toolkit includes built-in middleware like thunk and immutableStateInvariant. You can also create custom middleware to handle specific needs.

    Conclusion

    Redux Toolkit simplifies and enhances the Redux development experience. It empowers you to build complex applications with a clean, maintainable, and predictable architecture. We've explored key concepts like slices, reducers, actions, async operations, selectors, and middleware.

    By implementing these techniques and best practices, you can create robust and scalable Redux applications that are easier to understand and maintain. The task management app we built serves as a practical example demonstrating the power of Redux Toolkit in action.

    Remember to explore the comprehensive documentation and resources available for Redux Toolkit to further enhance your development process. Happy coding!

  • . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
    Terabox Video Player