2

I'm learning React and I'm trying to create a dynamic Dropdown select. I'm importing the data from fetched API successfully from another component. I want to populate select option by category from the fetched data, which is an array of a single string either 'women', 'men' or 'kids' . What I want, is to have initially all data listed, to have 3 options for category strings and list only matching products according to the onChange event. Now when I click to the select the whole app renders and can't see the bug.
Any help will be appreciated

Here is how API data looks like:

[
  {
    categories: [
      'women'
    ],
    variants: [
      'white',
      'black',
      'green'
    ],
    sizes: [
      38,
      39,
      40
    ],
    _id: '5f8edf08880a821cb8757d8a',
    name: 'Nike Air',
    description: 'Tennis court levitating sneaker',
    img: 'https://source.unsplash.com/user/erondu/PGTO_A0eLt4',
    price: 100,
    __v: 1
  }
]

Category component

import React, { useState } from 'react'

const Categories = ({ categories }: any) => {
    const [category, setCategory] = useState('')

    console.log('category', category)
    return (
      <>
        <select 
          value={category}
          onChange={(e) => setCategory(e.target.value)}
         >
          <option value="">Select</option>
          {categories && categories.map((option: any) => (
           <option key={option._id} 
            value={option.categories}
           >
          {option.categories}
          </option>
        ))}
       </select>
      </>
    )
}

export default Categories
8
  • Please don't use console.log output. Post something that is actually copyable/usable. Commented Nov 20, 2020 at 7:06
  • @ Yoshi thanks for pointing out. I updated the question Commented Nov 20, 2020 at 7:25
  • Thanks for your update, but how is an image any more copyable/usable than the console.log output from before? If you want people to help you, make it easy for them to help you. No one wants to type that example data just to get a prototype running. Commented Nov 20, 2020 at 7:27
  • Hey @ Yoshi I'm trying to be precise, but yeah, you right. So, can you help me with that, please? What is missing here to be more precise and make easier for community to help? Commented Nov 20, 2020 at 7:34
  • A good read in general is How to create a Minimal, Reproducible Example. Then for your question here, post the data shown in that image as JSON or a valid javascript data structure. This way people can copy. Commented Nov 20, 2020 at 7:37

1 Answer 1

2

Try something like that:

// Categories Component
const Categories = ({ data }) => {
  // You need an initial value if you like to show something when page first load
  // assuming you know exactly how your data will look like
  const [selected, setSelected] = React.useState(data[0].categories[0]);
  const [selectedCategory, setSelectedCategory] = React.useState(data);
 
  React.useEffect(() => {
    // Filter out categories by selected option
    const category = data.filter((item) => item.categories[0] === selected);

    setSelectedCategory(category);
    // Will change when data changes or another item selected
  }, [data, selected]);
  
  // Set selected option
  const handleSelcet = (e) => {
    setSelected(e.target.value);
  };
  
  return (
    <React.Fragment>
      <select onChange={handleSelcet}>
        {data.map((item) =>
          item.categories.map((category) => (
            <option key={category} value={category}>
              {category}
            </option>
          ))
        )}
      </select>
      {selectedCategory.map((category) => {
        return (
          <div key={category._id}>
            <p>Name: {category.name}</p>
            <p>Description: {category.description}</p>
            <p>Price: {category.price}</p>
            variants:
            <ul>
              {category.variants.map((item) => (
                <li key={item}>{item}</li>
              ))}
            </ul>
            <img src={category.img} alt="" />
          </div>
        );
      })}
    </React.Fragment>
  )
}

// Data from API
const data = [
  {
    categories: ["women"],
    variants: ["white", "black", "green"],
    sizes: [38, 39, 40],
    _id: "5f8edf08880a821cb8757d8a",
    name: "Nike Air",
    description: "Tennis court levitating sneaker",
    img: "https://source.unsplash.com/user/erondu/PGTO_A0eLt4",
    price: 100
  },
  {
    categories: ["man"],
    variants: ["black", "green", "yellow"],
    sizes: [37, 39, 40, 42],
    _id: "5f8edf08880a821cb8757d9b",
    name: "Another Nike Air",
    description: "Another description",
    img: "https://source.unsplash.com/user/erondu/1600x900",
    price: 120
  }
];

const App = () => {
  return (
    <div className="App">
      <Categories data={data} />
    </div>
  )
}

ReactDOM.render( <App /> , document.getElementById('root'))
img {
  width: 100%;
  max-width: 400px;
  height: auto;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

Here is an example with TypeScript

Edit laughing-hawking-q9ut6

Sign up to request clarification or add additional context in comments.

5 Comments

thanks for answer and you effort. I tried to run the code you provided in codesandbox. But have an error in first useState . it says 'TypeError: Cannot read property 'categories' of undefined ' . I think if we solve this, app will run. Thanks in advance
Did you use another sandbox or the one that linked above? Based on your code i assumed you're using TypeScript which already defines the data array. Check this sandbox without TypeScript, I just hardcoded the initial state.
looks great. I checked the link with typescript. I could solve the error putting initial value in quotes. But my problem is, that the 'data' props is the main data that I am already listing in 'ui', and want to output only the data from 'ui' based on selection. So we don't need to output anything, we have to show already created and styled data based on the category that will be selected.
Sorry I didn't get that quite right. Do you mean you don't need any data to be shown initially and show it just on select? if so, just remove the selected state and move the useEffet logic to handleSelcet function. check this sandbox
sorry for being not precise. I've already listed data in 'ui' and initially I want to keep all data listed in 'ui', and, based on option selection, show only the products which match the category.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.