首先我們先建立一個名為 SearchBar 的 component,並且在其中加上一個用於顯示資料的視窗 & 視窗的開關。
import React, { useState } from "react";
import cx from "classnames";
const Modal = ({ isOpen, onClose, originArr }) => {
const [inputValue, setInputValue] = useState("");
const [filteredData, setFilteredData] = useState([]);
const changeCase = (inputString, toLowerCase) => {
if (toLowerCase) {
return inputString.toLowerCase();
} else {
return inputString.toUpperCase();
}
};
const filterFunc = (keyword) => {
const newKeyword = changeCase(keyword, true);
const filteredData = originArr.data?.filter((data) => {
return data.title.toLowerCase().includes(newKeyword)&& keyword !== "";
});
return filteredData;
};
const handleOnChange = (e) => {
setInputValue(e.target.value);
const result = filterFunc(e.target.value);
setFilteredData(result);
};
return (
<div
className={cx(
"gap-[20px] rounded-xl absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-5 z-50 shadow-md border-2 flex flex-wrap justify-center items-center py-10 px-20",
{ block: isOpen, hidden: !isOpen }
)}
>
<div>
<label className="text-2xl">
請輸入資料:
<input
type="text"
value={inputValue}
onChange={(e) => handleOnChange(e)}
className="border-2"
/>
</label>
</div>
<div className="w-full text-center"></div>
<ul className="self-start w-full">
{filteredData?.length !== 0 && (
<div className="w-full text-center">
{filteredData &&
filteredData.map(
(data, index) =>
data && (
<li
className="my-2 py-2 border-b-2 flex justify-between"
data-todo={`todo${index}`}
key={index}
>
<span className="flex-1">{data.title}</span>
<div className="flex gap-[20px]"></div>
</li>
)
)}
</div>
)}
</ul>
<button
onClick={onClose}
className="bg-red-600 text-white px-4 py-2 rounded-md absolute right-2 top-2"
>
X
</button>
</div>
);
};
/**
* @description a component include toggleBtn & Modal
* @param {array} data
*/
const SearchBar = (data) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const handleOpenModal = () => {
setIsModalOpen(true);
};
const handleCloseModal = () => {
setIsModalOpen(false);
};
return (
<div>
<button
onClick={handleOpenModal}
className="self-center min-w-[80px] text-center rounded mx-2 border-2 px-4 py-1 cursor-pointer shadow-md transition-all bg-white hover:bg-[#e3e8eB] text-gray-500 text-xl font-bold"
>
搜尋todo
</button>
<Modal isOpen={isModalOpen} onClose={handleCloseModal} originArr={data} />
</div>
);
};
export default SearchBar;
我們可以看到這個 component 擁有一個顯示資料的視窗 & 視窗的開關,並且 Modal
接收的參數分別為是否顯示&原始資料,而SearchBar
則接收來自於父層的完整資料。
接下來我們整理一下資料流,看看是否和我們預期的一致。
首先SearchBar
負責接收父層傳入的 array,接下來資料會進入到Modal
,當 user 在輸入欄輸入資料的時候就會觸發onChange
,這時候我們就可以得到 keyWord 的資料,並且 keyword 會透過changeCase
這個 Function 轉變為小寫,而剛剛傳入的資料則會透過 filter 這個 Method 找出 title 包含 keyWord 的資料,並回傳給我們,最後我們將篩選後的資料塞進filteredData
,最後在 return 的部分,使用 map 將我們獲得的資料 render 到畫面上。
這邊需要注意的地方就在於這個部分
const filteredData = originArr.data?.filter((data) => {
return data.title.toLowerCase().includes(newKeyword) && keyword !== "";
});
至於為什麼呢,因為當user將輸入欄位的資料清空,這時候條件會是string.includes(''),並且這會是成立的,所以我們會發現所有的資料都被return進入到filteredData
中,所以這邊我們需要加上一個條件,keyword
不能是空的。
這樣才能保證我們的的資料不會全部被render出來。