3

I am a relatively new React developer, and I am attempting to show only certain services that have a serviceType of either exterior, interior, combo, or add on. As of now, I have mapped these services into a react component and I am able to display every service that I pull from my database. However, I would like to only display "exterior" services or "interior" services based on the user's selection.

My current code is as follows:

import React, { useEffect, useState } from 'react';
import Service from './Service';
import OffersService from '../../../services/OffersService';
import Button from '../../UI/Button';

const ServiceList = props => {
    const [offers, setOffers] = useState([]);
    const [service, setService] = useState('Exterior');

    const exteriorTypeHandler = () => {
        setService('Exterior');
    };

    const interiorTypeHandler = () => {
        setService('Interior');
    };

    const comboTypeHandler = () => {
        setService('Combo');
    };

    const addonsTypeHandler = () => {
        setService('Add Ons');
    };

    const offersList = offers.map(offer => (
        <Service
            key={offer.id}
            code={offer.serviceCode}
            name={offer.serviceName}
            description={offer.description}
            type={offer.serviceType}
            price={offer.salePrice}
        />
    ));

    useEffect(() => {
        getAllOffers();
    }, []);

    const getAllOffers = () => {
        OffersService.getAllServices()
            .then(response => {
                setOffers(response.data);
            })
            .catch(err => {
                console.log(err);
            });
    };

    return (
        <div>
            <div className='flex justify-center space-x-4'>
                <Button name='Exterior' onClick={exteriorTypeHandler} />
                <Button name='Interior' onClick={interiorTypeHandler} />
                <Button name='Combos' onClick={comboTypeHandler} />
                <Button name='Add Ons' onClick={addonsTypeHandler} />
            </div>

            <ul>{offersList}</ul>
        </div>
    );
};

export default ServiceList;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

In an attempt to get the results I'd like to see, I've tried the following code for my :

<ul>
  {offersList.forEach(offer => {
        if (offer.serviceType === service) {
        return offer;
        }
  })}
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I have also tried: {offersList.serviceType === service && <ul>{offersList}</ul>}

I haven't been able to find anything to help in my Udemy lectures, YouTube, or other Stack Overflow posts, so I appreciate any help that can be given!

2
  • Can you give the response of 'getAllOffers' API call? Commented May 1, 2022 at 3:57
  • Yes of course { "id": 1, "serviceCode": "Base-01", "serviceName": "Exterior Basic Clean", "description": "This is a basic exterior wash to help remove the day to day dirt that occurs during your routine drives. Foam, hand wash, and rinse of the complete exterior of your car. Includes a hand dry and all jams wiped.", "serviceType": "Exterior", "salePrice": "Starting at $45.00" }, Commented May 1, 2022 at 13:38

3 Answers 3

1

<ul>
  {offers.map(offer => {
        if (offer.serviceType === service) {
            return <li> <Service
                         key={offer.id}
                         code={offer.serviceCode}
                         name={offer.serviceName}
                         description={offer.description}
                         type={offer.serviceType}
                         price={offer.salePrice}
    /> </li>;
        }
  })}
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

You need to use map instead of forEach and return offer wrapped in <li></li> tag.

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

5 Comments

Mapping each offer on the offersList returns [object Object] and not the value that is in each index of the array.
Please also wrap offer in curly braces...sorry my bad for not noticing it earlier...Updated my answer
Yeah I'm still getting the issue of [object Object]... I'm going to keep messing around with it though
Updated my answer... Please have a look
This is perfect! Thank you. Creating the Service Component in the list makes so much sense! I really appreciate your help.
1

In my approach I used two state variables for offers and filtered services.

const [offers, setOffers] = useState([]);
const [filteredServices, setfilteredServices] = useState();

const offersList = offers.map((offer) => (
<Service
  key={offer?.id}
  code={offer?.serviceCode}
  name={offer?.serviceName}
  description={offer?.description}
  type={offer?.serviceType}
  price={offer?.salePrice}
/>
));

When user select a particular service, filter the offers according to that and set the filteredServices.

const onClickTypeHandler = (e) => {
const currentService = e.target.name;
if (currentService === "Show All") { // If want to show all the offers set that.
  setfilteredServices(offers);
} else {   // filter the offers that matched for selected service.
  const filtered = offers.filter(
    (service) => service?.serviceName === currentService
  );
  console.log(filtered);
  setfilteredServices(filtered);
}

In display of the offers, show filtered or all the offers as below.

<ul>
      {filteredServices
        ? filteredServices.map((filteredService) => (
            <p>{filteredService.serviceName}</p>
          ))
        : offersList} // in 1st render, filteredServices empty. So display the all offers. then show filtered services.
    </ul>
  };

Check this demo sanbox for complete demonstration. Check this and share your feedback.

Comments

0

First off, I am assuming the response.data is being fetched successfully. I like the approach but a little refactoring can be done. First off, if all the button names are actual serviceTypes from the fetched data, then my solution should work. Ideally, you want to use one onClick function for each button element. In this case, we have something that can delineate from the other Buttons and that is their name.

So all we need to do is grab that name using event from the onClick handler (it looks like this in the code: e.target.name), save the name in our state for asynchronous filtering, and compare offer.serviceType to our currentService while looping through. We use map instead of forEach because we need to actually allocate memory and display them in the DOM.

EDIT: Also, notice how I rendered the fetched data in return. I used a ternary operator to make sure that the request was completed and the data stored in memory before we attempt to access it using map. I forgot to the mention this too - if the serviceTypes from the database are not the matching the names on the Buttons, make sure to fix that for this to work.

import React, { useEffect, useState } from 'react';
import Service from './Service';
import OffersService from '../../../services/OffersService';
import Button from '../../UI/Button';

const ServiceList = props => {
    const [offers, setOffers] = useState([]);
    const [currentService, setCurrentService] = useState('')

    const onClickTypeHandler = (e) =>{
        console(e.target.name)
        setCurrentService(e.target.name)
    }

    useEffect(() => {
        getAllOffers();
    }, []);

    const getAllOffers = () => {
        OffersService.getAllServices()
            .then(response => {
                setOffers(response.data);
            })
            .catch(err => {
                console.log(err);
            });
    };

    return (
        <div>
            <div className='flex justify-center space-x-4'>
                <Button name='Exterior' onClick={onClickTypeHandler} />
                <Button name='Interior' onClick={onClickTypeHandler} />
                <Button name='Combos' onClick={onClickTypeHandler} />
                <Button name='Add Ons' onClick={onClickTypeHandler} />
            </div>

            <ul>{offers ? offers.map(offer => {
                if (offer.serviceType === currentService){
                    return <li>        
                             <Service
                              key={offer.id}
                              code={offer.serviceCode}
                              name={offer.serviceName}
                              description={offer.description}
                              type={offer.serviceType}
                              price={offer.salePrice}
                             />
                          </li>
                }
            }) : ''}</ul>
        </div>
    );
};

export default ServiceList;

8 Comments

Thank you for your edits. I've applied them to my code, but I am still not receiving any data on the screen, so I added some log statements to see what is appearing. When the button is clicked, I am not receiving the value of name, and when the list is trying to iterate over the map, I am receiving Offer is: [object Object]. So, I guess I need to figure out why I'm not getting the value of the offer within the array.
Also for more visibility, my button component's code looks like this: import React from 'react'; import './Button.css'; const Button = props => { return ( <div> <button className='bg-slate-700 hover:bg-blue-700 px-10 py-2 rounded-md' onClick={props.onClick}> {props.name} </button> </div> ); }; export default Button;
If offer is an object, that we should be able to go deeper to get what we need. Please console.log offer.
Console.log(offer) is [object Object]
Most definitely. I figured out that the name value in my Button was located here --> e.target.firstChild.nodeValue so now I am able to only have 1 clickHandler to manager the service state. Again, thanks for your help!
|

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.