In this article we will learn the very basics of redux toolkit and make a simple app using react and redux toolkit. I have kept it very simple with respect to styling, using a little bit css, but you guys can choose whatever you need for styling.
What Is Redux ?
Redux is an open-source JavaScript library for managing and centralizing application state. It helps us to write JavaScript apps that behave consistently across client, server, and native environments and are easy to test. While it's mostly used as a state management tool with React.
What Is RTK And Why Do We Need It ?
According to the official documentation,
Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.
It was originally created to help address three common concerns about Redux:
- "Configuring a Redux store is too complicated"
- "I have to add a lot of packages to get Redux to do anything useful"
- "Redux requires too much boilerplate code"
You can read more about redux toolkit here.
So, lets take a peek at what we are looking to build
Step 1 - Initialize App and Install Dependencies
npx create-react-app rtk-todo --use-npm
this step initializes a basic react-app for you using npm. Next you need to install the dependencies.
npm install react-redux @reduxjs/toolkit
now run this following command to start the development server
npm start
Step 2 - Setup Your Folder Structure
Create a structure like this inside the src of your project, we will create our components in the components folder and put all store related stuff inside the redux folder.
📦src
┣ 📂components
┃ ┣ 📜AddTodo.js
┃ ┣ 📜TodoItem.js
┃ ┗ 📜TodoList.js
┣ 📂redux
┃ ┣ 📜store.js
┃ ┗ 📜tasksSlice.js
┣ 📜App.js
┣ 📜index.css
┗ 📜index.js
Step 3 - Redux
Configuring a Redux is an extremely simple process with Redux toolkit. Let's start with writing our first slice.
import { createSlice } from "@reduxjs/toolkit"; | |
export const tasksSlice = createSlice({ | |
name: "tasks", | |
initialState:[], | |
reducers:{ | |
addTask: (state, action)=>{ | |
const newTask = { | |
id: new Date(), | |
name: action.payload.task | |
} | |
state.push(newTask); | |
}, | |
deleteTask: (state, action)=>{ | |
return state.filter((item) => item.id !== action.payload.id); | |
} | |
} | |
}); | |
export const {addTask, deleteTask} = tasksSlice.actions; | |
export default tasksSlice.reducer; |
Here we are using createSlice
, it takes a 'slice name', 'initial state' and an object of reducers, and then generates corresponding action generators and action creators to reducers, and each reducer has access to the state and the action.
Then using this we need to configure out store. For that we need to use configueStore
. It is an abstraction over the standard Redux createStore function adding some defaults easing our setup process.
import { configureStore } from "@reduxjs/toolkit"; | |
import taskReducer from "./tasksSlice"; | |
export default configureStore({ | |
reducer:{ | |
tasks: taskReducer, | |
} | |
}); |
Here we pass the slice reducer we created.
Note : If there is one function (like in our case), it is considered as the root reducer. If we have more than one a rootReducer
is created behind the scenes, using the combineReducers
functionality.
Next we just use this store in our index.js, like we used to do in normal redux.
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import './index.css'; | |
import App from './App'; | |
import store from "./redux/store"; | |
import { Provider } from "react-redux"; | |
ReactDOM.render( | |
<React.StrictMode> | |
<Provider store={store}> | |
<App /> | |
</Provider> | |
</React.StrictMode>, | |
document.getElementById('root') | |
); |
Step 4 - UI Components
Before building any UI, we should always visualize our component tree. In this app too we would follow a structure something like this.
App
┣ AddTodo
┗ TodoList
┗ TodoItem
In App.js we just call our components and wrap them together.
import React from 'react'; | |
import AddTodo from './components/AddTodo'; | |
import TodoList from './components/TodoList'; | |
const App = () => { | |
return ( | |
<div className="app"> | |
<h1 className="app-title">My Tasks</h1> | |
<AddTodo /> | |
<TodoList /> | |
</div> | |
); | |
}; | |
export default App; |
Next, we make a way to enter the task and dispatch the addTask
action.
import React, { useState } from 'react'; | |
import { useDispatch } from "react-redux"; | |
import { addTask } from "../redux/tasksSlice"; | |
const AddTodo = () => { | |
const [value, setValue] = useState(''); | |
const dispatch = useDispatch(); | |
const onSubmit = (event) => { | |
event.preventDefault(); | |
if(value.trim().length === 0) | |
{ | |
alert("Enter a task before adding !!"); | |
setValue(""); | |
return; | |
} | |
dispatch( | |
addTask({ | |
task: value | |
}) | |
); | |
setValue(""); | |
}; | |
return ( | |
<div className="add-todo"> | |
<input | |
type="text" | |
className="task-input" | |
placeholder="Add task" | |
value={value} | |
onChange={(event) => setValue(event.target.value)} | |
></input> | |
<button className="task-button" onClick={onSubmit}> | |
Save | |
</button> | |
</div> | |
); | |
}; | |
export default AddTodo; |
We can easily import the action from the slice and dispatch it using the useDispatch
function.
Now that we can add the task we need to display them. For that we can just access the state using useSelector
and map over the todo list to display as we want.
import React from 'react'; | |
import TodoItem from './TodoItem'; | |
import { useSelector } from "react-redux"; | |
const TodoList = () => { | |
const todos = useSelector((state)=>{ | |
return state.tasks; | |
}); | |
return ( | |
<ul className="tasks-list"> | |
{todos.map((todo) => ( | |
<TodoItem id={todo.id} title={todo.name} completed={todo.status} /> | |
))} | |
</ul> | |
); | |
}; | |
export default TodoList; |
Almost done, we just need to display each task and call the deleteTask
action just like we had called the addTask
action.
import React from 'react'; | |
import { useDispatch } from "react-redux"; | |
import { deleteTask } from "../redux/tasksSlice"; | |
const TodoItem = ({ id, title }) => { | |
const dispatch = useDispatch(); | |
const removeTask=()=>{ | |
dispatch( | |
deleteTask({ | |
id: id | |
}) | |
) | |
} | |
return ( | |
<li className="task-item"> | |
<div> | |
{title} | |
</div> | |
<div> | |
<button className="remove-task-button" onClick={()=>{ | |
removeTask(); | |
}}>Delete</button> | |
</div> | |
</li> | |
); | |
}; | |
export default TodoItem; |
So now if we try to check the progress, we can see that we can add or delete any task and it looks like this, and its quite bad.
So now we add the styles, for this tutorial I have only added basic CSS, but you guys can go wild, maybe use a UI framework like Antd or MaterialUI too, would surely look nice.
body{ | |
background-color: #edf5d1; | |
padding: 0px; | |
margin: 0px; | |
color: rgba(0,0,0,0.87); | |
font-family: 'Roboto', sans-serif; | |
} | |
.app{ | |
text-align: center; | |
background-color: white; | |
height: auto; | |
min-height: 100vh; | |
width : 40%; | |
margin: auto; | |
padding : 30px; | |
box-shadow: 2px 2px 20px #ececec; | |
} | |
.app-title{ | |
margin: 0; | |
margin-bottom: 30px; | |
} | |
.add-todo{ | |
margin: 30px 0px; | |
} | |
.task-input{ | |
height : 45px; | |
width : 300px; | |
border : 1px solid #e4f2f7; | |
border-radius: 6px; | |
padding : 2px 10px; | |
margin-right : 10px; | |
} | |
.task-input:focus { | |
box-shadow: 0 0 8px rgba(142,228,175,0.6); | |
outline: none; | |
} | |
.task-button{ | |
height : 49px; | |
width : 100px; | |
border : none; | |
border-radius: 6px; | |
background-color: #05386b; | |
color:white; | |
} | |
.task-button:hover{ | |
cursor: pointer; | |
} | |
.tasks-list{ | |
list-style: none; | |
margin: auto; | |
padding : 0px; | |
} | |
.task-item{ | |
margin: 15px 0px; | |
display: flex; | |
justify-content: space-between; | |
padding : 8px 40px; | |
} | |
.remove-task-button{ | |
background-color: #ac3b61; | |
height: 32px; | |
color: white; | |
border-radius: 4px; | |
width: 70px; | |
border:none; | |
} | |
.remove-task-button:hover{ | |
cursor: pointer; | |
} |
And thats done, it looks excatly like we aimed it to be.
Untill next time Happy Building !! 😎
Top comments (2)
Delete not working
In case you want to compare codes while building this, here is the code for this,
github.com/arunavamodak/redux_tool.... 😊