昨天已經做出來已完成、未完成的效果了,但是只有style上的轉換,沒有記錄起來。今天要把已完成、未完成的狀態記錄在state中,並做出已完成、未完成的分頁。
昨天在寫已完成、未完成的切換是在Item
component中用isDone
這個state偵測狀態,但這樣沒辦法儲存他的狀態,因此我們需要把它儲存在更上層的state中,也就是儲存項目Array的item
state。
首先,在新增項目時增加一個isDone
的property:
// Input.js
const clickHandler = (e)=>{
setItem([...item, {content: input, id: uuidv4(), isDone: false}]);
setInput("")
}
並在App
中加入done
、yet
state,來記錄已完成、未完成的事項;此外,寫一個useEffect
來偵測item state,當item state發生變化時就更改done state和yet state:
// App.js
const [done, setDone] = useState([]);
const [yet, setYet] = useState([]);
useEffect(() => {
// 若項目長度不為0,處理項目
if (item.length !== 0) {
const newDone = item.filter(i => i.isDone === true);
const newYet = item.filter(i => i.isDone === false);
setDone(newDone);
setYet(newYet);
} else {
// 若長度為0,則將done、yet設為空Array
setDone([]);
setYet([]);
}
},[item])
這樣,當項目新增時,便會透過side effect加入yet
state中。
接著,要儲存已完成項目,當點選checkbox時,isDone
的布林值就會轉換,轉換後的物件會被儲存在App
中的item
state裡面,且會觸發side effect重新處理done
、yet
state:
// Item.js
const checkHandler = (e) => {
// 轉換isDone的布林值
let newItem = item.map(i => {
if (i.id === id) {
if (e.target.checked) {
i.isDone = true;
} else {
i.isDone = false;
}
}
return i;
})
setItem(newItem);
}
return (
<Card>
<Form.Check type="checkbox" id={`check-${id}`} className="d-flex align-items-center">
{/*加入checkHandler,並且偵測isDone的狀態顯示是否checked*/}
<Form.Check.Input type="checkbox" className="m-0" onClick={checkHandler} checked={ isDone } />
{/* … */}
</Form.Check>
</Card>
)
此外,也需要將單一項目的isDone傳入Item
component:
// List.js
// …
const list = itemList.map((i, index)=>{
const {content, id, isDone} = i;
return (
{/*…*/}
<Item content={content} id={id} setIsEdit={setIsEdit} item={item} setItem={setItem} done={done} setDone={setDone} yet={yet} setYet={setYet} isDone={isDone}/>
)
這樣就成功儲存已完成、未完成的狀態了,但我們還看不出來,因此要來做做已完成、未完成的分頁。
首先,新增一個分頁導覽列Tab
component,並把List
component放入:
// Tab.js
const Tab = ({item, done, yet, setItem, setDone, setYet}) => {
return (
<div>
{/*預設顯示「全部」頁*/}
<Nav variant="tabs" fill defaultActiveKey="all">
<Nav.Item>
<Nav.Link eventKey="all" id="all">
全部
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="done" id="done">
已完成
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="yet" id="yet">
未完成
</Nav.Link>
</Nav.Item>
</Nav>
<List item={item} done={done} yet={yet} setItem={setItem} setDone={setDone} setYet={setYet}/>
</div>
)
}
接著要加入一個state記錄現在在哪一頁,以及使用onClick function偵測換頁:
// Tab.js
const [tab, setTab]=useState("all");
const tabHandler = (e)=>{
setTab(e.target.id);
}
return (
<div>
{/* … */}
<Nav.Link eventKey="all" id="all" onClick={tabHandler}>
全部
</Nav.Link>
{/* … */}
<Nav.Link eventKey="done" id="done" onClick={tabHandler}>
已完成
</Nav.Link>
{/* … */}
<Nav.Link eventKey="yet" id="yet" onClick={tabHandler}>
未完成
</Nav.Link>
{/*…*/}
</div>
)
這樣就能實現換頁效果,但是目前葉面上還顯示不出東西,因此我們要使用List
component來顯示項目。
為List
傳入tab state,並根據現在在哪一頁顯示項目:
// Tab.js
// …
// 傳入tab state
<List tab={tab} item={item} done={done} yet={yet} setItem={setItem} setDone={setDone} setYet={setYet}/>
// List.js
// …
// 根據tab判斷itemList要放入哪個Array
let itemList;
if(tab==="all"){
itemList = item;
}else if(tab === "done"){
itemList = done;
}else if(tab==="yet"){
itemList = yet;
}
// 並使用itemList去loop
const list = itemList.map((i, index)=>{
// …
}
最後,把原本在App
中的List
改成Tab
:
// App.js
const App = ()=>{
// …
return (
<>
<Container>
<Row>
{/*… */}
<Input input={input} setInput={setInput} item={item} setItem={setItem}/>
<Tab item={item} done={done} yet={yet} setItem={setItem} setDone={setDone} setYet={setYet}/>
{/* … */}
</Row>
</Container>
</>
)
}
完整程式碼在這裡:codepen
這樣就完成已完成、未完成狀態切換的功能,以及分頁功能了!