Как использовать несколько выбранных опций React

#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>
    </>
  );
};