Choosing how many DIV’s to mount at a time within a . map()

Asked

Viewed 65 times

0

Good morning guys, I went through a recent situation where I had to resolve the basis of a gambiarra but I wonder if there is a way to do what I had in mind at the time but could not accomplish. The point is, I was consuming an API and the same I was having a return of a Object where each Indian obtained one Array with 2 information ( 1 measure and 1 name ). I would like to know if with React I could inform on .map() how many DIV’s I wanted to create every 3 Indices for example, making it easier to stylize later. There is a way to do this ?

This is the return of the Object in the API:

Controller Function - Backend:

async searchById(req, res) {
    const idDrink = req.query.idDrink;

    baseurl.get(`/lookup.php?i=${idDrink}`)
      .then(function (response) {
        const results = response.data.drinks || [];

        if (results.length > 0) {
          const data = results.map(item => {
            return {
              id: item.idDrink,
              name: item.strDrink,
              category: item.strCategory,
              tags: item.strTags,
              alcoholic: item.strAlcoholic,
              glass: item.strGlass,
              instructions: item.strInstructions,
              image: item.strDrinkThumb,
              ingredientsAndMeasures: {
                ingredient1: [item.strMeasure1, item.strIngredient1],
                ingredient2: [item.strMeasure2, item.strIngredient2],
                ingredient3: [item.strMeasure3, item.strIngredient3],
                ingredient4: [item.strMeasure4, item.strIngredient4],
                ingredient5: [item.strMeasure5, item.strIngredient5],
                ingredient6: [item.strMeasure6, item.strIngredient6],
                ingredient7: [item.strMeasure7, item.strIngredient7],
                ingredient8: [item.strMeasure8, item.strIngredient8],
                ingredient9: [item.strMeasure9, item.strIngredient9],
                ingredient10: [item.strMeasure10, item.strIngredient10],
                ingredient11: [item.strMeasure11, item.strIngredient11],
                ingredient12: [item.strMeasure12, item.strIngredient12],
              },
            }
          })
          return res.status(200).json(data)
        }
      })
  },

Components and Page to render Drink - Frontend:

Drinkitem Component:

import React from 'react';
import { connect } from 'react-redux'
import './drink-item.css';

function DrinkItem(props) {

  return (
    <div className='drink-wrapper'>
      {props.items && props.items.map(drink => (
        <div className="drink-items-wrapper" key={drink.id}>
          
          <div className="separate-content">

            <div className="drink-image">
              <a href={drink.image} target="_blank"><img src={drink.image} alt={drink.name}/></a>
              <b>{drink.name || "Drink undefined"}</b>
            </div>

            <div className="drink-content">
              <div className="drink-info">
                <p><b>Glass Recomended:</b> {drink.glass}</p>
                <p><b>Drink Category:</b> {drink.category}</p>
                <p><b>Alcoholic:</b> {drink.alcoholic === 'Alcoholic' ? "Yes" : "No"}</p>
              </div>

              <div className="drink-instruction">
                <h2>Instructions</h2>
                <textarea>{drink.instructions}</textarea>
              </div>
            </div>

          </div>

          <div className="wrap-title-ingredients">
            <h2>Ingredients</h2>
          </div>
          {Object.values(drink.ingredientsAndMeasures).map(ing => (
            ing[0] || ing[1] !== null
              ? 
              <div className="drink-ingredients">
                  <img src={'https://www.thecocktaildb.com/images/ingredients/' + `${ing[1]}` + '-Small.png'} alt={ing} />
                  <p>{ing}</p>
                  <div className="separator"></div>
              </div>
              : <span></span>
          ))}
        </div>
      ))}
    </div>
  )
}

export default connect()(DrinkItem);

Page Drink:

import React, { useState, useEffect } from 'react'
import api from '../../services/api';
import { connect } from 'react-redux';

import DrinkItem from '../../components/DrinkItem/DrinkItem'
import Header from '../../components/Header/Header';
import Footer from '../../components/Footer/Footer';

import { css } from "@emotion/core";
import ClipLoader from "react-spinners/ClipLoader";
import './drink.css';

const override = css`
  display: block;
  margin: 0 auto;
  border-color: red;
`;

function Drink({ drink }) {
  const [drinkInfo, setDrinkInfo] = useState([]);
  const [isDrinkInfoLoading, setisDrinkInfoLoading] = useState(false);

  const idDrink = drink.id || drink.idDrink;

  async function getDrinkInfo() {
    setisDrinkInfoLoading(true)
    await api.get('searchId', {
      params: {
        idDrink: idDrink,
      }
    }).then(function (response) {
      setisDrinkInfoLoading(false);
      setDrinkInfo(response.data)
    })
  }

  console.log(drinkInfo)

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

  return (
    <>
      <div className="content-drink"> 
        <Header />

        <div className="sweet-loading">
          <ClipLoader
            css={override}
            size={150}
            color={"#123abc"}
            loading={isDrinkInfoLoading}
          />
        </div>

        <div className="wrap-drink">
          <div className="wrap-title">
            <h1>
              {drinkInfo.length > 0
              ? <h1>{drinkInfo[0].name}</h1>
              : <h1>Loading Item Name</h1>
              }
            </h1>
          </div>

          {drinkInfo.length > 0
            ? <DrinkItem items={drinkInfo} />
            : <div><p>No Drink to show !</p></div>
          }
        </div>
      </div>
      <Footer />
    </>
  )
}

export default connect(state => ({
  drink: state.drink,
}))(Drink);

I appreciate any help provided.

  • "every 3 Indices for example" what do you mean by indices?

  • ingredient1 for example is an Indice, ie within a DIV I would like to put the ingredient1, 2 and 3, in the next 4, 5 and 6 and so on

  • Friend, I just took and copied the.log console that was given by Node. But anyway, I’ll add the code I’m currently using.

  • Ready. Well, basically with the current code for each Ingredient is formed a DIV, I would like to group more than 1 ingredient in a DIV so I could do a different styling.

1 answer

1


Having this ingredientsAndMeasures that you receive from the API, what you will do is relatively simple, will transform the object into an array (in your case you can disregard the keys).

It will then create an empty array that will receive the separate ingredient arrays and choose the division size.

  const ingredientsArray = Object.values(drink.ingredientsAndMeasures); //Conversão de objeto para array

  const divComponents = []; //Declaração do array que receberá os ingredientes divididos de n em n
  const tamDivisao = 3; //Tamanho da divisão

Having this set, it will split through a foreach in the array with the ingredients, adding n to new arrays within the array divComponents

  ingredientsArray.forEach((v, i) => {
    if(i % tamDivisao === 0){
      divComponents.push([]); // Adiciona um array vazio ao array principal
    }
    divComponents[Math.floor(i/tamDivisao)] = [...divComponents[Math.floor(i/tamDivisao)], v];
  })

Thus, you will have an array with several arrays of size N (containing the split ingredients), just iterate it.

  const divs = <div>{divComponents.map((v)=>{
    return <div style={{border: "1px solid red"}}>{v.map((w)=>{
      return <div></div>;
    })}</div>
  })}</div>;

The divs already is its component, so just style it (and put Keys to identify everything that was iterated);


Follow the whole code of that part

  const ingredientsArray = Object.values(drink.ingredientsAndMeasures); //Conversão de objeto para array

  const divComponents = []; //Declaração do array que receberá os ingredientes divididos de n em n
  const tamDivisao = 3; //Tamanho da divisão

  ingredientsArray.forEach((v, i) => {
    if(i % tamDivisao === 0){
      divComponents.push([]);
    }
    divComponents[Math.floor(i/tamDivisao)] = [...divComponents[Math.floor(i/tamDivisao)], v];
  })

  const divs = <div>{divComponents.map((v)=>{
    return <div style={{border: "1px solid red"}}>{v.map((w)=>{
      return <p>{w[0]}, {w[1]}</p>;
    })}</div>
  })}</div>;

EDIT Version of divs pulling image from API

  const divs = <div>{divComponents.map((v)=>{
    return <div style={{border: "1px solid red"}}>{v.map((w)=>{
      return <div>
              <img src={`https://www.thecocktaildb.com/images/ingredients/${w[1]}-Small.png`} alt={w[1]}/>
              <p>
                {w[0]} - {w[1]}
              </p>
            </div>
    })}</div>
  })}</div>;
  • I can do this part right on the Drink page or it has to be the Drinkitem Component ?

  • Then you choose, in your case the ideal is to do in the component DrinkItem because you are already passing the answer from the server to him, treating there and by granularization. But since apparently (correct me if I’m wrong) you’re only interested in the first return item if it exists, just replace the drink.ingredientsAndMeasures in the conversion to array by drinkInfo[0].ingredientsAndMeasures in the Page Drink

  • In case you use the script I sent with drinkInfo[0].ingredientsAndMeasures

  • Actually I am interested in the 2 information of the Array pq are the measure and the name of ingredient. But the fact is, I think I can do it right on Page because it’s only there that you need to address this issue, since the stylization I could do directly for her. Anyway, I’ll try it both ways and see which one suits me best.

  • I said in the "first return item" because your page apparently shows one drink at a time only, the first item in the case would be the found drink. Then for styling, just add the classes or style in the const divs

  • The page of this publication is actually a Drink in specific, IE, I have a main page with several Drinks and when clicking on one is sent the ID of this to that page, where is searched the information of the same through the ID. With this, it brings this list of ingredients, where I also need to make another call to another part of the API with each of the ingredients Ids to grab the images, since the other API does not bring me this.

  • The point is that I want to do a row of 3 ingredients and their measurements, for example, and down 3 more and so on. Due to the way the API was returning me, I was making 1 ingredient underneath the other and not grouped.

  • I’ll make an edit to pull the images

  • Done, if you solve your problem, if marking the answer as a solution is humble, if you need help with things beyond that, with React in general, in my portfolio have my contact, I’ll be happy to help

  • Later I will do the Wagner tests, if I’m sure, I will definitely mark the answer. Thank you very much for the help and attention so far !!

Show 5 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.