allCategories
是被认为是派生状态.它是从当前的data
状态和vegOnly
状态派生出来的.data
状态是在初始渲染时获取的,并在初始渲染时更新105,因此它不能用于计算初始catItems
状态.
派生状态不属于REACTIVE状态,因为它很容易从其他现有状态/props /等派生.这意味着UI应该直接引用计算的allCategory
array.
...
const [data, setData] = useState([]);
const [vegOnly, setVegOnly] = useState(false);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
"https://63b6952d1907f863aafa9342.mockapi.io/menu/"
);
const data = await response.json();
setData(data);
setMainArray(data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
...
const filteredData =
vegOnly === false ? data : data.filter((item) => item.Type === "veg");
//from above to here for filter veg only
const allCategory = [
"All",
...new Set(filteredData.map((curElem) => curElem.Category))
]; // 3rd step to make it array
...
<ul>
{allCategory.map((curClem, index) => {
return (
<li key={index} onClick={() => filterItem(curClem)}>
{curClem}
</li>
);
})}
</ul>
有相当多的冗余/不必要的状态.所有的"过滤数据"也被认为是派生状态.您应该存储数据数组的一次副本状态,以及过滤条件,而不是数组的重复项.
示例:
const App = () => {
const [data, setData] = useState([]); //solved from stackoverflow
const [search, setSearch] = useState("");
const [category, setCategory] = useState("All");
const [vegOnly, setVegOnly] = useState(false);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
"https://63b6952d1907f863aafa9342.mockapi.io/menu/"
);
const data = await response.json();
setData(data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
const handleVegInputChange = (e) => {
const { checked } = e.target;
setVegOnly(checked);
};
// filter the data inline by the different filtering conditons
// and selected category.
const filteredData = data
.filter((item) => {
return item.Name.toLowerCase().includes(search.toLowerCase());
})
.filter((item) => {
if (vegOnly) {
return item.Type === "veg";
}
return true;
})
.filter((item) => {
if (category !== "All") {
return item.Category === category;
}
return true;
});
// Derive the categories from the data array
const allCategory = [
"All",
...new Set(data.map((curElem) => curElem.Category))
]; // 3rd step to make it array
// Set the selected filtering category
const filterItem = (category) => {
setCategory(category);
};
return (
<div className="first">
...
...
<div className="resinfo2">
<div className="rescategory">
<ul>
{allCategory.map((curClem, index) => {
return (
<li
key={index}
// Style the selected category
style={{
color: curClem === category ? "lightgreen" : "inherit"
}}
onClick={() => filterItem(curClem)}
>
{curClem}
</li>
);
})}
</ul>
</div>
<div className="resitems">
<div className="box">
<input
type="text"
className="search-input"
placeholder="Search for dishes"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<img src="/images/search.png" />
</div>
<div className="contents">
{data ? (
<div>
<div className="checkbox">
<label>
<input
type="checkbox"
value={vegOnly}
onChange={handleVegInputChange}
/>
Veg Only
</label>
</div>
<div className="items">
{filteredData.map((item) => (
<div className="itemslist" key={item.id}>
<ul>
<li
className={
item.Type === "veg" ? "veg" : "non-veg"
}
></li>
<li>{item.Name}</li>
<li>₹ {item.Price}</li>
<img src="/images/pizza1.jpg" />
<div className="hr"></div>
</ul>
</div>
))}
</div>
</div>
) : (
<div className="loading">Loading...</div>
)}
</div>
</div>
<div className="rescart"></div>
</div>
</div>
);
};