Home > Software design >  React: hide and show specific elements from array displayed using map()
React: hide and show specific elements from array displayed using map()

Time:10-29

The goal is to display a series of recipes. The user first gets a general overview of the available recipes containing the name of the recipe, a picture, and some basic information (time to cook, etc). When the user clicks a button (show full recipe), the full recipe should be shown. When the user clicks a new button (hide full recipe) the full recipe is hidden again and the user only sees a general overview of the recipes.

For now, I have created an array of (two) recipes. By using a map() I return the different recipes. However, when the show or hide full recipe button is clicked, all the recipes are shown. How can I make sure that when the user clicks 'show full recipe', he/she only gets to see the specific recipe?

import React, { useState } from 'react';

var recipes = [{
                _id: 1,
                title: "Spaghetti",
                type: "Dinner or Lunch",
                category: "vegan",
                image: "http://via.placeholder.com/350x250",
                cookingTime: 35,
                ingredients: [[1, "onion"], [3, "tomato"], [1, "aubergine"], [1, "beans"], [1, "tomatoesauce"], [1, "tomatoconcentrate"], [1, "pepper"], [1, "salt"], [1, "pasta"]],
                cookingSteps: [["Boil a pot full of watter. Once the watter is boiling, add the pasta. The cooking time depends on the pasta."], ["Cut the onion and the garlic. Add them to a poth with olive oil. Cook for 2 to 3 minutes"], ["Cut the other vegetables. After the onion and the garlic are marrinated, add the different vegtables to the poth. Let them cook with the lid on for about 10 to 15 minutes or untill the vegetables are ready."], ["Once the vegetables are ready, add the tomato sauce and the tomato concentrate. Add the beans. Let this cook for another five minutes"], ["Remove the lid from the pot. Add pepper and salt and other additional spcices, like italian spcies or special peppers"]]
              },
              {
                _id: 2,
                title: "Eggs",
                type: "Breakfast",
                category: "vegan",
                image: "http://via.placeholder.com/350x250",
                cookingTime: 10,
                ingredients: [[4, "egg"], [1, "tomato"], [1, "pepper"], [1, "salt"]],
                cookingSteps: [["cut the tomato. Heat olive oil in a pan, add the tomato and bake it till the outer peal of the tomato gets soft. If you like pepper, it is adviced to first bake the pepper a bit in the pan"], ["Meanwhile, scrambel the eggs. Once the tomatoes are ready (see step 1), add the eggs"]]
              }
            ];


//TODO: make sure only one specific recipe shows when clicked
function DisplayRecipes(){
    //declare useState and onClick for button to show full recipe
    const[isNotDisplayed, isDisplayed] = React.useState(false)
    const onClickShow = () => {isDisplayed(true)};
    const onClickHIde = () => isDisplayed(false);


    //return statement to render recipe
    return(
        <div className="card-list">
            <div className="container">
                <h1 className="page-title">Our vegan recipes...</h1>
                <div className="row">
                    {
                        recipes.map((recipe)=>{
                            return(
                                <div key={recipe._id} className="col-md-3">
                                    <div className="card bwm-card">
                                        <div className="card-title">
                                            <h2>{recipe.title}</h2>
                                        </div>
                                        <img className="card-img-top" src={recipe.image} alt={recipe.title} />
                                        <div className="card-subtitle">
                                            <h3><b>Type: {recipe.type} </b></h3>
                                            <h3><b>Category: {recipe.category}</b></h3>
                                            <h3><b>Cooking time: {recipe.cookingTime} minutes</b></h3>
                                        </div>
                                        <div>
                                            { isNotDisplayed ? 
                                                    <div className="card-body">
                                                        <div className="card-body-ingredient">
                                                        {
                                                            recipe.ingredients.map((ingredient, i) =>{
                                                                return(
                                                                    <table>
                                                                        <tr>
                                                                            <th>Quantity</th>
                                                                            <th>Ingredient</th>
                                                                        </tr>
                                                                        <tr>
                                                                            <th>{ingredient[0]}</th>
                                                                            <th>{ingredient[1]}</th>
                                                                        </tr>
                                                                    </table>
                                                                )
                                                            })
                                                        }
                                                    </div>
                                                    <div className="card-body-cookingsteps">
                                                            {
                                                                recipe.cookingSteps.map((cookingstep, i) =>{
                                                                    return(
                                                                        <p>{cookingstep}</p>
                                                                    )
                                                                })
                                                            }
                                                    </div>
                                                        <div>
                                                            <button type="submit" value="Hide full recipe" onClick= {onClickHIde}>Hide full recipe</button>
                                                        </div>
                                                    </div>
                                                : 
                                                    <button type="submit" value="Show full recipe" onClick= {onClickShow}>Show full recipe</button>
                                            } 
                                        </div>
                                    </div>
                                </div>
                            )
                        })
                    } 
                </div>
            </div>
        </div>
    )
} 

export default DisplayRecipes;

Thank you in advance!

CodePudding user response:

You can consider using refs here:

Pass the ref to your onClickShow and show the div with that particular ref.

You can also use CSS, in this case, get the div using the ref and mark display of that div as none and vice-versa.

CodePudding user response:

You need a separate piece of state for each recipe. Currently all the recipes share the same state, so when one is open, they're all open.

The best way to separate out the state is to make each recipe its own component with its own state.

import React, {useState} from 'react';

const recipes = [
    {
        _id: 1,
        title: 'Spaghetti',
        type: 'Dinner or Lunch',
        category: 'vegan',
        image: 'http://via.placeholder.com/350x250',
        cookingTime: 35,
        ingredients: [
            [1, 'onion'],
            [3, 'tomato'],
            [1, 'aubergine'],
            [1, 'beans'],
            [1, 'tomatoesauce'],
            [1, 'tomatoconcentrate'],
            [1, 'pepper'],
            [1, 'salt'],
            [1, 'pasta'],
        ],
        cookingSteps: [
            [
                'Boil a pot full of watter. Once the watter is boiling, add the pasta. The cooking time depends on the pasta.',
            ],
            [
                'Cut the onion and the garlic. Add them to a poth with olive oil. Cook for 2 to 3 minutes',
            ],
            [
                'Cut the other vegetables. After the onion and the garlic are marrinated, add the different vegtables to the poth. Let them cook with the lid on for about 10 to 15 minutes or untill the vegetables are ready.',
            ],
            [
                'Once the vegetables are ready, add the tomato sauce and the tomato concentrate. Add the beans. Let this cook for another five minutes',
            ],
            [
                'Remove the lid from the pot. Add pepper and salt and other additional spcices, like italian spcies or special peppers',
            ],
        ],
    },
    {
        _id: 2,
        title: 'Eggs',
        type: 'Breakfast',
        category: 'vegan',
        image: 'http://via.placeholder.com/350x250',
        cookingTime: 10,
        ingredients: [
            [4, 'egg'],
            [1, 'tomato'],
            [1, 'pepper'],
            [1, 'salt'],
        ],
        cookingSteps: [
            [
                'cut the tomato. Heat olive oil in a pan, add the tomato and bake it till the outer peal of the tomato gets soft. If you like pepper, it is adviced to first bake the pepper a bit in the pan',
            ],
            [
                'Meanwhile, scrambel the eggs. Once the tomatoes are ready (see step 1), add the eggs',
            ],
        ],
    },
];

const Recipe = ({recipe}) => {
    const [isOpen, setIsOpen] = React.useState(false);

    return (
        <div className="col-md-3">
            <div className="card bwm-card">
                <div className="card-title">
                    <h2>{recipe.title}</h2>
                </div>
                <img className="card-img-top" src={recipe.image} alt={recipe.title} />
                <div className="card-subtitle">
                    <h3>
                        <b>Type: {recipe.type} </b>
                    </h3>
                    <h3>
                        <b>Category: {recipe.category}</b>
                    </h3>
                    <h3>
                        <b>Cooking time: {recipe.cookingTime} minutes</b>
                    </h3>
                </div>
                <div>
                    {isOpen ? (
                        <div className="card-body">
                            <div className="card-body-ingredient">
                                {recipe.ingredients.map((ingredient, i) => {
                                    return (
                                        <table>
                                            <tr>
                                                <th>Quantity</th>
                                                <th>Ingredient</th>
                                            </tr>
                                            <tr>
                                                <th>{ingredient[0]}</th>
                                                <th>{ingredient[1]}</th>
                                            </tr>
                                        </table>
                                    );
                                })}
                            </div>
                            <div className="card-body-cookingsteps">
                                {recipe.cookingSteps.map((cookingstep, i) => {
                                    return <p>{cookingstep}</p>;
                                })}
                            </div>
                            <div>
                                <button
                                    type="submit"
                                    value="Hide full recipe"
                                    onClick={() => setIsOpen(false)}>
                                    Hide full recipe
                                </button>
                            </div>
                        </div>
                    ) : (
                        <button
                            type="submit"
                            value="Show full recipe"
                            onClick={() => setIsOpen(true)}>
                            Show full recipe
                        </button>
                    )}
                </div>
            </div>
        </div>
    );
};

function DisplayRecipes() {
    return (
        <div className="card-list">
            <div className="container">
                <h1 className="page-title">Our vegan recipes...</h1>
                <div className="row">
                    {recipes.map((recipe) => (
                        <Recipe key={recipe._id} recipe={recipe} />
                    ))}
                </div>
            </div>
        </div>
    );
}

export default DisplayRecipes;
  • Related