2

I am working on fitness app using react js where there is a list of bodyparts. I wanted to have a seperate image for each body part but getting same image for all

BodyPart.js

import React,{useState} from 'react'
import { Stack, Typography } from '@mui/material';

const BodyPart = ({ item, setBodyPart, bodyPart}) => {
    
    const image = require(`../assets/images/bodypart/${bodyPart}.jpg`);
   
    return (
        <Stack type="button"
            alignItems="center"
            justifyContent="center"
            className="bodyPart-card"
            sx={{
                borderTop: bodyPart === item ? '4px solid #ff2625' : '',
                backgroundColor: "#fff",
                borderBottomLeftRadius: '20px',
                width: '270px',
                height: '280px ',
                cursor: 'pointer',
                gap: '47px'
            }}
            onClick={()=>{
                setBodyPart(item);
                window.scrollTo({top:1800, left:100, behavior:'smooth'})
            
            }}
        >
            <img
                src= {image}
                alt='dumbell'
                style={{ width: '70px', height: '70px' }}
            />
            <Typography fontSize="24px" fontWeight="bold" color="#3A1212">
                {item}
            </Typography>
        </Stack>
    )
}

export default BodyPart

HorizontalScroll.js

import React, { useContext } from 'react'
import { Box, Typography } from '@mui/material';
import BodyPart from './BodyPart';
import { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu';
import RightArrowIcon from '../assets/icons/right-arrow.png';
import LeftArrowIcon from '../assets/icons/left-arrow.png';
import ExerciseCard from './ExerciseCard';


const LeftArrow = () => {
    const { scrollPrev } = useContext(VisibilityContext);

    return (
        <Typography onClick={() => scrollPrev()} className="right-arrow">
            <img src={LeftArrowIcon} alt="right-arrow" />
        </Typography>
    );
};

const RightArrow = () => {
    const { scrollNext } = useContext(VisibilityContext);

    return (
        <Typography onClick={() => scrollNext()} className="left-arrow">
            <img src={RightArrowIcon} alt="right-arrow" />
        </Typography>
    );
};


const HorizontalScrollbar = ({ data, bodyParts, setBodyPart, bodyPart}) => {
    
    return (<ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
        {data.map((item) => (
            <Box
                key={item.id || item}
                itemId={item.id || item}
                title={item.id || item}
                m="0 40px"
            >
                {bodyParts ? <BodyPart item={item} 
                    setBodyPart={setBodyPart} bodyPart={bodyPart}/>: <ExerciseCard exercise={item}/> }
            </Box>
        ))}
    </ScrollMenu>
)
}

export default HorizontalScrollbar

SearchExercises.js

import React,{useEffect, useState} from 'react'
import {Box, Button, Stack,TextField, Typography} from '@mui/material';

import {fetchData, exercisesOptions} from '../utils/fetchData';
import HorizontalScrollbar from './HorizontalScrollbar';
import images from './images'

const SearchExercises = ({setExercises,bodyPart,setBodyPart}) => {

  const [search, setSearch] = useState('');
  const [bodyParts, setBodyParts] = useState([])

  useEffect(()=>{
    const fetchExercisesData = async () => {
      const bodyPartData = await fetchData('https://exercisedb.p.rapidapi.com/exercises/bodyPartList',
      exercisesOptions);
      setBodyParts(['all',...bodyPartData]);
   }

   fetchExercisesData();
  }, [])
  
  const handleSearch = async () => {
       if(search) {
        const exercisesData = await fetchData(
          'https://exercisedb.p.rapidapi.com/exercises',
          exercisesOptions
        );
       
        const searchedExercises = exercisesData.filter(
          (item) => item.name.toLowerCase().includes(search)
                 || item.target.toLowerCase().includes(search)
                 || item.equipment.toLowerCase().includes(search)
                 || item.bodyPart.toLowerCase().includes(search),
        );
        setSearch('');
        setExercises(searchedExercises);
       }
  }

  return (
    <Stack alignItems="center" mt="37px"
    justifyContent="center" p="20px">
        <Typography fontWeight={700}
        sx={{fontSize:{lg: '44px', xs:'30px'}}}
        mb="50px" textAlign="center">
            Awesome Exercises You <br/>
            Should know
        </Typography>
        <Box position="relative" mb="72px">
        <TextField 
        sx={{
            input: {fontWeight: '700',
            border :'none', 
            borderRadius:'4px'
        },
        width :{lg:'800px', xs:'350px'},
        backgroundColor : "#fff",
        borderRadius:'40px'
        }}
        height="76px"
        value={search}
        onChange={(e)=>{setSearch(e.target.value.toLowerCase())}}
        placeholder="Search Exercises"
        type="text" />
        <Button className="search-btn"
        sx={{
            bgcolor:'#FF2625',
            color:"#fff",
            textTransform :'none',
            width:{lg:'175px', xs:'80px'},
            fontSize :{lg:'20px', xs:'14px'},
            height:'56px',
            position : 'absolute',
            right:0
        }} onClick={handleSearch}>
          Search
        </Button>
        </Box>

        <Box sx={{position:'relative', width:'100%', p:'20px'}}>
             <HorizontalScrollbar data={bodyParts} bodyParts
              setBodyPart={setBodyPart} bodyPart={bodyPart} isBodyParts/>
        </Box>
    </Stack>
      
  )
}

export default SearchExercises

getting this as output

I wanted to have separate image for each body part. I tried to create array of all bodyParts data with images and name but that also didn't work in this scenarios.

This "https://exercisedb.p.rapidapi.com/exercises/bodyPartList" API returns below data

Added data return by the API

I have already render the required data as individual box here. I just wanted to apply image for each bodyPart separately currently it is applying same image

Gauri
  • 43
  • 3
  • What is the value of `bodyPart` that is passed to `SearchExercises`? This prop is passed all the way through `HorizontalScrollbar` to `BodyPart` where it's the same for all `BodyPart` components. I suspect you really want to grab a property off the `data` prop, (*`bodyParts` array*) that is passed to `HorizontalScrollbar`. We can't see the data that the `"https://exercisedb.p.rapidapi.com/exercises/bodyPartList"` API endpoint returns so we can't even guess as to what you really wanted to render. Can you [edit] to include example API response data, and what you need to render from it? – Drew Reese Jul 05 '23 at 16:05
  • @DrewReese I have updated the question with example – Gauri Jul 05 '23 at 17:43

1 Answers1

0

There is some bodyPart prop being passed to SearchExercises all the way through HorizontalScrollbar to BodyPart... which is the same value for all values the HorizontalScrollbar component is mapping.

The HorizontalScrollbar component's data prop is the body parts data that should be mapped, not the passed bodyPart prop.

SearchExercises

const SearchExercises = ({ activeBodyPart, setExercises, setBodyPart }) => {
  ...
  const [bodyParts, setBodyParts] = useState([]);

  useEffect(() => {
    const fetchExercisesData = async () => {
      const bodyPartData = await fetchData(
        'https://exercisedb.p.rapidapi.com/exercises/bodyPartList',
        exercisesOptions
      );
      setBodyParts(['all', ...bodyPartData]);
   }

   fetchExercisesData();
  }, []);

  ...

  return (
    ...
    <HorizontalScrollbar
      data={bodyParts}
      bodyParts
      setBodyPart={setBodyPart}
      activeBodyPart={activeBodyPart}
    />
    ...
  );

HorizontalScrollbar

const HorizontalScrollbar = ({ activeBodyPart, data, bodyParts, setBodyPart }) => {
  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {data.map((bodyPart) => (
        <Box
          key={bodyPart}
          itemId={bodyPart}
          title={bodyPart}
          m="0 40px"
        >
          {bodyParts
            ? (
              <BodyPart
                activeBodyPart={activeBodyPart}
                setBodyPart={setBodyPart}
                bodyPart={bodyPart}
              />
            ) : <ExerciseCard exercise={bodyPart} />
          }
        </Box>
      ))}
    </ScrollMenu>
  )
}

BodyPart

const BodyPart = ({ activeBodyPart, setBodyPart, bodyPart}) => {
  const image = require(`../assets/images/bodypart/${bodyPart}.jpg`);
   
  return (
    <Stack
      type="button"
      alignItems="center"
      justifyContent="center"
      className="bodyPart-card"
      sx={{
        borderTop: bodyPart === activeBodyPart ? '4px solid #ff2625' : '',
        backgroundColor: "#fff",
        borderBottomLeftRadius: '20px',
        width: '270px',
        height: '280px ',
        cursor: 'pointer',
        gap: '47px'
      }}
      onClick={() => {
        setBodyPart(bodyPart);
        window.scrollTo({ top: 1800, left: 100, behavior: 'smooth' });
      }}
    >
      <img
        src={image}
        alt='dumbell'
        style={{ width: '70px', height: '70px' }}
      />
      <Typography fontSize="24px" fontWeight="bold" color="#3A1212">
        {bodyPart}
      </Typography>
    </Stack>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181