如何将不同的数据集多次渲染到一个 React 组件中?

How can I render different sets of data into one React component multiple times?

我有食谱的数据集,我想将其映射到我制作的旋转木马上的卡片上。

我正在尝试用最少的代码以最有效的方式做到这一点,我已经通过为每组食谱创建多个滑块来实现它。但是我想做到这一点,所以我只需要一个滑块组件,其中已经有卡片组件 - 然后我可以根据需要将数据映射到其中。而不是只有几个相同的组件,我之前已经将每个数据集映射到这些组件中。

下面的代码将显示我正在尝试做的事情。

还有一个代码 sandbox 如果您转到菜单部分并单击意大利面选项,然后点击顶部的海鲜按钮,它将显示我目前遇到的问题,即我的映射方法不起作用。

recipeCard.js

import React from 'react'
import { MenuCard } from './menuCard'
import styled from 'styled-components'
import salad from '../assets/homePage/salad.jpg'

export const RecipeCard = ({image, title}) => {
    return (
        <div>
            <div style={{height: "200px"}}>
                <RecipeCardDiv>
                    <ImageHolder>
                        <img style={{height: "100%", width: "100%", borderRadius: "25px 25px 0px 0px",}} src={image}/>
                    </ImageHolder>
                    <RecipeTitleDiv>
                        {title}
                    </RecipeTitleDiv>
                    <RecipeButton>
                        Recipe

                    </RecipeButton>


     
                </RecipeCardDiv>   
            </div>
            
        </div>
    )
}

export const RecipeCardDiv = styled.div`
    position: absolute;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 132px;
    height: 180px;
    /* left: 47px;
    top: 128px; */
    z-index: 0;

    background: #F6F6F6;
    box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
    border-radius: 25px;
`

const ImageHolder = styled.div`
    position: absolute;
    height: 95px;
    width: 132px;
    border-radius: 25px;
    /* background-color: red; */
    top: 0;

`
const RecipeTitleDiv = styled.span`
    position: absolute;
    height: 25px;
    width: 100%;
    top: 58%;
    left: 5%;

    font-family: 'Kaisei Opti', serif;
    font-style: bold;
    font-weight: 900;
    font-size: 14px;
    

    color: #000000;

`
const RecipeButton = styled.button`
    position: absolute;
    height: 22.5px;
    width: 75px;
    border-radius: 25px;
    font-family: 'Kaisei Opti', serif;
    font-style: bold;
    border: none;
    outline: none;

    bottom: 6%;
    /* right: 10%; */
    color: white;

    background: #30E3CA;
    box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);

`

menuCarousel.js

import React, { Component } from "react";
import Slider from "react-slick";
import { meatPastaRecipes, seafoodPastaRecipes, veggiePastaRecipes } from "../data/pastaRecipes";
import { veggieSaladsRecipeCards } from "../data/saladRecipes";
import { PastaCard, NoodlesCard } from "./menuCard";
import { RecipeCard } from "./recipeCard";



//below is how I have been mapping my data into the various sliders and then rendering these sliders where I need them in application
//However I want to be able to have the one slider component and map the data into the cards in the carousel at the point in the application where the slider is being rendered
export  class VeggieSaladSlider extends Component {
  render() {
    const settings = {
      dots: true,
      infinite: true,
      speed: 500,
      slidesToShow: 2,
      slidesToScroll: 1,
    //   variableWidth: true,
      variableHeight: true,
      centerMode: true,
      centerPadding: "6px",
      draggable: true
    };
    return (
      <div style={{height: "220px", width: "300px", padding: "2rem", marginTop: "3.5rem", overflow: "hidden"}}>
        <Slider {...settings}>
          
            {veggieSaladsRecipeCards.map(salad => 
              <div>
              <RecipeCard
                key={salad.id}
                title={salad.title}
                image={salad.image}
              />
              </div>
            )}
        </Slider>
      </div>
    );
  }
}

export  class VeggiePastaSlider extends Component {
  render() {
    const settings = {
      dots: true,
      infinite: true,
      speed: 500,
      slidesToShow: 2,
      slidesToScroll: 1,
    //   variableWidth: true,
      variableHeight: true,
      centerMode: true,
      centerPadding: "6px",
      draggable: true
    };
    return (
      <div style={{height: "220px", width: "300px", padding: "2rem", marginTop: "3.5rem", overflow: "hidden"}}>
        <Slider {...settings}>
          
            {veggiePastaRecipes.map(pasta => 
              <div>
              <RecipeCard
                key={pasta.id}
                title={pasta.title}
                image={pasta.image}
              />
              </div>
            )}
        </Slider>
      </div>
    );
  }
}

export  class MeatPastaSlider extends Component {
  render() {
    const settings = {
      dots: true,
      infinite: true,
      speed: 500,
      slidesToShow: 2,
      slidesToScroll: 1,
    //   variableWidth: true,
      variableHeight: true,
      centerMode: true,
      centerPadding: "6px",
      draggable: true
    };
    return (
      <div style={{height: "220px", width: "300px", padding: "2rem", marginTop: "3.5rem", overflow: "hidden"}}>
        <Slider {...settings}>
          
            {meatPastaRecipes.map(pasta => 
              <div>
              <RecipeCard
                key={pasta.id}
                title={pasta.title}
                image={pasta.image}
              />
              </div>
            )}
        </Slider>
      </div>
    );
  }
}

//This is the template for the slider that I am trying to be able to use multiple times with different data sets mapped into it
//see code file below (pastaMenuPage.js) where the <SeadfoodPastaSlider/> is being rendered and having data mapped into it
export  class SeafoodPastaSlider extends Component {
  render() {
    const settings = {
      dots: true,
      infinite: true,
      speed: 500,
      slidesToShow: 2,
      slidesToScroll: 1,
    //   variableWidth: true,
      variableHeight: true,
      centerMode: true,
      centerPadding: "6px",
      draggable: true
    };
    return (
      <div style={{height: "220px", width: "300px", padding: "2rem", marginTop: "3.5rem", overflow: "hidden"}}>
        <Slider {...settings}>
          
              <div>
                <RecipeCard/>
              </div>
          
        </Slider>
      </div>
    );
  }
}

pastaMenuPage.js

import React, {useState} from 'react'
import { HeaderNav } from '../../components/header'
import styled from 'styled-components'

import { PopularCardo } from '../../components/popularCard'
import LazyLoad, { MeatPastaSlider, SeafoodPastaSlider, VeggiePastaSlider } from '../../components/menuCarousel'
import { MenuSection, OptionsButtonMeat, OptionsButtonSeafood, OptionsButtonVeggie, OptionsDiv, PopularCardDiv, PopularSection, TitleDiv } from './saladMenuPage'
import { veggiePastaRecipes, seafoodPastaRecipes } from '../../data/pastaRecipes'




export const PastaMenuPage = () => {

    const [active, setActive] = useState("VeggieMenu");
    
    return (
        <div style={{height: "100vh", overflow: "hidden", display: "flex", justifyContent: "center", alignItems: "center"}}>
            <HeaderNav/>
            <TitleDiv>
                Pasta
            </TitleDiv>
            <OptionsDiv>
                <OptionsButtonMeat onClick={()  => setActive("MeatMenu")}>
                    Meat
                </OptionsButtonMeat>
                <OptionsButtonSeafood style={{marginLeft: "52.5%"}} onClick={()  => setActive("SeafoodMenu")}>
                    Seafood
                </OptionsButtonSeafood>
                <OptionsButtonVeggie style={{marginRight: "52.5%"}} onClick={()  => setActive("VeggieMenu")}>
                    Veggie
                </OptionsButtonVeggie>
            </OptionsDiv>
            <MenuSection>
                {active === "VeggieMenu" && <VeggiePastaSlider/>}
                {active === "MeatMenu" && <MeatPastaSlider/>}
                {active === "SeafoodMenu" && seafoodPastaRecipes.map(pasta => 
                    <SeafoodPastaSlider
                        key={pasta.id}
                        title={pasta.title}
                        image={pasta.image}
                    />
                )} 
            </MenuSection>
            <TitleDiv style={{top: "67.5%"}}>
                Popular 
            </TitleDiv>
            <PopularSection>
                <PopularCardDiv>
                    {veggiePastaRecipes.slice(2,3).map(pasta => 
                            <PopularCardo
                            key={pasta.id}
                            title={pasta.title}
                            image={pasta.image}
                            />
                    )}
                </PopularCardDiv>
            </PopularSection>
        </div>
    )
}

和数据文件柜 pastaRecipes.js

export const veggiePastaRecipes = [
    {
        id: 1,
        title: "Mushroom & Leek",
        image: "https://cmx.weightwatchers.co.uk/assets-proxy/weight-watchers/image/upload/t_WINE_EXTRALARGE/ak6clrxuzruvrv3wweqj.jpg",
        recipe: ""
    },
    {
        id: 2,
        title: "Cacio e Pepe",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/cacio-e-pepe-with-runner-beans-e523207.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 3,
        title: "Spaghetti Primavera",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/healthy-pasta-primaver-35cbc26.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 4,
        title: "Caponata",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/caponata-pasta-a0027c4.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 5,
        title: "Tomato & Avocado",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/mexican-penne-5cd4efb.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 6,
        title: "Mac n Cheese",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/macaroni-cheese-251d55c.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
]

export const meatPastaRecipes = [
    {
        id: 1,
        title: "Spaghetti Bolognese",
        image: "https://www.slimmingeats.com/blog/wp-content/uploads/2010/04/spaghetti-bolognese-36-720x720.jpg",
        recipe: ""
    },
    {
        id: 2,
        title: "Tuna Pasta Bake",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/recipe-image-legacy-id-51616_12-796faab.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 3,
        title: "Classic Lasagne",
        image: "https://cdn.bosh.tv/uploads/images/recipes/_full/Lasagne-Website.jpg?v=1601992601",
        recipe: ""
    },
    {
        id: 4,
        title: "Broccoli & Salmon Bake",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/recipe-image-legacy-id-227467_12-0d8623c.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 5,
        title: "Beef Stroganoff",
        image: "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/beefstroganoff-d53f55e.jpg?quality=90&webp=true&resize=300,272",
        recipe: ""
    },
    {
        id: 6,
        title: "Spaghetti Carbonara",
        image: "https://easyweeknight.com/wp-content/uploads/2019/02/spaghetti-carbonara3.jpg",
        recipe: ""
    },
]

export const seafoodPastaRecipes = [
    {
        id: 1,
        title: "Crab Ravioli",
        image: "https://media-cdn.greatbritishchefs.com/media/oqobaojp/img27462.jpg?mode=crop&width=768&height=512",
        recipe: ""
    },
    {
        id: 2,
        title: "Crab Linguine",
        image: "https://media-cdn.greatbritishchefs.com/media/sxipor0k/img11530.jpg?mode=crop&width=768&height=512",
        recipe: ""
    },
    {
        id: 3,
        title: "Conchiglie Frutti Di Mare",
        image: "https://media-cdn.greatbritishchefs.com/media/eqypizew/img62005.jpg?mode=crop&width=768&height=512",
        recipe: ""
    },
    {
        id: 4,
        title: "Fishghetti",
        image: "https://media-cdn.greatbritishchefs.com/media/tv4foh5e/img26026.jpg?mode=crop&width=768&height=512",
        recipe: ""
    },
    {
        id: 5,
        title: "Prawn Linguine",
        image: "https://media-cdn.greatbritishchefs.com/media/al4icv5v/img52113.jpg?mode=crop&width=768&height=512",
        recipe: ""
    },
]

总而言之,我正在寻找一种更先进、更有效的方法来将数据映射到一个组件中,这样我就不需要在一个文件中包含多个相同的组件。

您在这里遇到的问题是,您预先告诉 Slider 您将要呈现特定的项目列表。因此,我们在所有想要实现轮播行为的地方重复滑块逻辑。

但我们需要的是滑块动态呈现内容,因为滑块不关心它需要呈现什么。它需要做的就是提供轮播行为。这在反应中可以使用 children prop.

来实现

为滑块创建一个新组件,

解决方案 1

SliderContainer.js

import React from "react";
import Slider from "react-slick";

const settings = {
  dots: true,
  infinite: true,
  speed: 500,
  slidesToShow: 2,
  slidesToScroll: 1,
  //   variableWidth: true,
  variableHeight: true,
  centerMode: true,
  centerPadding: "6px",
  draggable: true
};

const SliderContainer = ({ children }) => (
  <div
    style={{
      height: "220px",
      width: "300px",
      padding: "2rem",
      marginTop: "3.5rem",
      overflow: "hidden"
    }}
  >
    <Slider {...settings}>{children}</Slider>
  </div>
);

export default SliderContainer;

现在在所有你想实现轮播行为的地方使用这个组件。

export class VeggieSaladSlider extends Component {
  render() {
    return (
      <SliderContainer>
        {veggieSaladsRecipeCards.map((salad) => (
          <div>
            <RecipeCard
              key={salad.id}
              title={salad.title}
              image={salad.image}
            />
          </div>
        ))}
      </SliderContainer>
    );
  }
}

解决方案 2

如果保证所有轮播项目都具有以下形状

{
  id: ...,
  title: ...,
  image: ...
}

我们可以进一步增强 SliderContainer 组件来获取一个道具,它是一个项目列表而不是子道具。

import React from "react";
import Slider from "react-slick";

import { RecipeCard } from "./recipeCard";

const settings = {
  dots: true,
  infinite: true,
  speed: 500,
  slidesToShow: 2,
  slidesToScroll: 1,
  //   variableWidth: true,
  variableHeight: true,
  centerMode: true,
  centerPadding: "6px",
  draggable: true
};

const SliderContainer = ({ items }) => (
  <div
    style={{
      height: "220px",
      width: "300px",
      padding: "2rem",
      marginTop: "3.5rem",
      overflow: "hidden"
    }}
  >
    <Slider {...settings}>
      {items.map(({ id, title, image }) => (
        <div key={id}>
          <RecipeCard key={id} title={title} image={image} />
        </div>
      ))}
    </Slider>
  </div>
);

export default SliderContainer;

现在有了这个改变,我们可以将不同的滑块呈现为

export class VeggieSaladSlider extends Component {
  render() {
    return <SliderContainer items={veggieSaladsRecipeCards} />;
  }
}

您可以将自定义道具传递给您的组件,请参阅下面的示例:

class PastaSlider extends React.Component{

    render() {
        // this "customProp" key was set as an attribute to PastaSlider component and can be accessed from this.props 
        return <div>{this.props.customProp}</div>
    }

}

function App() {

 // this is your import { veggieSaladsRecipeCards, etc } from "../data/saladRecipes";
  const recipes =[
    'veggie',
    'pasta',
    'meatz'
  ]

  return (
    <div className="App">
        {recipes.map( (item,index) => <PastaSlider key={index} customProp={item} />)}
    </div>
  );
}
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
<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>
<div id="root" ></div>