選取器
選取器代表一塊衍生狀態。您可以將衍生狀態視為將狀態傳遞給純函數的輸出,而該純函數可由所述狀態衍生出新值。
衍生狀態是很強大的概念,因為它能讓我們建構取決於其他資料的動態資料。在我們的待辦事項應用程式範例中,下列內容被視為衍生狀態
- 已篩選的待辦事項清單:透過建立一個根據特定條件(例如,篩選出已完成的項目)來過濾掉某些項目的新清單,從完整的待辦事項清單中衍生出來。
- 待辦事項清單統計資料:透過計算清單的實用屬性(例如,清單中的項目總數、已完成項目的數量,以及已完成項目的百分比)從完整的待辦事項清單中衍生出來。
若要實作已篩選的待辦事項清單,我們需要選擇一組可將其值儲存在原子中篩選條件。我們將使用的篩選選項為:「全部顯示」、「顯示已完成」和「顯示未完成」。預設值為「全部顯示」
const todoListFilterState = atom({
key: 'TodoListFilter',
default: 'Show All',
});
透過使用 todoListFilterState
和 todoListState
,我們可以建構一個 filteredTodoListState
選取器,以衍生已篩選清單
const filteredTodoListState = selector({
key: 'FilteredTodoList',
get: ({get}) => {
const filter = get(todoListFilterState);
const list = get(todoListState);
switch (filter) {
case 'Show Completed':
return list.filter((item) => item.isComplete);
case 'Show Uncompleted':
return list.filter((item) => !item.isComplete);
default:
return list;
}
},
});
filteredTodoListState
在內部追蹤兩個依賴項:todoListFilterState
和 todoListState
,因此若其中任一項變更,它就會重新執行。
從元件角度來看,可以使用與讀取原子相同的 hooks 來讀取選取器。然而,需要注意的是,某些 hooks 只適用於可寫入狀態(即
useRecoilState()
)。所有原子都是可寫入狀態,但只有部分選取器被視為可寫入狀態(同時具有get
和set
屬性的選取器)。如需瞭解有關此主題的更多資訊,請參閱核心概念頁面。
只要在 TodoList
元件中變更一行即可顯示已過濾的 todoList
function TodoList() {
// changed from todoListState to filteredTodoListState
const todoList = useRecoilValue(filteredTodoListState);
return (
<>
<TodoListStats />
<TodoListFilters />
<TodoItemCreator />
{todoList.map((todoItem) => (
<TodoItem item={todoItem} key={todoItem.id} />
))}
</>
);
}
請注意,由於 todoListFilterState
已指定預設值 "Show All"
,因此使用者介面會顯示所有待辦事項。如需變更篩選條件,我們需要實作 TodoListFilters
元件
function TodoListFilters() {
const [filter, setFilter] = useRecoilState(todoListFilterState);
const updateFilter = ({target: {value}}) => {
setFilter(value);
};
return (
<>
Filter:
<select value={filter} onChange={updateFilter}>
<option value="Show All">All</option>
<option value="Show Completed">Completed</option>
<option value="Show Uncompleted">Uncompleted</option>
</select>
</>
);
}
利用數行程式碼,我們便能實作篩選器!我們將使用相同概念來實作 TodoListStats
元件。
我們要顯示下列統計資料
- 待辦事項總數
- 已完成事項總數
- 未完成事項總數
- 已完成事項的百分比
雖然我們可以為每個統計資料建立一個選取器,但是一個較簡單的方法是建立一個選取器,使其回傳包含我們所需資料的物件。我們將這個選取器稱為 todoListStatsState
const todoListStatsState = selector({
key: 'TodoListStats',
get: ({get}) => {
const todoList = get(todoListState);
const totalNum = todoList.length;
const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
const totalUncompletedNum = totalNum - totalCompletedNum;
const percentCompleted = totalNum === 0 ? 0 : totalCompletedNum / totalNum * 100;
return {
totalNum,
totalCompletedNum,
totalUncompletedNum,
percentCompleted,
};
},
});
要讀取 todoListStatsState
的值,請再使用一次 useRecoilValue()
function TodoListStats() {
const {
totalNum,
totalCompletedNum,
totalUncompletedNum,
percentCompleted,
} = useRecoilValue(todoListStatsState);
const formattedPercentCompleted = Math.round(percentCompleted);
return (
<ul>
<li>Total items: {totalNum}</li>
<li>Items completed: {totalCompletedNum}</li>
<li>Items not completed: {totalUncompletedNum}</li>
<li>Percent completed: {formattedPercentCompleted}</li>
</ul>
);
}
總之,我們已建立一個符合所有需求的待辦事項清單應用程式
- 新增待辦事項
- 編輯待辦事項
- 刪除待辦事項
- 篩選待辦事項
- 顯示有用的統計資料