#reactjs
#reactjs
Вопрос:
Существует массив данных, который необходимо отфильтровать при выборе опции в выбранном. Я приведу демонстрационные данные, но может быть много значений.
Если вы фильтруете только по одному значению, то данные отображаются правильно, но если вы используете несколько, то отображаются неправильно. Фильтр по интервалу дат, к сожалению, не работает
Как провести рефакторинг, чтобы фильтрация работала правильно и учитывала несколько фильтров
import React, {useState} from 'react'
import DatePicker from 'react-date-picker';
const items = [
{
id: "1",
status: 100,
event: "Open door",
user: 'Mike',
created_at: '2020-09-05T12:31',
},
{
id: "2",
status: 200,
event: "Call Answer",
user: 'Vik',
created_at: '2020-09-04T18:32',
},
{
id: "3",
status: 300,
event: "Call",
user: 'Max',
created_at: '2020-08-03T18:30',
},
{
id: "4",
status: 400,
event: "Missed call",
user: 'Alex',
created_at: '2020-07-28T10:40',
},
]
const Sample = () => {
const [event, setEvent] = useState('All')
const [user, setUser] = useState('All')
const [dateBefore, setDateBefore] = useState(null)
const [dateAfter, setDateAfter] = useState(null)
let filter = []
if (user !== 'All') {
filter.push(items.filter((u) => u.user === user))
}
if (event !== 'All') {
filter.push(items.filter((e) => e.event === event))
}
if (user amp;amp; event === 'All') {
filter.push(items)
}
if(dateAfter || dateBefore !== null) {
filter.push(items.filter((e) => e.created_at > dateBefore amp;amp; e.created_at < dateAfter))
}
const handleChangeUser = (event) => {
setUser(event.target.value);
};
const handleChangeEvent = (event) => {
setEvent(event.target.value);
}
let eventArr = items.map(e => {
return e.event
})
eventArr.unshift('All')
let userArr = items.map(e => {
return e.user
})
userArr.unshift('All')
return (
<>
<select style={{margin: '0 15px'}} value={event} onChange={handleChangeEvent}>
{ eventArr.map((name) => {
return <option key={name} value={name}>
{name}
</option>
})}
</select>
<select style={{margin: '0 15px'}} value={user} onChange={handleChangeUser}>
{ userArr.map((name) => {
return <option key={name} value={name}>
{name}
</option> })}
</select>
<DatePicker
value={dateBefore}
onChange={setDateBefore}
/>
<DatePicker
value={dateAfter}
onChange={setDateAfter}
/>
<div>
{
filter[0].map(userName => {
return(
<>
<div style={{marginTop: '10px'}}>{ userName.user ' - ' userName.event }</div>
<div style={{borderBottom: '2px solid black'}}>{userName.created_at}</div>
</>)
})
}
</div>
</>
)
}
export default Sample;
Комментарии:
1. подождите, так в чем ваш вопрос
2. @cbracketdash, вопрос в том, как это исправить, чтобы это работало
3. а, хорошо, я посмотрю
Ответ №1:
Проблема
Похоже, что все ваши фильтры фильтруются индивидуально из исходного items
массива и, таким образом, переопределяют любую предыдущую фильтрацию. Т. е. если вы выполняете фильтры в несколько проходов, то каждый последующий фильтр должен фильтровать предыдущий результат фильтра, а не добавлять другой отфильтрованный источник.
Решение
Создайте функцию обратного вызова фильтра, которая просматривает все ваши значения фильтра одновременно. Это можно сделать за один проход по данным с помощью array::filter.
const filter = items.filter(
(item) =>
(user === "All" || item.user === user) amp;amp;
(event === "All" || item.event === event) amp;amp;
(!dateBefore ||
item.created_at > DateTime.fromJSDate(dateBefore).toISO()) amp;amp;
(!dateAfter || item.created_at < DateTime.fromJSDate(dateAfter).toISO())
);
react-date-picker
также работает с объектами даты javascript, поэтому возвращаемый формат не соответствует тому, что находится в ваших данных. Я решил эту проблему путем импорта Luxon
и создал новый объект DateTime из формата даты javascript и преобразовал его в формат ISO.
import React, { useState } from "react";
import DatePicker from "react-date-picker";
import { DateTime } from "luxon";
const items = [
{
id: "1",
status: 100,
event: "Open door",
user: "Mike",
created_at: "2020-09-05T12:31"
},
{
id: "5",
status: 100,
event: "Call Answer",
user: "Mike",
created_at: "2020-09-10T12:31"
},
{
id: "2",
status: 200,
event: "Call Answer",
user: "Vik",
created_at: "2020-09-04T18:32"
},
{
id: "3",
status: 300,
event: "Call",
user: "Max",
created_at: "2020-08-03T18:30"
},
{
id: "4",
status: 400,
event: "Missed call",
user: "Alex",
created_at: "2020-07-28T10:40"
}
];
// Factor out duplicate select JSX code, more DRY
const Select = ({ options, value, onChange }) => (
<select style={{ margin: "0 15px" }} value={value} onChange={onChange}>
{options.map((name) => (
<option key={name} value={name}>
{name}
</option>
))}
</select>
);
const Sample = () => {
const [event, setEvent] = useState("All");
const [user, setUser] = useState("All");
const [dateBefore, setDateBefore] = useState(null);
const [dateAfter, setDateAfter] = useState(null);
const filter = items.filter(
(item) =>
(user === "All" || item.user === user) amp;amp;
(event === "All" || item.event === event) amp;amp;
(!dateBefore ||
item.created_at > DateTime.fromJSDate(dateBefore).toISO()) amp;amp;
(!dateAfter || item.created_at < DateTime.fromJSDate(dateAfter).toISO())
);
const handleChangeUser = (event) => {
setUser(event.target.value);
};
const handleChangeEvent = (event) => {
setEvent(event.target.value);
};
// Mapping the data to options led to duplicate options,
// reducing first into map removes duplicates and allows
// for computing options in single pass. Values inserted
// into map maintain insertion order.
const [eventArr, userArr] = items.reduce(
([events, users], { event, user }) => [
{ ...events, [event]: event },
{ ...users, [user]: user }
],
[
{
All: "All"
},
{
All: "All"
}
]
);
return (
<>
<Select
onChange={handleChangeEvent}
value={event}
options={Object.values(eventArr)}
/>
<Select
onChange={handleChangeUser}
value={user}
options={Object.values(userArr)}
/>
<DatePicker value={dateBefore} onChange={setDateBefore} />
<DatePicker value={dateAfter} onChange={setDateAfter} />
<div>
{filter.map((userName) => {
return (
<>
<div style={{ marginTop: "10px" }}>
{userName.user " - " userName.event}
</div>
<div style={{ borderBottom: "2px solid black" }}>
{userName.created_at}
</div>
</>
);
})}
</div>
</>
);
};