Skip to content

ReactJS - Using Flux

ReactJS - State Management with Redux Toolkit

Section titled “ReactJS - State Management with Redux Toolkit”

While Flux is a pattern, Redux is a popular library that helps implement it. Modern Redux development is greatly simplified by Redux Toolkit, which is now the recommended approach. This tutorial will guide you through setting up a simple Todo application using React and Redux Toolkit.

Step 1 - Install Redux Toolkit and React-Redux

Section titled “Step 1 - Install Redux Toolkit and React-Redux”

Open your terminal or command prompt in your React project’s root directory and run:

npm install @reduxjs/toolkit react-redux

Or if you are using Yarn:

yarn add @reduxjs/toolkit react-redux

A common practice with Redux Toolkit is to organize state logic by ‘features’ or ‘slices’. For our Todo app, we’ll create a features directory within src.

Your src folder might look something like this:

src/
├── app/
│ └── store.js # Redux store configuration
├── features/
│ └── todos/
│ ├── todosSlice.js # Reducer and actions for todos
│ └── Todos.jsx # React component for todos
├── App.js # Main application component
└── index.js # Entry point, renders App

Step 3 - Define a Redux Slice (todosSlice.js)

Section titled “Step 3 - Define a Redux Slice (todosSlice.js)”

A ‘slice’ is a collection of Redux reducer logic and actions for a single feature. Redux Toolkit’s createSlice function generates action creators and action types automatically.

import { createSlice } from '@reduxjs/toolkit';
let nextTodoId = 0;
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: {
reducer(state, action) {
state.push(action.payload); // Immer handles immutability here
},
prepare(text) {
return { payload: { id: nextTodoId++, text, completed: false } };
}
},
toggleTodo(state, action) {
const todo = state.find(todo => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
}
}
});
export const { addTodo, toggleTodo } = todosSlice.actions;
export default todosSlice.reducer;

Key points:

  • createSlice takes a name, an initial state, and an object of reducer functions.
  • Reducer functions can ‘mutate’ state directly thanks to Immer, which runs under the hood in Redux Toolkit.
  • prepare callback in addTodo allows customizing the action payload, here we generate a unique ID.
  • Action creators (addTodo, toggleTodo) are automatically generated and exported.

Step 4 - Configure the Redux Store (store.js)

Section titled “Step 4 - Configure the Redux Store (store.js)”

configureStore from Redux Toolkit simplifies store setup.

import { configureStore } from '@reduxjs/toolkit';
import todosReducer from '../features/todos/todosSlice';
export const store = configureStore({
reducer: {
todos: todosReducer,
// other reducers can be added here
},
});

Step 5 - Provide the Store to React (index.js)

Section titled “Step 5 - Provide the Store to React (index.js)”

Wrap your root application component with the <Provider> component from react-redux to make the store available to all components.

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
import './index.css'; // Assuming you have some basic styling
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

Step 6 - Use Redux State and Actions in Components (Todos.jsx and App.js)

Section titled “Step 6 - Use Redux State and Actions in Components (Todos.jsx and App.js)”

We’ll use the useSelector hook to read data from the store and useDispatch to dispatch actions.

import React, { useState, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addTodo, toggleTodo } from './todosSlice';
function Todos() {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
const [text, setText] = useState('');
const inputRef = useRef(null);
const handleAddTodo = (e) => {
e.preventDefault();
if (text.trim()) {
dispatch(addTodo(text.trim()));
setText('');
inputRef.current.focus();
}
};
return (
<div>
<form onSubmit={handleAddTodo}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
ref={inputRef}
placeholder="What needs to be done?"
/>
<button type="submit">Add Todo</button>
</form>
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => dispatch(toggleTodo(todo.id))}
style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer' }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
export default Todos;

Now, let’s integrate the Todos component into App.js.

import React from 'react';
import Todos from './features/todos/Todos';
import './App.css'; // Assuming you have some basic styling
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Todo List with Redux Toolkit</h1>
</header>
<main>
<Todos />
</main>
</div>
);
}
export default App;

With these steps, you have a functioning Todo application using React and Redux Toolkit. You can add new todos and toggle their completion status. This demonstrates the modern, simplified way of working with Redux.

Common Pitfalls & Further Learning:

  • Forgetting to wrap App in <Provider>.
  • Incorrectly selecting state with useSelector (ensure your state.featureName matches your store configuration).
  • For more complex applications, explore asynchronous logic with createAsyncThunk and middleware like Redux Thunk (included by default in Redux Toolkit).
  • Refer to the official Redux Toolkit documentation for advanced patterns: https://redux-toolkit.js.org/