通过复选框过滤对象数组

Filter array of objects through checkboxes

这是一个 React.js 过滤器问题,我被困在这里。该问题将电影的静态数据作为对象数组,我们必须根据不同复选框的选中状态过滤该数据。

静态数据:

export const data = [
    {
        id: 1,
        title: 'Batman',
        rating: 1,
        genre: 'Action'
    },
    {
        id: 2,
        title: 'Superman',
        rating: 5,
        genre: 'Comedy'
    },
    {
        id: 3,
        title: 'Spiderman',
        rating: 3,
        genre: 'Thriller'
    },
    {
        id: 4,
        title: 'BananaMan',
        rating: 2,
        genre: 'Action'
    },
    {
        id: 5,
        title: 'OrangeMan',
        rating: 4,
        genre: 'Drama'
    }
];

摘要 UI 有以下图片代表两种类型的过滤器。

截图:

两个过滤器在 UI 上呈现为复选框。所以,我创建了一个名为 Checkboxes 的组件,它将采用一个名为 list 的道具,它是一个对象数组。

list 代表独特的 ids 复选框,这将帮助我们跟踪复选框的 checkedunchecked 状态。不同的复选框 id 将保存在数组状态中。

const [checkedArray, setCheckedArray] = useState([]);

最后,我在名为 App.js 的父组件中两次渲染此 Checkboxes 组件。一个用于评级,另一个用于类型。

列表:

export const listCheckboxesRating = [
    {
        id: 0,
        name: 'rating',
        label: 'Any Rating'
    },
    {
        id: 1,
        name: 'rating1',
        label: 'Rating 1'
    },
    {
        id: 2,
        name: 'rating2',
        label: 'Rating 2'
    },
    {
        id: 3,
        name: 'rating3',
        label: 'Rating 3'
    },
    {
        id: 4,
        name: 'rating4',
        label: 'Rating 4'
    },
    {
        id: 5,
        name: 'rating5',
        label: 'Rating 5'
    }
];

export const listCheckboxesGenre = [
    {
        id: 0,
        name: 'genre',
        label: 'Any Genre'
    },
    {
        id: 1,
        name: 'action',
        label: 'Action'
    },
    {
        id: 2,
        name: 'comedy',
        label: 'Comedy'
    },
    {
        id: 3,
        name: 'drama',
        label: 'Drama'
    },
    {
        id: 4,
        name: 'thriller',
        label: 'Thriller'
    }
];

复选框组件:

import {useState} from 'react';
import {handleToggle} from '../../utils';

const Checkboxes = ({list, handleFilters}) => {
    const [checkedArray, setCheckedArray] = useState([]);

    const onChangeHandler = (checkboxId) => {
        const newState = handleToggle(checkboxId, checkedArray);
        setCheckedArray(newState);
        // Update this checked information into Parent Component
        handleFilters(newState);
    };

    return list.map((item, index) => {
        return (
            <div key={index}>
                <input
                    type="checkbox"
                    id={item.name}
                    checked={checkedArray.indexOf(item.id) !== -1}
                    onChange={() => onChangeHandler(item.id)}
                />
                <label htmlFor={item.name}>{item.label}</label>
            </div>
        );
    });
};

export default Checkboxes;

我通过名为 handleFilters 的函数将这些复选框的全部状态传递给父组件 App.js。可以看到这行代码。

handleFilters(newState);

App.js 组件正在使用如下内容保存这些复选框的状态:

 const [selected, setSelected] = useState({
        rating: [],
        genre: []
    });

App.js分量:

import {useState} from 'react';
import Checkboxes from './Checkboxes';
import {data, listCheckboxesRating, listCheckboxesGenre} from '../data';

const App = () => {
    const [movies, setMovies] = useState(data);
    const [selected, setSelected] = useState({
        rating: [],
        genre: []
    });

    /**
     * This function will perform the filtration process based on the key value.
     *
     * @param {number[]} checkboxState - It will take the final state of checkboxes
     * @param {string} key - It will help us to determine the type of filtration
     */
    const handleFilters = (checkboxState, key) => {
        const newFilters = {...selected};
        newFilters[key] = checkboxState;
        // Filtration process
        for (let key in newFilters) {
            if (
                newFilters.hasOwnProperty(key) &&
                Array.isArray(newFilters[key]) &&
                newFilters[key].length > 0
            ) {
                if (key === 'rating') {
                } else if (key === 'genre') {
                }
            }
        }

        // Save the filtered movies and update the state.
        //  setMovies();
        setSelected(newFilters);
    };

    return (
        <div>
            {movies.map((movie) => (
                <div key={movie.id}>
                    <div>Name: {movie.title}</div>
                    <div>Genre :{movie.genre}</div>
                    <div>Rating: {movie.rating}</div>
                    <hr />
                </div>
            ))}

            <div className="row">
                <div className="col">
                    <h1>Filter by Rating</h1>
                    <Checkboxes
                        list={listCheckboxesRating}
                        handleFilters={(checkboxState) =>
                            handleFilters(checkboxState, 'rating')
                        }
                    />
                </div>

                <div className="col">
                    <h1>Filter by Genre</h1>
                    <Checkboxes
                        list={listCheckboxesGenre}
                        handleFilters={(checkboxState) =>
                            handleFilters(checkboxState, 'genre')
                        }
                    />
                </div>
            </div>
        </div>
    );
};

export default App;

两个过滤器的复选框 checkedunchecked 状态都工作正常,并且在父 selected 状态中使用 ids 正确更新,如下所示。

 const [selected, setSelected] = useState({
            rating: [1,2,3],
            genre: [2,4]
        });

但这是我卡住的令人困惑的部分。如何映射这些复选框 ids 并根据这些 ids 过滤数据?

我在 App.js 组件 handleFilters(checkboxState, key) 中定义了一个函数,我想在其中迭代 selected 状态以执行过滤过程,但我的想法不是逻辑和我需要帮助。

工作演示:

repository link.

所以我不得不对您的代码进行一些更改以解决正确的过滤问题:

  1. listCheckboxesRatinglistCheckboxesGenre 上添加了一个 value 键以用于过滤。
export const listCheckboxesGenre = [
  {
    id: 0,
    name: 'genre',
    label: 'Any Genre',
    value: ''
  },
  ...
}
  1. 更新了 onChangeHandler 以将 value 传回 handleFilters 而不是 id
const onChangeHandler = (checkboxId) => {
    // ...
    handleFilters(newState.map((id) => list[id].value));
};
  1. 创建了几个过滤电影的过滤函数。
const handleFilters = (checkboxState, key) => {
  const newFilters = { ...selected };
  newFilters[key] = checkboxState;

  const hasFilters =
    newFilters.rating.length > 0 || newFilters.genre.length > 0;
  const filterRating = (module) =>
    newFilters.rating.includes(0) ||
    newFilters.rating.includes(module.rating);
  const filterGenre = (module) =>
    newFilters.genre.includes('') || newFilters.genre.includes(module.genre);

  const filteredMovies = data.filter(
    (m) => !hasFilters || filterRating(m) || filterGenre(m)
  );
  setMovies(filteredMovies);
  setSelected(newFilters);
};

工作演示:

我做了一些重构。

  1. 使数据更通用。
    export const listCheckboxesRating = {
      key: 'rating',
      data: [
        {
          id: 1,
          name: 'rating1',
          label: 'Rating 1',
          value: 1
        },
        ...
    }
  1. 重构 Checkbox 组件,只传递数据。
    <Checkboxes
      list={listCheckboxesGenre}
      handleFilters={handleFilters}
    />
  1. 使过滤过程与值和键一起工作。
    // Filtration process
    let filteredMovies = data.filter((movie) => {
      for (let key in newFilters) {
        if (
          newFilters.hasOwnProperty(key) &&
          Array.isArray(newFilters[key]) &&
          newFilters[key].length > 0
        ) {
          if (
            newFilters[key].findIndex((value) => movie[key] === value) === -1
          ) {
            return false;
          }
        }
      }
      return true;
    });

工作演示: https://codesandbox.io/s/react-filter-forked-gs2b2?file=/src/components/App.js