ReactJSのチェックボックスを使用してリストにフィルターを適用するにはどうすればよいですか?

vbernal

私はいくつかの製品をレンダリングするリストを持っています、これらの製品はいくつかのカテゴリーに分けられます。一部の製品には、複数のカテゴリがある場合があります。

チェックボックスを使用して、これらのカテゴリにフィルタを適用しようとしています。ユーザーがチェックボックスをオンにすると、選択したカテゴリでリストを更新する必要があります。

ここに画像の説明を入力してください

私はまだ初心者でRedux、リストを更新するためにコンポーネント間で通信する方法がわかりません。カテゴリのリストがDrawerコンポーネントにあり、製品のリストがコンポーネントにあるため、コンポーネント間の通信について説明しましたCard

ファイルがたくさんあるので、コードをcodesandboxに入れました

Here I'm rendering my list of products:

import React, { useState, useMemo, useEffect } from 'react';
import { useSelector } from 'react-redux';

import CardItem from '../CardItem';
import Pagination from '../Pagination';
import Search from '../Search';
import { useStyles } from './styles';

const Card = (props) => {
  const { activeFilter } = props;
  const classes = useStyles();
  const data = useSelector((state) => state.perfume.collections);
  const [searchPerfume, setSearchPerfume] = useState('');
  const [currentPage, setCurrentPage] = useState(1);
  const [perfumesPerPage, setPerfumesPerPage] = useState(3);

  console.log('activeFilter: ', activeFilter);

  const filteredPerfumes = useMemo(() => {
    return data.filter((perfume) =>
      perfume.name.toLowerCase().includes(searchPerfume.toLowerCase())
    );
  }, [data, searchPerfume]);

  const currentPerfumes = filteredPerfumes.slice(
    (currentPage - 1) * perfumesPerPage,
    currentPage * perfumesPerPage
  );
  const pages = Math.ceil(filteredPerfumes.length / perfumesPerPage);

  useEffect(() => {
    if (currentPage > pages) {
      setCurrentPage(1);
    }
  }, [currentPage, pages]);

  const pageNumbers = Array(pages)
    .fill(null)
    .map((val, index) => index + 1);

  const handleClick = (page) => {
    setCurrentPage(page);
  };

  return (
    <div>
      <Search
        data-testid="input-filter-id"
        setSearchPerfume={setSearchPerfume}
      />
      {currentPerfumes
        .filter((perfume) => {
          return (
            perfume.name.toLowerCase().indexOf(searchPerfume.toLowerCase()) >= 0
          );
        })
        .map((item) => (
          <CardItem key={item.id} item={item} />
        ))}
      <Pagination
        pageNumbers={pageNumbers}
        handleClick={handleClick}
        currentPage={currentPage}
      />
    </div>
  );
};

export default Card;

Here I'm rendering my list of categories:

import Divider from '@material-ui/core/Divider';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Checkbox from '@material-ui/core/Checkbox';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';

import { useStyles } from './styles';

const DrawerComponent = (props) => {
  const { activeFilter, setActiveFilter } = props;
  const classes = useStyles();
  const data = useSelector((state) => state.perfume.collections);

  const handleChange = (text) => (event) => {
    setActiveFilter((prev) => ({
      ...prev,
      value: event.target.checked,
      text,
    }));
  };

  const allCategories = data
    .reduce((p, c) => [...p, ...c.categories], [])
    .filter((elem, index, self) => index === self.indexOf(elem));

  return (
    <div className={classes.root}>
      <div className={classes.toolbar} />
      <Divider />
      <List className={classes.list}>
        {allCategories.sort().map((text, index) => (
          <ListItem className={classes.itemList} button key={text}>
            <Checkbox onChange={handleChange(text)} />
            <ListItemText primary={text} />
          </ListItem>
        ))}
      </List>
    </div>
  );
};

export default DrawerComponent;

Do you know how I can apply this filter to my list?

Thank you very much in advance.

Linda Paiste

State Contol

Generally when you need to communicate between components, the solution is Lifting State Up. Meaning that the state should be controlled by a common ancestor.

Instead of having this in your DrawerComponent:

const [activeFilter, setActiveFilter] = React.useState([]);

Move that hook up to your PerfumeStore and pass activeFilter and setActiveFilter as props to DrawerComponent.

You need to add an onChange function to the Checkbox components in DrawerComponent which adds or removes the category by calling setActiveFilter.

So now you need to apply the activeFilter to your list of perfumes, which is determined in Card. You could move all of that filtering up to PerfumeStore, but to keep it simple let's pass activeFilter down as a prop to Card (it just needs to read but not write the filter, so we don't pass down setActiveFilter). Now the Card component has the information that it needs to filter the items based on the selected categories.

Redux Selectors

Everything so far has just had to do with react and the fact that you are using redux hasn't come into play at all. The way that you would incorporate redux principles, if you wanted to, is do define some of the filtering and mapping logic outside of your components as selector functions of state and other arguments. Then instead of calling useSelector to get a huge chunk of state which you process inside the component, you can call useSelector with a selector that gets just the data which you need.

An obvious place to do this is in DrawerComponent, where you are getting the category list. Make a selector function getPerfumeCategories which takes the state and returns the categories. Then in your component, you call const allCategories = useSelector(getPerfumeCategories);

Now all that this component is responsible for is rendering the categories. It is no longer responsible for storing the selections (we've already moved the useState out) or for finding the categories from the state. This is good! You can read up on principles like the Single Responsibility Principle, Separation of Concerns, Logic vs. Presentation components, etc. if you want a deeper understanding of why this is good.

In Card you could use a selector that gets an already-filtered list of perfumes. But in this case a getCurrentPagePerfumes function would take a lot of different arguments so it's kind of messy.

Edit: Filtering

You've asked for help with how to apply the value of activeFilter to filter the perfumes which are shown in your list.

Multiple categories can be selected at once, so activeFilter needs to identify all of the actively selected categories. I first suggested an array of names, but removing items from an array (without mutation) is more complicated than assigning values to objects.

So then I thought about having an object where the keys are the category names and the values are a boolean true/false of whether the category is checked. This makes handleChange really simple because we can update the value for that key to the value of event.target.checked.

  const handleChange = (text) => (event) => {
    setActiveFilter((prev) => ({
      ...prev,
      [text]: event.target.checked,
    }));
  };
  • ...prev says "keep everything the same except the key that I am changing".
  • [text] says "the key I am updating is the variable text, not the literal key 'text'"
  • event.target.checked is the boolean of whether this category is checked.

We could set an initial state for activeFilter which includes a key for every category and all the values are false (ie. nothing selected). Or we could allow for the object to be incomplete with the assumption that if it key isn't included, then it isn't checked. Let's do that.

So now our activeFilter looks something like: {Floral: true, Floriental: false, Fresh: true} where some are true, some are false, and lots are missing and therefore assumed to be false.

We need to figure out how to filter the displayed perfumes based on the value of activeFilter. Let's start by writing a function that determines whether one perfume is eligible to be shown, and then we can use that as a callback of array.filter() on an array of perfumes. We want a perfume to be included if any of its categories are checked (unless you want it to match all the checked categories?). That looks like:

    perfume.categories.some( 
      category => activeFilter[category] === true
    );

Where .some() loops through the categories and returns true if our callback is true for any category.

I added this to your filteredPerfumes memo and added activeFilter as a dependency so that it will re-filter when the checkboxes change.

  const filteredPerfumes = useMemo(() => {
    return data.filter((perfume) =>
      perfume.name.toLowerCase().includes(searchPerfume.toLowerCase())
      && perfume.categories.some( 
        category => activeFilter[category] === true
      )
    );
  }, [data, searchPerfume, activeFilter]);

That works, except that nothing shows when no categories are checked -- whoops! So we want to add a special case that says "all perfumes pass the category filter if no categories are checked." To do that, we need to know if there are checked categories or not. There's a lot of ways to do that, but here's one:

const hasCategoryFilter = Object.values(activeFilter).includes(true);

We look at all of the values in the activeFilter object and see if it includes any which are true.

Now we need to use this value to only filter based on categories when it's true. I'll pull our previous logic into a function and add an if statement (note: the boolean operator || is shorter to use, but I think the if is more readable).

    const matchesCategories = (perfume) => {
      if ( hasCategoryFilter ) {
        return perfume.categories.some( 
          category => activeFilter[category] === true
        );
      } else return true;
    }

補足:2つの独立したフィルターがあります。1つは検索用、もう1つはカテゴリ用です。したがって、1data.filter呼び出して、両方の条件を一度にまたは2回チェックし、各条件を個別にチェックできます。どちらをするかは関係ありません。

最後のフィルターは次のとおりです。

  const filteredPerfumes = useMemo(() => {
    const hasCategoryFilter = Object.values(activeFilter).includes(true);

    const matchesCategories = (perfume) => {
      if ( hasCategoryFilter ) {
        return perfume.categories.some( 
          category => activeFilter[category] === true
        );
      } else return true;
    }

    return data.filter((perfume) =>
      perfume.name.toLowerCase().includes(searchPerfume.toLowerCase())
    ).filter( matchesCategories );
    
  }, [data, searchPerfume, activeFilter]);

サンドボックスを更新

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

jqueryを使用してチェックボックスをクリックして、特定のテキストを含むdivを含むリストから要素をフィルタリングするにはどうすればよいですか?

分類Dev

jqueryまたはjavascriptを使用して2つのチェックボックスがオンになっている場合、divのリストをフィルタリングするにはどうすればよいですか?

分類Dev

パイプを使用して、いくつかのチェックボックスと検索フィールドでデータフィルタリングを整理するにはどうすればよいですか?

分類Dev

JQueryでチェックボックスを使用してフィルタリングする場合、完全に一致するだけでなく、データ属性の完全な文字列内を検索するにはどうすればよいですか?

分類Dev

Vanilla JSを使用して、チェックボックスをオンにし、チェックボックスをオフにしたときにclearInterval()をトリガーしてsetInterval()をトリガーするにはどうすればよいですか?

分類Dev

WIXのチェックボックスを使用してリピーターデータを取得するにはどうすればよいですか?

分類Dev

ng-repeatリストのチェックボックスフィルターをangularjsを使用して作成するにはどうすればよいですか?

分類Dev

チェックボックスを使用して異なるフィールドに切り替えるにはどうすればよいですか?

分類Dev

フラッターでカスタムチェックボックスのリストを作成するにはどうすればよいですか?

分類Dev

reactjsのインラインスタイルにベンダープレフィックスを適用するにはどうすればよいですか?

分類Dev

Bootstrap-Vue:テーブルコンポーネントでチェックボックスを使用してデータをフィルタリングするにはどうすればよいですか?

分類Dev

Selenium Webdriverを使用してチェックボックスが無効になっている場合に、チェックボックスのオン/オフのステータスを取得するにはどうすればよいですか?

分類Dev

LINQを使用してグリッドビューでチェックボックスがオンになっているときに特定のコードを適用するにはどうすればよいですか?

分類Dev

複数のselect2ボックスを使用してテーブルをフィルタリングするにはどうすればよいですか?

分類Dev

チェックボックスの値を使用してデータベースのデータを更新するにはどうすればよいですか?

分類Dev

CSSを使用してチェックボックスをフォームにフォーマットするにはどうすればよいですか?シンプルなルックアンドフィールが欲しい

分類Dev

ブートストラップのチェックボックスとラベルを適切に配置するにはどうすればよいですか?

分類Dev

チェックボックスを使用してデータベースから複数の行を削除するにはどうすればよいですか?

分類Dev

チェックボックスの値を取得してテキストファイルに保存するにはどうすればよいですか?

分類Dev

JAX-RSのフィルターを使用してHTTPリクエストにヘッダーを追加するにはどうすればよいですか?

分類Dev

送信ボタンをクリックしてチェックボックスの値を取得するにはどうすればよいですか?

分類Dev

リストビューアイテムのチェックボックスがチェックボックスの外をクリックして選択解除されないようにするにはどうすればよいですか?

分類Dev

チェックボックスを使用してリストビューアイテムを削除するにはどうすればよいですか?

分類Dev

チェックボックスを適切に使用して、フラッターを使用してボタンを有効または無効にするにはどうすればよいですか?

分類Dev

phpのwhileループと表示用のjavascriptを使用して、データベースからフェッチされた複数の画像から特定のクリックされた画像を表示するにはどうすればよいですか?

分類Dev

ラジオボタンのリストに影響を与えるには(1つがチェックされている場合)、すべてではなく、それぞれに個別に適用するにはどうすればよいですか?

分類Dev

2番目のテーブルにいくつかの条件を適用してmysqlクエリを使用して2つのテーブルからデータをフェッチするにはどうすればよいですか?

分類Dev

チェックボックスの境界線の色をフラッターで変更するにはどうすればよいですか?デフォルトでは黒で表示されていますが、灰色にしたいです

分類Dev

JQueryセレクターを使用して兄弟divのチェックボックスを選択するにはどうすればよいですか?

Related 関連記事

  1. 1

    jqueryを使用してチェックボックスをクリックして、特定のテキストを含むdivを含むリストから要素をフィルタリングするにはどうすればよいですか?

  2. 2

    jqueryまたはjavascriptを使用して2つのチェックボックスがオンになっている場合、divのリストをフィルタリングするにはどうすればよいですか?

  3. 3

    パイプを使用して、いくつかのチェックボックスと検索フィールドでデータフィルタリングを整理するにはどうすればよいですか?

  4. 4

    JQueryでチェックボックスを使用してフィルタリングする場合、完全に一致するだけでなく、データ属性の完全な文字列内を検索するにはどうすればよいですか?

  5. 5

    Vanilla JSを使用して、チェックボックスをオンにし、チェックボックスをオフにしたときにclearInterval()をトリガーしてsetInterval()をトリガーするにはどうすればよいですか?

  6. 6

    WIXのチェックボックスを使用してリピーターデータを取得するにはどうすればよいですか?

  7. 7

    ng-repeatリストのチェックボックスフィルターをangularjsを使用して作成するにはどうすればよいですか?

  8. 8

    チェックボックスを使用して異なるフィールドに切り替えるにはどうすればよいですか?

  9. 9

    フラッターでカスタムチェックボックスのリストを作成するにはどうすればよいですか?

  10. 10

    reactjsのインラインスタイルにベンダープレフィックスを適用するにはどうすればよいですか?

  11. 11

    Bootstrap-Vue:テーブルコンポーネントでチェックボックスを使用してデータをフィルタリングするにはどうすればよいですか?

  12. 12

    Selenium Webdriverを使用してチェックボックスが無効になっている場合に、チェックボックスのオン/オフのステータスを取得するにはどうすればよいですか?

  13. 13

    LINQを使用してグリッドビューでチェックボックスがオンになっているときに特定のコードを適用するにはどうすればよいですか?

  14. 14

    複数のselect2ボックスを使用してテーブルをフィルタリングするにはどうすればよいですか?

  15. 15

    チェックボックスの値を使用してデータベースのデータを更新するにはどうすればよいですか?

  16. 16

    CSSを使用してチェックボックスをフォームにフォーマットするにはどうすればよいですか?シンプルなルックアンドフィールが欲しい

  17. 17

    ブートストラップのチェックボックスとラベルを適切に配置するにはどうすればよいですか?

  18. 18

    チェックボックスを使用してデータベースから複数の行を削除するにはどうすればよいですか?

  19. 19

    チェックボックスの値を取得してテキストファイルに保存するにはどうすればよいですか?

  20. 20

    JAX-RSのフィルターを使用してHTTPリクエストにヘッダーを追加するにはどうすればよいですか?

  21. 21

    送信ボタンをクリックしてチェックボックスの値を取得するにはどうすればよいですか?

  22. 22

    リストビューアイテムのチェックボックスがチェックボックスの外をクリックして選択解除されないようにするにはどうすればよいですか?

  23. 23

    チェックボックスを使用してリストビューアイテムを削除するにはどうすればよいですか?

  24. 24

    チェックボックスを適切に使用して、フラッターを使用してボタンを有効または無効にするにはどうすればよいですか?

  25. 25

    phpのwhileループと表示用のjavascriptを使用して、データベースからフェッチされた複数の画像から特定のクリックされた画像を表示するにはどうすればよいですか?

  26. 26

    ラジオボタンのリストに影響を与えるには(1つがチェックされている場合)、すべてではなく、それぞれに個別に適用するにはどうすればよいですか?

  27. 27

    2番目のテーブルにいくつかの条件を適用してmysqlクエリを使用して2つのテーブルからデータをフェッチするにはどうすればよいですか?

  28. 28

    チェックボックスの境界線の色をフラッターで変更するにはどうすればよいですか?デフォルトでは黒で表示されていますが、灰色にしたいです

  29. 29

    JQueryセレクターを使用して兄弟divのチェックボックスを選択するにはどうすればよいですか?

ホットタグ

アーカイブ