Изучаю react, делаю простой todo list с @reduxjs/toolkit и возник вопрос как правильнее фильтровать задачи.
todoSlice.js
без фильтрации:
import { createSlice } from '@reduxjs/toolkit'
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [],
todosFilter: [],
ident: 0,
keyFilter: 'all'
},
reducers: {
add (state, action) {
state.todos.push({
id: state.ident,
title: action.payload.title,
completed: false
})
state.ident = state.ident + 1
},
remove (state, action) {
state.todos = state.todos.filter(todo => todo.id !== action.payload.id)
},
toggle (state, action) {
const editTodo = state.todos.find(todo => todo.id === action.payload.id)
editTodo.completed = !editTodo.completed
}
}
})
export const { add, remove, toggle } = todoSlice.actions
export default todoSlice.reducer
Вариант 1
todoSlice.js
с фильтрацией через дополнительный массив с фильтрованными элементами:
import { createSlice } from '@reduxjs/toolkit'
function filtered (key, todos) {
return todos.filter(todo => {
if (key === 'all') return true
if (key === 'completed' && todo.completed) return true
if (key === 'uncompleted' && !todo.completed) return true
})
}
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [],
todosFilter: [],
ident: 0,
keyFilter: 'all'
},
reducers: {
add (state, action) {
state.todos.push({
id: state.ident,
title: action.payload.title,
completed: false
})
state.todosFilter = filtered(state.keyFilter, state.todos)
state.ident = state.ident + 1
},
remove (state, action) {
state.todos = state.todos.filter(todo => todo.id !== action.payload.id)
// Фильтрация после удаления
state.todosFilter = filtered(state.keyFilter, state.todos)
},
toggle (state, action) {
const editTodo = state.todos.find(todo => todo.id === action.payload.id)
editTodo.completed = !editTodo.completed
// Фильтрация после смены выполнения задачи
state.todosFilter = filtered(state.keyFilter, state.todos)
},
filter (state, action) {
state.keyFilter = action.payload.key
state.todosFilter = filtered(state.keyFilter, state.todos)
}
}
})
export const { add, remove, toggle, filter } = todoSlice.actions
export default todoSlice.reducer
При каждом взаимодействии со state нужно синхронизировать два массива, основной и массив с фильтрованными элементами, что меня смущает.
Вариант 2
todoSlice.js
фильтрация со значением внутри объекта:
import { createSlice } from '@reduxjs/toolkit'
function filtered (key, todo) {
todo.validFilter = false
if (key === 'all') todo.validFilter = true
if (key === 'completed' && todo.completed) todo.validFilter = true
if (key === 'uncompleted' && !todo.completed) todo.validFilter = true
return todo
}
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [],
ident: 0,
keyFilter: 'all'
},
reducers: {
add (state, action) {
const newTodo = {
id: state.ident,
title: action.payload.title,
completed: false,
validFilter: false
}
const modNewTodo = filtered(state.keyFilter, newTodo)
state.todos.push(modNewTodo)
state.ident = state.ident + 1
},
remove (state, action) {
state.todos = state.todos.filter(todo => todo.id !== action.payload.id)
},
toggle (state, action) {
const editTodo = state.todos.find(todo => todo.id === action.payload.id)
editTodo.completed = !editTodo.completed
filtered(state.keyFilter, editTodo)
},
filter (state, action) {
state.keyFilter = action.payload.key
state.todos = state.todos.map(todo => {
return filtered(state.keyFilter, todo)
})
}
}
})
export const { add, remove, toggle, filter } = todoSlice.actions
export default todoSlice.reducer
В таком случае при рендере нужно проверять валидный ли элемент или нет. Этот вариант мне больше нравиться:
const TodoList = () => {
const listTodo = useSelector(state => state.todos.todos)
return (
<ul className='todo-list'>
{ listTodo.map(todo => {
if (todo.validFilter === false) return null
return <TodoItem key={todo.id} {...todo} />
})
}
</ul>
)
}
Какой из них более правильнее? Или есть вариант лучше этих двух?