过滤器下拉菜单只工作一次-React



我正在尝试基于行代码(API中的一个值(创建一个下拉过滤器。过滤器只能工作一次。当第二次尝试筛选时,列表将变为空。我也希望在其他选项上进行筛选。

代码沙盒

export default function App() {
const [trains, setTrains] = useState([]);
const [color, setColor] = useState("");
const handleSelect = (val) => {
setColor(val);
let filteredColors = trains.filter((item) => {
return item.LineCode === val;
});
setTrains(filteredColors);
};
const fetchTrains = () => {
let api =
"https://api.wmata.com/TrainPositions/TrainPositions?contentType=json&api_key=e13626d03d8e4c03ac07f95541b3091b";
fetch(api)
.then((response) => response.json())
.then((data) => setTrains(data.TrainPositions))
.catch((error) => {
console.log("Error fetching and parsing data", error);
});
};
useEffect(() => {
fetchTrains();
}, []);
return (
<div className="App">
<h1>Trains</h1>
<LineColor trains={trains} changeOption={handleSelect} />
<div className="card-container">
{trains.map((item, index) => {
return (
<div className="card" key={index}>
<p>Line Code:{item.LineCode}</p>
</div>
);
})}
</div>
</div>
);
}
function LineColor(props) {
const [color, setColor] = useState("");
const handleSelect = (e) => {
setColor(e.target.value);
props.changeOption(e.target.value);
};
return (
<div>
<select value={color} onChange={handleSelect}>
<option value="RD">RD</option>
<option value="BL">BL</option>
<option value="YL">YL</option>
<option value="OR">OR</option>
<option value="GR">GR</option>
<option value="SV">SV</option>
<option value="null">NULL</option>
</select>
</div>
);
}

请提供任何帮助,我们将不胜感激。

import "./styles.css";
import { useEffect, useState, useMemo } from "react";
import LineColor from "./LineColor";
export default function App() {
const [trains, setTrains] = useState([]);
const [total, setTotal] = useState([]);
const [color, setColor] = useState("");
const handleSelect = (val) => {
setColor(val);
let filteredColors = total.filter((item) => {
return item.LineCode === val;
});
setTrains(filteredColors);
};
const fetchTrains = () => {
let api =
"https://api.wmata.com/TrainPositions/TrainPositions?contentType=json&api_key=e13626d03d8e4c03ac07f95541b3091b";
fetch(api)
.then((response) => response.json())
.then((data) => setTotal(data.TrainPositions))
.catch((error) => {
console.log("Error fetching and parsing data", error);
});
};
useEffect(() => {
fetchTrains();
}, []);
return (
<div className="App">
<h1>Trains</h1>
<LineColor trains={trains} changeOption={handleSelect} />
<div className="card-container">
{trains.map((item, index) => {
return (
<div className="card" key={index}>
<p>Line Code:{item.LineCode}</p>
</div>
);
})}
</div>
</div>
);
}
]);

下面介绍的可能是实现所需目标的一种解决方案(正如OP所指出的,并考虑到需要扩大规模以包括更多用于过滤的列(。

以下是主要组件:

import "./styles.css";
import { useEffect, useState } from "react";
import Dropdown from "./Dropdown";
export default function App() {
const [trains, setTrains] = useState([]);
const [ddOptions, setDdOptions] = useState({
LineCode: [],
DestinationStationCode: [],
TrainNumber: []
});
const filterTitles = {
LineCode: "Color",
DestinationStationCode: "Destination Station",
TrainNumber: "Train Number"
};
const [filters, setFilters] = useState({
LineCode: "",
DestinationStationCode: "",
TrainNumber: ""
});
const fetchTrains = () => {
let api =
"https://api.wmata.com/TrainPositions/TrainPositions?contentType=json&api_key=USE_YOUR_API_KEY_HERE";
fetch(api)
.then((response) => response.json())
.then((data) => setTrains(data.TrainPositions))
.catch((error) => {console.log("Error fetching and parsing data", error);
});
};
useEffect(() => {
fetchTrains();
}, []);
useEffect(() => {
// console.log("trains: ", trains);
setDdOptions((prev) => ({
...prev,
LineCode: [
...new Set(trains.map(({ LineCode }) => LineCode || "empty"))
].concat(["all"]),
DestinationStationCode: [
...new Set(
trains.map(
({ DestinationStationCode }) => DestinationStationCode || "empty"
)
)
].concat(["all"]),
TrainNumber: [
...new Set(trains.map(({ TrainNumber }) => TrainNumber || "empty"))
].concat(["all"])
}));
}, [trains]);
return (
<div className="App">
<h1>Trains</h1>
<div style={{ display: "flex" }}>
{Object.entries(ddOptions).map(([k, v]) => (
<Dropdown
key={k}
title={filterTitles[k]}
optionVal={filters[k]}
handleChange={(ev) => {
console.log("ev-tgt-val: ", ev.target.value);
setFilters((prev) => ({
...prev,
[k]: ev.target.value
}));
}}
optionsList={v}
/>
))}
</div>
<div className="card-container">
{trains
?.filter((train) =>
Object.entries(filters).reduce(
(fin, [k, v]) =>
fin &&
(!v ||
train[k] === v ||
(v === "empty" && !train[k]) ||
v === "all"),
true
)
)
?.map((item, index) => {
return (
<div className="card" key={index}>
<p>
Line Code: {item.LineCode} &emsp; &emsp; Destination Station
Code: {item.DestinationStationCode} &emsp; &emsp; Train
Number: {item.TrainNumber}
</p>
</div>
);
})}
</div>
</div>
);
}

以及用于渲染过滤框的Dropdown组件(它是一个受控组件(,选项如下:

import React from "react";
const Dropdown = (props) => {
const { optionVal, handleChange, optionsList, title } = props;
return (
<div>
{title} &emsp;
<select value={optionVal} onChange={handleChange}>
{optionsList?.concat()?.map((val, idx) => (
<option key={idx} value={val}>
{val}
</option>
))}
</select>
&emsp; &emsp;
</div>
);
};
export default Dropdown;

解释

  • OP代码的问题是由于trains状态在每次应用颜色filter时发生突变
  • 这是通过在渲染时不更改trains阵列,而是在移动中创建filtered阵列来解决的
  • 由于OP(在问题下方的评论中(表示,他们计划不仅对color,而且对其他列都具有类似的功能,因此将他们的子组件修改为一个受控组件,该组件足够通用,能够呈现不同类型的过滤器

增强功能

  • OP的代码对子组件内的线条颜色进行硬编码
  • 这意味着,如果添加了新颜色,则需要更改代码
  • 此外,如果删除或重命名了现有颜色,则将不再显示该颜色的结果
  • 不是硬编码,而是在trains阵列上使用.filter来收集每个filter的唯一列表
  • 将此列表传递给子组件以呈现下拉列表

打开的任务

编辑/更新:

  • 当用户选择其中一个下拉项时,动态更改可用选项
  • 如果用户选择";OR";线路颜色,则目的地站、列车号等只需要显示那些具有";OR";作为线条颜色(即,线条代码,在API结果中(

这现在在这里的codesandbox版本中处理。解决方案相当简单:只需在填充每个过滤器的下拉选项时应用现有过滤器。

我认为它实际上可能是

useEffect(() => {
fetchTrains();
}, [color]);

useEffect需要知道何时运行。

Use effect具有[]中提到的依赖项,因此如果colortrains中的任何一个更改为useEffect,则会呈现页面。可以有不止一个依赖项[trains,color]

为此,您应该使用useMemo,这样您就可以使用单一的真值来源trains,然后计算对它的调整(在这种情况下是过滤(。

例如,通过更改handleSelect以仅设置颜色:

const handleSelect = (val) => {
setColor(val);
};

然后,您可以过滤掉color状态,而不会丢失trains状态下的数据。


const filteredTrains = useMemo(
() =>
color
? trains.filter((item) => {
if (color === "null") {
return item.LineCode === null;
}
return item.LineCode === color;
})
: trains,
[color, trains]
);

然后,您可以使用新的filteredTrains值进行映射以设置您的卡:

{filteredTrains.map((item, index) => {
return (
<div className="card" key={index}>
<p>Line Code:{item.LineCode}</p>
</div>
);
})}

工作示例:

const { useEffect, useMemo, useState } = React;
function App() {
const [trains, setTrains] = useState([]);
useEffect(() => {
const fetchTrains = () => {
let api =
"https://api.wmata.com/TrainPositions/TrainPositions?contentType=json&api_key=e13626d03d8e4c03ac07f95541b3091b";
fetch(api)
.then((response) => response.json())
.then((data) => setTrains(data.TrainPositions))
.catch((error) => {
console.log("Error fetching and parsing data", error);
});
};
fetchTrains();
}, []);
const [color, setColor] = useState("");
const filteredTrains = useMemo(
() =>
color
? trains.filter((item) => {
if (color === "null") {
return item.LineCode === null;
}
return item.LineCode === color;
})
: trains,
[color, trains]
);
const lines = useMemo(
() => Array.from(new Set(trains.map((train) => train.LineCode))),
[trains]
);
return (
<div className="App">
<h1>Trains</h1>
<select value={color} onChange={e=>setColor(e.target.value)}>
{lines.map(line=>(<option key={line} value={line||'null'}>{line||'NULL'}</option>))}
</select>
<div className="card-container">
{filteredTrains.map((item, index) => {
return (
<div className="card" key={index}>
<p>Line Code:{item.LineCode}</p>
</div>
);
})}
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"/>

相关内容

  • 没有找到相关文章

最新更新