官方文檔的todolist示例~
1. 安裝
npm install recoil 或 yarn add recoil
2. 具體實現
todoList是一個對象數組,將todoListState作為一個原子, 初始值為空數組。
數組每一項對象屬性有{id,text, isComplete}
const todoListState = atom({
key : 'todoListState',
default: [],
})
使用useRecoilValue()
鉤子將todoListState運用于 TodoList
組件中。
function TodoList() {
const todoList = useRecoilValue(todoListState);
return (
<>
{/* <TodoListStats /> */}
{/* <TodoListFilters /> */}
<TodoItemCreator />
{todoList.map((todoItem) => (
<TodoItem key={todoItem.id} item={todoItem} />
))}
</>
);
}
(被注釋的組件在下文實現~)
要創建一個新的todo項,我們需要使用setter函數去更新todoListState的內容----在 TodoItemCreator 組件中使用useSetRecoilState()
鉤子。
function TodoItemCreator(){
const [inputValue, setInputValue] = useState('');
const setTodoList = useSetRecoilState(todoListState);
const addItem = () => {
setTodoList((oldTodoList) => [
...oldTodoList,
{
id: uuid(),
text: inputValue,
isComplete: false,
}
]);
setInputValue('')
}
const onChange = ({target: {value}}) => {
setInputValue(value);
};
return (
<div>
<input type="text" value={inputValue} onChange={onChange} />
<button onClick={addItem}>Add</button>
</div>
);
}
todoItem 組件將展示todoListState中所有的值。 我們使用 useRecoilState()
獲取值,并且使用setter函數去更新todoList的值,更新某項的狀態,或者刪除某項。
function TodoItem({item}) {
const [todoList, setTodoList] = useRecoilState(todoListState);
const index = todoList.findIndex((listItem) => listItem === item);
const editItemText = ({target: {value}}) => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
text: value,
});
setTodoList(newList);
};
const toggleItemCompletion = () => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
isComplete: !item.isComplete,
});
setTodoList(newList);
};
const deleteItem = () => {
const newList = removeItemAtIndex(todoList, index);
setTodoList(newList);
};
return (
<div>
<input type="text" value={item.text} onChange={editItemText} />
<input
type="checkbox"
checked={item.isComplete}
onChange={toggleItemCompletion}
/>
<button onClick={deleteItem}>X</button>
</div>
);
}
function replaceItemAtIndex(arr, index, newValue) {
return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}
function removeItemAtIndex(arr, index) {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
到此 一個基礎的增刪改的todoList就實現啦。
之后我們使用selector來呈現基于todoList數組的衍生數據,通過函數方法獲取及呈現與todoList相關的數據。如:
- 根據某個條件過濾出的數據
- 通過計算獲取數組的一些屬性,如completed的item數量, 或者completed item 所占的百分比。
- TodoListFilters
我們將過濾條件設置為一個原子。原子的值范圍為: "Show All", "Show Completed", "Show Uncompleted"。默認值為‘show all’.
const todoListFilterState = atom({
key: 'todoListFilterState',
default: 'Show All',
});
我們使用todoListFilterState
和todoListState
, 構建filteredTodoListState
selector 來衍生出過濾的list數據。
const filteredTodoListState = selector({
key: 'filteredTodoListState',
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
這兩個依賴項的變化,當其中一個變化時,filteredTodoListState會被重新執行。
然后我們來實現 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
展現todoList數量,completed數量, uncomplted數量, completed所占百分比。
創建todoListStatsState
seletor。
const todoListStatsState = selector({
key: 'todoListStatsState',
get: ({get}) => {
const todoList = get(filteredTodoListState);
const totalNum = todoList.length;
const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
const totalUncompletedNum = totalNum - totalCompletedNum;
const percentCompleted = totalNum === 0 ? 0 : totalCompletedNum / totalNum;
return {
totalNum,
totalCompletedNum,
totalUncompletedNum,
percentCompleted,
};
},
});
讀取 todoListStatsState
。
function TodoListStats() {
const {
totalNum,
totalCompletedNum,
totalUncompletedNum,
percentCompleted,
} = useRecoilValue(todoListStatsState);
const formattedPercentCompleted = Math.round(percentCompleted * 100);
return (
<ul>
<li>Total items: {totalNum}</li>
<li>Items completed: {totalCompletedNum}</li>
<li>Items not completed: {totalUncompletedNum}</li>
<li>Percent completed: {formattedPercentCompleted}</li>
</ul>
);
}