Store, Slices & Redux Toolkit
createSlice & configureStore
import { createSlice, configureStore } from '@reduxjs/toolkit';
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
// Looks like mutation, but Immer (used internally) produces a correct
// immutable update behind the scenes — no manual spreading needed
added(state, action) {
state.push({ id: nanoid(), text: action.payload, completed: false });
},
toggled(state, action) {
const todo = state.find(t => t.id === action.payload);
if (todo) todo.completed = !todo.completed;
},
},
});
// Auto-generated action creators + reducer, no hand-written boilerplate
export const { added, toggled } = todosSlice.actions;
export default todosSlice.reducer;
const store = configureStore({
reducer: { todos: todosSlice.reducer },
// configureStore wires in Redux DevTools, redux-thunk, and dev-mode
// mutation/serializability checks by default — no manual setup needed
});Connecting React Components
// App root
import { Provider } from 'react-redux';
<Provider store={store}>
<App />
</Provider>
// Inside a component
import { useSelector, useDispatch } from 'react-redux';
import { added, toggled } from './todosSlice';
function TodoList() {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
return (
<div>
{todos.map(todo => (
<li key={todo.id} onClick={() => dispatch(toggled(todo.id))}>
{todo.text}
</li>
))}
<button onClick={() => dispatch(added('Buy milk'))}>Add</button>
</div>
);
}Testing Reducers
import reducer, { added } from './todosSlice';
test('adds a todo', () => {
const state = reducer([], added('Buy milk'));
expect(state).toHaveLength(1);
expect(state[0].text).toBe('Buy milk');
});
// A reducer is a pure function — test it in complete isolation,
// no component rendering or mocking needed.Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free