user3178479
user3178479

Reputation: 99

@reduxjs/toolkit filter list

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

Answers (1)

Yago Biermann
Yago Biermann

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:

todoSlice.js:

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;

FilterTodo.js:

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>
    </>
  );
}

TodoList.js:

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

Related Questions