Reputation: 99
Details: I am using @reduxjs/toolkit and reactjs to build a simple todo app. I use @reduxjs/toolkit. And I want to add a filter list by All and completed \ not completed.
Questions: How I can add filter list to this todo list (All and completed \ not completed)?
todoSlice.js:
import { createSlice } from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [
{
id: 1,
completde: false,
title: 'todo 1',
completed: false,
},
{
id: 2,
completde: true,
title: 'todo 2',
completed: false,
},
],
},
reducers: {
addTodo(state, action) {
state.todos.push({
id: new Date().toISOString(),
title: action.payload.title,
});
},
toggleTodo(state, action) {
const toggleTodoItem = state.todos.find(
(todo) => todo.id === action.payload.id
);
toggleTodoItem.completed = !toggleTodoItem.completed;
},
removeTodo(state, action) {
state.todos = state.todos.filter((todo) => todo.id !== action.payload.id);
},
filterTodo(state, action) {
// state.filterKey = action.payload.key;
console.log(action.payload);
},
},
});
const { actions, reducer } = todoSlice;
export const { addTodo, removeTodo, toggleTodo, filterTodo } = actions;
export default reducer;
FilterTodo.js:
import { useDispatch } from 'react-redux';
import { filterTodo } from './todoSlice';
export default function TodoFilter({ key }) {
const dispatch = useDispatch();
return (
<>
<button onClick={dispatch(filterTodo('ALL'))}>All</button>
<button onClick={dispatch(filterTodo('COMPLETED'))}>Completed</button>
<button onClick={dispatch(filterTodo('NOT_COMPLETED'))}>
Not completed
</button>
</>
);
}
TodoList.js:
import TodoItem from './TodoItem';
import { useSelector } from 'react-redux';
export default function TodoList() {
const todos = useSelector((state) => state.todos.todos);
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} {...todo} />
))}
</ul>
);
}
... ... ....
Upvotes: 2
Views: 2107
Reputation: 1803
One of the several ways to filter is adding a filter string to the slice's state and later create a function to filter the array based on this string, to avoid typos i like to add an object with all the possible filters as well, if you are using typescript it can be a enum instead of an object. Take a look:
import { createSlice } from '@reduxjs/toolkit';
// the object to represent the filters
export const filters = {
ALL: "ALL",
COMPLETED: "COMPLETED",
NOT_COMPLETED: "NOT_COMPLETED"
}
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [
{
id: 1,
completde: false,
title: 'todo 1',
completed: false,
},
{
id: 2,
completde: true,
title: 'todo 2',
completed: false,
},
],
// default: show all todos
filterBy: filters.ALL
},
reducers: {
addTodo(state, action) {
state.todos.push({
id: new Date().toISOString(),
title: action.payload.title,
});
},
toggleTodo(state, action) {
const toggleTodoItem = state.todos.find(
(todo) => todo.id === action.payload.id
);
toggleTodoItem.completed = !toggleTodoItem.completed;
},
removeTodo(state, action) {
state.todos = state.todos.filter((todo) => todo.id !== action.payload.id);
},
filterBy(state, action) {
state.filterBy = action.payload
},
},
});
const { actions, reducer } = todoSlice;
export const { addTodo, removeTodo, toggleTodo, filterBy } = actions;
export default reducer;
import { useDispatch } from 'react-redux';
import { filterBy, filters } from './todoSlice';
export default function TodoFilter({ key }) {
const dispatch = useDispatch();
return (
<>
// use the filters object to dispatch the right payload
<button onClick={dispatch(filterBy(filters.ALL))}>All</button>
<button onClick={dispatch(filterBy(filters.COMPLETED))}>Completed</button>
<button onClick={dispatch(filterBy(filters.NOT_COMPLETED))}>
Not completed
</button>
</>
);
}
import TodoItem from './TodoItem';
import { useSelector } from 'react-redux';
// don't forget to change the path
import { filters } from 'path/to/todoSlice';
export default function TodoList() {
const todos = useSelector((state) => state.todos.todos);
const filter = useSelector((state) => state.todos.filterBy);
// the function to handle the filter logic
const filteredTodo = () => {
if(filter === filters.COMPLETED) {
return todos.filter(todo => todo.completed);
}
if(filter === filters.NOT_COMPLETED) {
return todos.filter(todo => !todo.completed)
}
// if none of above return all todos
return todos;
}
return (
<ul>
{filteredTodo().map((todo) => (
<TodoItem key={todo.id} {...todo} />
))}
</ul>
);
}
Upvotes: 4