Home > Blockchain >  ReactJS - trying to achieve tag filtering functionality to render only items that have a certain tag
ReactJS - trying to achieve tag filtering functionality to render only items that have a certain tag

Time:05-08

After a few years of lurking without an account, I am today posting my first question.

I've been working on a personal project for a little while, to learn, trying to create a store-like website that has it's inventory of items read from json files.

The functionality that I'm trying to achieve is something of a filter: type a tag in a search field and only the items that have that tag will be displayed.

I've successfully managed to create a functional search filter using useState & Array.filter() with the help of online searches, but this is because I've used it on strings.

For instance, one of my json files may have items looking like this:

[
   {
    "title": "Name of the First Product",
    "description": "Short Description",
    "image": "/assets/img/image_001.png",
    "price": "50.00",
    "currency": "$",
    .
    .
    .
    "tags": [ "tag1", "tag2", "tag3"]
   },
   {
    "title": "Name of the Second Product",
    "description": "Short Description",
    "image": "/assets/img/image_001.png",
    "price": "55.00",
    "currency": "$",
    .
    .
    .
    "tags": [ "tag2", "tag3", "tag4"]
   },
   .
   .
   .
]

The filter functionality is working for strings. If I'm typing First Product in the search field, the correct item is rendered and nothing else is rendered. However, this is not the case for arrays of tags. Nothing is rendered when I'm trying to search for a tag (array of strings).

As seen above, some of the tags may repeat, so I'm merging all the tags into one final string and eliminating the duplicates, before the filter function. For the sake of the example, let's say it looks like the following (note: .toLowerCase() is already applied to each one of the tags in the const below):

const finalArrayOfTags = [ "tag1", "tag2", "tag3", "tag4", ... ];

The way I'm filtering is:

function Function() {

const [query, setQuery] = useState("");

const fileData = require("/src/files/inventory/list.json");

() => {
   JSON.parse(fileData);
}

const tagArray = fileData.map(i => i.tags);
const tagFlatten = tagArray.flat();
const tagToLower = tagFlatten.map(j => j.toLowerCase());
const finalArrayOfTags = [...new Set(tagToLower)];

return (

<Row>
   <Form>
      <Form.Row>
         <Form.Control type="text" placeholder="Search" onChange={event => setQuery(event.target.value)}/>
      </Form.Row>
   </Form>
</Row>
<Row>
   {fileData.filter(items => {
      if (query === "") {
         return item;
      } else if (item.title.toLowerCase().includes(query.toLowerCase())) {
         return item;
      } else if (item.description.toLowerCase().includes(query.toLowerCase())) {
      .
      .
      .
      } else if (finalArrayOfTags.forEach(tag => tag.includes(query))) {
         return item;
      }}).map(item, index => (
          <>
             /// ... render the item component
          </>
      ))}
</Row>

);
}

export default Function;

The first 2 "else if"s are rendering the correct items, because they are looking inside a string. The "else if" at the end doesn't render anything, because it's looking inside an array of strings and I can't figure out how to split it out or iterate through them.

Does anyone have any advice for me ? :)

CodePudding user response:

Array.forEach does not return anything hence the check always fails. What you want to use instead is either .every or .some

Both methods behave the same way like like other array methods, the main difference is,

In the case of .every it will only return true if all the elements of that array satisfy the tags.include

Where as in the case of .some it will return true as long as any one item satisfied the tags.include

So your final code might look like

else if (finalArrayOfTags.some(tag => tag.includes(query))) {
      return item;
   }}).map(item, index => (
       <>
          /// ... render the item component
       </>
   ))}

CodePudding user response:

Check the following code

import React, {useState} from "react";

export default function App() {

  const [searchKey, setSearchKey] = useState('');

  // SAMPLE DATA
  const [data, setData] = useState([{
    name: 'Product 1',
    tags: ['tag1', 'tag2', 'tag3']
  },{
    name: 'Product 2',
    tags: ['tag2', 'tag3']
  },{
    name: 'Product 4',
    tags: ['tag4', 'tag5',]
  }])

  return (
    <div>
      
      {/* SEARCH BAR */}
      <input placeholder="Search..." value={searchKey} onChange={e => setSearchKey(e.target.value)}/>

      <ul>
        {
          data.filter(e => {
            // IF NO SEARCH KEYWORD FOUND - Returns all data
            if(!searchKey.trim()){
              return true;
            }
            // SEARCH ON TAGS AND PRODUCT NAME
            return e.tags.indexOf(searchKey) !== -1?true:false || new RegExp(searchKey, 'gi').test(e.name);
          }).map((e,k) => (
            <li key={k}>{e.name}</li>
          ))
        }
      </ul>

    </div>
  );
}

CodePudding user response:

Use like this finalArrayOfTags.find( (tag, index ) => tag.includes(query) ) in the else if statement , I mean use it like

const finalArrayOfTags = ['tag1', 'tag2', 'tag3'];
let result;
if (result = finalArrayOfTags.find( (tag, index ) => tag.includes('tag1') )){
        item = result;
        return item; 
}
  • Related