How to map data according to button id

Hi,

I’m developing a React Application and I’m stuck since hours on a special point.

I’m displaying elements on my main page as you could see bellow.

When we click on the button “détails”, I did a route another page to display more information according on what item it had been clicked.

I put an id on my button which is also my db index that’s way I thought I could filter on my other page what data I want to display comparing data index and button id, but it don’t work…
I really don’t know how to do that.

This is my Shops component displaying “detail” button:

class Shops extends Component {
  state = {};

  render() {
    return (
      <Container>
        <ListGroup>
          {this.props.shops.map((detail, index) => (
            <ListGroup.Item>
              <Row>
                <Col>
                  <Image
                    alt=""
                    src={detail.imgURL}
                    width={150}
                    height={150}
                    rounded
                  />
                </Col>
                <Col>
                  <h3 class="shop_title">{detail.nom}</h3>
                  <StarRatings
                    rating={this.props.rating}
                    starRatedColor="#DAA520"
                    changeRating={this.changeRating}
                    numberOfStars={5}
                    starDimension="15px"
                    name="rating"
                    starSpacing="2px"
                  />
                  <p id="resume">{detail.resume}</p>
                </Col>
                <Col>
                  <Row>
                    {detail.startPrice === ""
                      ? "Sur devis"
                      : "A partir de " + detail.startPrice + " €"}
                  </Row>
                  <Row>
                    <Link to="/shopDetail">
                      <Button
                        className="detailButton"
                        id={index}
                        variant="primary"
                        onClick={this.props.filterClick}
                      >
                        Détails
                      </Button>
                    </Link>
                  </Row>
                </Col>
              </Row>
            </ListGroup.Item>
          ))}
        </ListGroup>
      </Container>
    );
  }
}

export default Shops;

And this is my component displaying detail:


import React, { Component } from "react";

class ShopDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {

    return (
      <div className="container">
        <div className="row">
          <div className="col-4">Logo</div>
          <div className="col-8">
              <h2>Titre</h2>
              <p>Notation</p>
          </div>
        </div>
        <div className="row">
          <div className="col">
              <h3>Titre2</h3>
              <p>Website</p>
          </div>
          <div className="col">
              <h2>Services</h2>
              <p>Rappel des services</p>
          </div>
        </div>
        <div className="row">
        <div className="col">
              <h2>Présentation</h2>
              <p>Résume</p>
          </div>
        </div>
      </div>
    );
  }
}

export default ShopDetails;

And so I have access to the object “shops” which contain all my shop data and their index are equal to each own button index to.

If you could help me on that. It would be amazing.

Thanks a lot for your help and precious time

1 Like

So you need to pass the id and data index to you details page, is that right?

I’m sure you know how to pass data to component as props.

Next step is getting the data onClick. That can be done using target value? I believe, I can’t remember off top of my head.

Please, show us the filterClick function, so we can help you and see how can we face the problem the way you are doing it :slight_smile:

Btw, Just to let you know, I would have another approach, I would use

<Link to={`/details/${id}`}>

Example:
id = josepho

And when the other page charges, it would get the id from the link, using window.location.pathname and then, it would load the necesarry information of the product. If all the information is passed through props, all you have to do is filter what you need using the id you got from the link.

1 Like

Here is what I had used in the past. Sorry got distracted while I was trying to find it in my Github:

//dynamically generated HTML
<div key={val.name} onClick={this.handleClick} val={val.name} data={type}>
div stuff
</div>

//event handler
handleClick(e) {
		let myVar=e.currentTarget.getAttribute('val');
		let myOtherVar=e.currentTarget.getAttribute('data');
}
2 Likes

Hi, thanks to helping me with that.

Here is my filterClick Function to let you see.

filterClick = e => {
    e.preventDefault();

    this.setState({
      ...this.state,
      isNotClicked: false,

      //If its Search Click is cliked
      isClicked: true,
      filteredResultsLength: this.state.filteredResults.length
    });
    function multiFilter(array, filters) {
      const filterKeys = Object.keys(filters);
      // filters all elements passing the criteria
      return array.filter(item => {
        // dynamically validate all filter criteria
        return filterKeys.every(key => {
          // ignores an empty filter
          if (!filters[key].length) return true;
          return filters[key].includes(item[key]);
        });
      });
    }

    let shops = this.state.shops;

    if (
      this.state.bornePhoto === true &&
      this.state.cabinePhoto === true &&
      this.state.helioBooth === true
    ) {
      let filters = {
        cabinePhoto: ["OUI"],
        bornePhoto: ["OUI"],
        heliobooth: ["OUI"]
      };
      var filtered = multiFilter(shops, filters);

      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (
      this.state.helioBooth === true &&
      this.state.cabinePhoto === true
    ) {
      let filters = {
        heliobooth: ["OUI"],
        cabinePhoto: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });
      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (
      this.state.bornePhoto === true &&
      this.state.cabinePhoto === true
    ) {
      let filters = {
        cabinePhoto: ["OUI"],
        bornePhoto: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (
      this.state.helioBooth === true &&
      this.state.bornePhoto === true
    ) {
      let filters = {
        heliobooth: ["OUI"],
        bornePhoto: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (this.state.bornePhoto === true) {
      let filters = {
        bornePhoto: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (this.state.cabinePhoto === true) {
      let filters = {
        cabinePhoto: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else if (this.state.helioBooth === true) {
      let filters = {
        heliobooth: ["OUI"]
      };

      filtered = multiFilter(shops, filters);
      this.setState({
        filteredResults: filtered
      });

      //2nd filter to filter b2b/b2C
      if (this.state.selectedOption === "pros") {
        filters = {
          pros: ["OUI"]
        };

        filtered = multiFilter(filtered, filters);

        this.setState({
          filteredResults: filtered
        });
      }
    } else {
      console.log("aucun résultat");
    }

    // *the value of each key is an array with the values to filter
    // *filter the shops array by choosen parameters
  };

And here its whats my database object look like.

I think your approach could be good too. More over I’m already using react-router in my app.

Then. My filterClick function which is attached to my button have to be change for a function that just render the right shop in the database according to what button is clicked.
I hope its more clear this way. Let me know if you need anything else to understand my point.

Thanks a lot for your time and answers

Hey,

I used the way you tell me.

class Shops extends Component {
  state = {};

  render() {
 
    return (
      <Container>
        <ListGroup>
          {this.props.shops.map((detail, index) => (
            <ListGroup.Item key="index">
              <Row>
                <Col>
                  <Image
                    alt=""
                    src={detail.imgURL}
                    width={150}
                    height={150}
                    rounded
                  />
                </Col>
                <Col>
                  <h3 class="shop_title">{detail.nom}</h3>
                  <StarRatings
                    rating={this.props.rating}
                    starRatedColor="#DAA520"
                    changeRating={this.changeRating}
                    numberOfStars={5}
                    starDimension="15px"
                    name="rating"
                    starSpacing="2px"
                  />
                  <p id="resume">{detail.resume}</p>
                </Col>
                <Col>
                  <Row>
                    {detail.startPrice === ""
                      ? "Sur devis"
                      : "A partir de " + detail.startPrice + " €"}
                  </Row>
                  <Row>

 { /* Make route with id, with key= detail.id */}
                      <Link to={"/shopDetail/" + detail.id}>
                      <Button
                        className="detailButton"
                        key={detail.id}
                        variant="primary"
                        onClick={this.props.filterClick}
                      >
                        Détails
                      </Button>
                    </Link>
                    <Route path={`/shopDetail/:id` }  render={() => (
              <ShopDetails
                shops = {this.props.state.shops}
                handleChanges={this.props.handleChanges}
                isClicked={this.props.isClicked}
                filterClick={this.props.filterClick}
                moreFilterClick={this.props.moreFilterClick}
                rating={this.props.rating}
                filteredResults={this.props.filteredResults}
                startDate={this.props.startDate} // momentPropTypes.momentObj or null,
                startDateId="your_unique_start_date_id" // PropTypes.string.isRequired,
                endDate={this.props.endDate} // momentPropTypes.momentObj or null,
                endDateId="your_unique_end_date_id" // PropTypes.string.isRequired,
                onDatesChange={({ startDate, endDate }) =>
                  this.setState({ startDate, endDate })
                } // PropTypes.func.isRequired,
                focusedInput={this.props.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null,
                onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired
                SelectedShop = {this.props.SelectedShop}
              />
            )} />

                  </Row>
                </Col>
              </Row>
            </ListGroup.Item>
          ))}
        </ListGroup>
      </Container>
    );
  }
}

export default Shops;

So here is my shops component, displaying by button route.

And here is my ShopDetail component which is supposed to display the matching data.

class ShopDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {  }
  }
  
  render() { 
 
    console.log(window.location.pathname);

    return ( 
      <div>
        <h1>
        {this.props.shops.map((detail, index) => (
          <h1 key={index}>
            {detail.nom}
          </h1>
        ))}
        </h1>
      </div>
     );
  }
}
 
export default ShopDetails;

I’m just really not sure now how I can just render the right content according of the id page.

My path are good, I can click on each button and each one open the right page according to the id path. But for the shopDetail content, I’m not sure to understand how I can specify and link the good shop thanks to “window.location.pathname” for exemple.

Thanks a lot for your time and answers

Why is there a Route inside your List?

Put the Route in your BrowserRoute

Inside of ShopDetails in ComponentDidMount() you can do:

ComponentDidMount(){
// 1
    const id = window.location.pathname.replace("/shopDetail/", "")
// 2   GetInformationFromDatabase().then...
// 3   setState({
//        All the Data of you Single Item...
//        Name..., id, idk
//     })
}

render(){
    <h1>{this.state.data.name}</h1>
}

Let me know if that would work for you.
If it doesn’t, or it is not the idea, please try to do some images explaining how you want your webpage to look. Because I’m not sure if I’m understanding at all what you want to do.

You are welcome :slight_smile:

Thanks a lot for this answer.

I will share you the way I organize my react-router, because by your question I’m guessing I’m doing something on the wrong way at these level.

Index.js

import React from "react";

import ReactDOM from "react-dom";

import "./index.css";

import App from "./App";

import * as serviceWorker from "./serviceWorker";

import "bootstrap/dist/css/bootstrap.css";

import { BrowserRouter, Route } from "react-router-dom";

ReactDOM.render(

<BrowserRouter>

<Route exactly pattern="/" component={App}/>

</BrowserRouter>,

document.getElementById("root")

);

// If you want your app to work offline and load faster, you can change

// unregister() to register() below. Note this comes with some pitfalls.

// Learn more about service workers: https://bit.ly/CRA-PWA

serviceWorker.unregister();

App.js

(…)



  render() {
    return (
      <Router>
        <div>
          <HeaderFilters
            wrapperHeaderFunction = {this.wrapperHeaderFunction}
            zip_code = {this.state.zip_code}
            handleChanges={this.handleChanges}
            isClicked={this.isClicked}
            filterClick={this.filterClick}
            selectedOption={this.state.selectedOption}
            moreFilterClick={this.moreFilterClick}
            filteredResults={this.state.filteredResults}
            rating={this.state.rating}
            startDate={this.state.startDate} // momentPropTypes.momentObj or null,
            startDateId="your_unique_start_date_id" // PropTypes.string.isRequired,
            endDate={this.state.endDate} // momentPropTypes.momentObj or null,
            endDateId="your_unique_end_date_id" // PropTypes.string.isRequired,
            onDatesChange={({ startDate, endDate }) =>
              this.setState({ startDate, endDate })
            } // PropTypes.func.isRequired,
            focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null,
            onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired,
          />

          {this.state.isMoreFiltersRequired ? (
            <MoreFilters
              handleChanges={this.handleChanges}
              isClicked={this.isClicked}
              filterClick={this.filterClick}
              moreFilterClick={this.moreFilterClick}
              filteredResults={this.state.filteredResults}
              rating={this.state.rating}
            />
          ) : null}
          <div>
            {this.state.login ? <Spinner animation="border" size="xl" /> : null}
          </div>

          <Route
            exact
            path="/"
            render={() => (
              <ShopPreview
                loading={this.state.loading}
                shops={this.state.shops}
                filteredResults={this.state.filteredResults}
                rating={this.state.rating}
              />
            )}
          />

          <Route
            path="/search"
            render={() => (
              <ShopSearch
                loading={this.state.loading}
                shops={this.state.shops}
                filteredResults={this.state.filteredResults}
                rating={this.state.rating}
              />
            )}
          />

          <Route
            path="/shopDetail/:id"
            render={() => (
              <ShopDetails
                shops = {this.state.shops}
                handleChanges={this.handleChanges}
                isClicked={this.isClicked}
                filterClick={this.filterClick}
                moreFilterClick={this.moreFilterClick}
                rating={this.state.rating}
                filteredResults={this.state.filteredResults}
                startDate={this.state.startDate} // momentPropTypes.momentObj or null,
                startDateId="your_unique_start_date_id" // PropTypes.string.isRequired,
                endDate={this.state.endDate} // momentPropTypes.momentObj or null,
                endDateId="your_unique_end_date_id" // PropTypes.string.isRequired,
                onDatesChange={({ startDate, endDate }) =>
                  this.setState({ startDate, endDate })
                } // PropTypes.func.isRequired,
                focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null,
                onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired
                SelectedShop = {this.state.SelectedShop}
              />
            )}
          />
        </div>
      </Router>
    );
  }
}

export default App;

May be I don’t have my BrowserRouter on the right place ?

Something weird for you in my code ?

I don’t do it that way, (neither i knew you could use a route inside a route :smile:) but if it works then it’s okay, don’t mind on it.

Let me know if my answer worked for you.

Btw, what you wanted to do was to show the details under your list item? Like if you click details and then something else is opened below it?

Something like this? (Info button) https://rojolelo.github.io/directorio-de-negocios/

Yeap, that work. Ahah ok, so I let it like this.

And yes it’s exactly what I want to do, just in my case my component are displayed in external path. And not on the same page as in the link example.
But yes ! It’s exactly what I’m looking to do, when a user clicks on detail, these show the shop detail from the database according to what shop detail have been clicked.

Try to do this, you already have your information when you map, right?

<Container>
        <ListGroup>
          {this.props.shops.map((detail, index) => (
            
                    ...
                    ...

                      <Link to={"/shopDetail/" + detail.id}>
                      <Button >
                        Détails
                      </Button>
                    </Link>

{  detail.id ===   window.location.pathname.replace("/shopDetail/", "") ?                               
 <ShopDetails/> : null}

                    ...
                    ...

          ))}
        </ListGroup>
      </Container>

Yes, this work:

<Container>
        <ListGroup>
          {this.props.shops.map((detail, index) => (
            <ListGroup.Item key="index">
              <Row>
                <Col>
                  <Image
                    alt=""
                    src={detail.imgURL}
                    width={150}
                    height={150}
                    rounded
                  />
                </Col>
                <Col>
                  <h3 class="shop_title">{detail.nom}</h3>
                  <StarRatings
                    rating={this.props.rating}
                    starRatedColor="#DAA520"
                    changeRating={this.changeRating}
                    numberOfStars={5}
                    starDimension="15px"
                    name="rating"
                    starSpacing="2px"
                  />
                  <p id="resume">{detail.resume}</p>
                </Col>
                <Col>
                  <Row>
                    {detail.startPrice === ""
                      ? "Sur devis"
                      : "A partir de " + detail.startPrice + " €"}
                  </Row>
                  <Row>

 { /* Make route with id, with key= detail.id */}
                      <Link to={"/shopDetail/" + detail.id}>
                      <Button
                        className="detailButton"
                        key={detail.id}
                        variant="primary"
                        onClick={this.props.filterClick}
                      >
                        Détails
                      </Button>
                    </Link>
                    
{  detail.id ===   window.location.pathname.replace("/shopDetail/", "") ?                               
<ShopDetails/> : null}
                  

                  </Row>

I have my different shops data accessible from shopDetail, and my path is right change according with my button id.
But it still render all my data, not just those from the shop ID.

class ShopDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    console.log(window.location.pathname);

    return (
      <div>
        <h1>
          {this.props.shops.map((detail, index) => (
            <h1 key={index}>{detail.nom}</h1>
          ))}
        </h1>
      </div>
    );
  }
}

export default ShopDetails;

This is a screen shot of my shopDetail component when I clicked on the button “detail” of my shop with ID = 0.

class ShopDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

ComponentDidMount(){
    const id= window.location.pathname.replace("/shopDetail/", “”);
    const shops = this.props.shops
    let shop = {};

    for (let i = 0; i < shops.length; i++){
        if (shops[i].id === id) {
            shop["id"] = shops[i].id
            shop["rating"] = shops[i].rating
            shop["idk"] = shops[i].idk
            shop["anything"] = shops[i].anything
            break;
        }
    }

    this.setState({
         shop
    })
}

  render() {

//Notice that you are mapping in your code, and that goes trough all your data.
    return (
      <div>
        <h1>
         {this.state.shop.name}
        </h1>
      </div>
    );
  }
}

Important: in your <ShopDetail key={}/> component add some value to the key={}, the index could be

1 Like

Ok, now I understand better. Its really more clear.
One last thing, when I try to implement your code, I have finally an empty object in my state of my component ShopDetails. So my {this.state.shop.name} have nothing to render because my state is empty.

See bellow underline in red:

All the rest seems to be understood :slight_smile:

use console.log() on the information you need to track, to see why it’s not reaching into the state, maybe the filter is not working properly, or maybe ComponentDidMount is not the right function to use and you have to try another approach to solve that; don’t forget to initialize shop={} in your state.

But the most important, dont forget to use console.log to track the information.

1 Like

Hey,

Thanks a lot for your answer.
So I track all my data with some console.log and it’s really weird. I have access to my state in my react developer tools, and it’s stocked my shops like state.
Moreover I can see my state in my location object. So it should be ok.
But when I try to

const location = this.props.location.shops
      
      console.log("Location:", location)

My Location return undefined…
I’m really stuck … I don’t understand why I can’t access to my data.

shopDetails.js

import React, { Component } from 'react'

class ShopDetails extends Component {
    constructor(props){
      super(props)
      this.setState({
        shop:{}
      })
    }
  render() { 
     
      console.log("Props shops: " ,this.props.shops)
      const id = window.location.pathname.replace("/shopDetail/", "");
      const data = this.props.shops
      const location = this.props.location.shops
      
      console.log("Location:", location)
      const shop = data.find(s => s.id === id)
      
      return (
  <div>
  
  </div>
)    
}}
 
export default ShopDetails

Shop.js

<Container>
        <ListGroup>
          {this.props.shops.map((detail, index) => (
            <ListGroup.Item key="index">
              <Row>
                <Col>
                  <Image
                    alt=""
                    src={detail.imgURL}
                    width={150}
                    height={150}
                    rounded
                  />
                </Col>
                <Col>
                  <h3 className="shop_title">{detail.nom}</h3>
                  <StarRatings
                    rating={this.props.rating}
                    starRatedColor="#DAA520"
                    changeRating={this.changeRating}
                    numberOfStars={5}
                    starDimension="15px"
                    name="rating"
                    starSpacing="2px"
                  />
                  <p id="resume">{detail.resume}</p>
                </Col>
                <Col>
                  <Row>
                    {detail.startPrice === ""
                      ? "Sur devis"
                      : "A partir de " + detail.startPrice + " €"}
                  </Row>
                  <Row>
                    {/* Make route with id, with key= detail.id */}
                    <Link
                      to={{
                        pathname: "/shopDetail/" + detail.id,
                        state: {shops : this.props.shops}
                      }}
                    >
                      <Button
                        className="detailButton"
                        key={detail.id}
                        variant="primary"
                        onClick={this.props.filterClick}
                      >
                        Détails
                      </Button>
                    </Link>
                  </Row>
                </Col>
              </Row>
            </ListGroup.Item>
          ))}
        </ListGroup>
      </Container>
    );
  }
}

export default Shops;

Let me know if you have any idea. Thanks a lot for your time.

I could be wrong, but this line doesn’t look correct

const location = this.props.location.shops

this.props.location refers to window.location, which doesn’t include the property shops, which would explain why it’s returning undefined


Also, not completely sure why you’re pulling in the location like this

const id = window.location.pathname.replace("/shopDetail/", "");

Couldn’t you just use

const id = this.props.location.pathname...

Hi,

location have shops passed as state like you could see bellow