在上一章Todolist with React (3),使用 React-redux 完成了渲染任務清單、和任務新增刪除的動作,就讓我們繼續完成最後一個部分 — Filter 篩選器,控制任務清單的顯示。
切換上方的 Filter,可以切換對應的任務清單
首先先處理顯示篩選器按鈕的部分,被選擇的按鈕要顯示 Selected (黃底) 的樣式,總共會有三種狀態 SHOW_ALL
、SHOW_TODO
、SHOW_DONE
。
在 filter.js 定義控制 Filter 的 Reducer,State 參數的預設值為 SHOW_ALL
,目前尚未定義 Action,直接回傳當前原本的 State。然後要在 index.js 裡整合 Filter Reducer。
reducer/filter.js
import * as types from '../actions/ActionTypes';
export default function filter(state = 'SHOW_ALL', action) {
switch (action.type) {
default:
return state;
}
}
reducer/index.js
...
import filterReducer from './filter';
const todoApp = combineReducers({
filterReducer,
todosReducer
});
因為在 TaskList Component 內也會需要 Filter 的狀態來顯示對應的任務清單,所以我們在 TaskList Component 使用 useSelector
取得目前的 Filter 狀態,再把它傳入 Filter Component。
components/TaskList.js
...
import { useSelector } from "react-redux";
function TaskList() {
...
const filter = useSelector((store) => store.filterReducer);
const renderItems = ...
return (
<Wrapper>
<Filter selected = {filter}/>
...
</Wrapper>
);
}
利用 Styled-components 根據 Props 可以改變樣式的功能,只要按鈕是 Selected 的狀態,傳入的 active
就是 true
,會切換背景顏色變成黃色。
components/Filter.js
const Button = styled.div`
background-color: ${(props) => (props.active ? "#ffc236" : "#bebebe")};
...
`;
function Filter(props) {
return (
<ButtonContainer>
<Button active={props.selected === "SHOW_ALL"}>
ALL
</Button>
<Button active={props.selected === "SHOW_TODO"}>
TODO
</Button>
<Button active={props.selected === "SHOW_DONE"}>
DONE
</Button>
</ButtonContainer>
);
}
可以顯示被選擇的按鈕後,接下來要新增 Filter 切換的功能,在 ActionTypes.js 定義 SET_FILTER
動作。
actions/ActionTypes.js
...
export const SET_FILTER = 'SET_FILTER';
Step1:建立 Action Creator 產生設定 Filter 的動作 Function setFilter
,並取得切換的 filter 名稱。
actions/filter.js
import * as types from './ActionTypes';
// action creator
export function setFilter(filter){
return {
type: types.SET_FILTER,
filter
};
}
Step2:處理 Filter Reducer,讀取 action.type 為 SET_FILTER 時,直接回傳新的 Filter 狀態。
reducer/filter.js
import * as types from '../actions/ActionTypes';
export default function filter(state = 'SHOW_ALL', action) {
switch (action.type) {
case types.SET_FILTER:
return action.filter;
default:
return state;
}
}
Step3:完成 Action 和 Reducer 的設定後,在 Filter Component 利用 useDispatch()
來調用 Action。點擊 後直接呼叫 setFilter()
,傳入對應的篩選器名稱。
components/Filter.js
import { useDispatch } from "react-redux";
function Filter(props) {
const dispatch = useDispatch();
return (
<ButtonContainer>
<Button
active={...}
onClick={() => dispatch(actions.setFilter("SHOW_ALL"))}
>
ALL
</Button>
<Button
active={...}
onClick={() => dispatch(actions.setFilter("SHOW_TODO"))}
>
TODO
</Button>
<Button
active={...}
onClick={() => dispatch(actions.setFilter("SHOW_DONE"))}
>
DONE
</Button>
</ButtonContainer>
);
}
切換篩選器的同時,下方的任務清單也要可以根據狀態切換對應的列表,我們只在 forEach
裡面新增 if
條件式判斷,總共會有三種情況:
filter === "SHOW_ALL"
:當 Filter 是 SHOW_ALL
時,判斷式永遠為 true,顯示該任務。filter === "SHOW_TODO" && !item.isCompleted
:當 Filter 是 SHOW_TODO
時,任務的狀態 isCompleted
要是 false
,判斷式會為 true,才能顯示該任務。filter === "SHOW_DONE" && item.isCompleted
:當 Filter 是 SHOW_DONE
時,任務的狀態 isCompleted
要是 true,判斷式會為 true,才能顯示該任務。components/TaskList.js
function TaskList() {
...
const renderItems = () => {
let list = [];
tasks.forEach((item, index) => {
if (
(filter === "SHOW_ALL") ||
(filter === "SHOW_TODO" && !item.isCompleted) ||
(filter === "SHOW_DONE" && item.isCompleted)
) {
list.push(
<TaskItem key={item.taskName} task={{ ...item, idx: index }} />
);
}
});
return list;
};
return (
<Wrapper>
...
<TaskItemContainer>{renderItems()}</TaskItemContainer>
</Wrapper>
);
}
終於完成任務清單和篩選器的功能,剩下最後一個功能 — 完成任務的時候要可以勾選框框,設定任務的狀態。一樣在 ActionTypes.js 定義勾選任務的動作 TOGGLE_TASK。
actions/ActionTypes.js
...
export const TOGGLE_TASK = 'TOGGLE_TASK';
Step1:建立 Action Creator 產生勾選 Task 的動作 Function toggleTask
,並取得對應的任務索引值。
actions/todos.js
export function toggleTask(idx){
return {
type: types.TOGGLE_TASK,
idx
};
}
Step2:處理 Todo Reducer,讀取 action.type 為 TOGGLE_TASK 時,修改該任務的完成狀態並回傳的新 State。
reducer/todos.js
...
export default function todos(state = initialTasks, action) {
switch (action.type) {
case types.ADD_TASK:...
case types.DELETE_TASK:...
case types.TOGGLE_TASK:
let newState = [...state];
newState[action.idx].isCompleted = !newState[action.idx].isCompleted;
return newState;
default:...
}
}
Step3:完成 Action 和 Reducer 的設定後,在 TaskItem Component 利用 useDispatch()
來調用 Action。點擊 <CheckBox />
後直接呼叫 toggleTask()
,傳入對應的任務索引值。
components/TaskItem.js
function TaskItem(props) {
const dispatch = useDispatch();
return (
<Container>
<CheckBox
type="checkbox"
checked={...}
onChange={() => dispatch(actions.toggleTask(props.task.idx))}
/>
...
</Container>
);
}
這樣就完成了一個簡單的 Todolist 囉
如果文章中有錯誤的地方,要麻煩各位大大不吝賜教;喜歡的話,也要記得幫我按讚訂閱喔❤️