kwi
kwi

Reputation: 23

Passing data between several components in React

So, I've got this view of animal cards called like this:

    <AnimalsCard {...elephant}/>
    <AnimalsCard {...hippo}/>
    <AnimalsCard {...sugar_glider}/>

View

My AnimalCard code looks like this:

export const AnimalsCard = ({ id, animal, img }) => {
    const { t } = useTranslation();
    return (
        <>
                <CardContainer id={id} img={img}>
                <CardContent>
                    <CardTitle>{animal}</CardTitle>
                    <CardButton to={`${id}`}>Dowiedz się więcej</CardButton>
                    </CardContent>
                </CardContainer>

        </>
    )
};

And my animal objects look like that:

export const elephant = {
    animal: 'Słoń indyjski',
    id: 'slon_indyjski',
    species: 'mammals',
    img: require('../../../images/animals/mammals/elephant.jpg'),
    occurance: '',
    habitat: '',
    diet: '',
    reproduction: '',
    conservationStatus: '',
    funFactOne: ''
}

When I click the button, new page of the animal id opens up:

        <Route path="/:id" component={AnimalsDetails} />

On AnimalsDetails page, I would like to display all of the animal object data. I unfortunately have no idea how to pass it because I'm a beginner and hardly know anything about props and stuff. My current approach is that in the AnimalsDetails I retrieve the ID using useParams();

export const AnimalsDetails = () => {
    const [mammal, setMammal] = useState(null);
    let { id } = useParams();
    
useEffect(() => {
        const mammal = mammalsData.filter(thisMammal => thisMammal.id === id);
        setMammal(mammal);
        // console.log(mammal[0].animal);
     }, [id]);

 return ( <AnimalsDetailsContainer>
                {/* <div>{mammal[0].id}</div> */}
            </AnimalsDetailsContainer>
    )
};

export default AnimalsDetails;

And then using the ID from URL, I'm filtering my mammalsData array that I've created only to test if the approach works (I wish I could use the previously created objects but I don't know if it's possible). It looks like that:

export const mammalsData = [
    {

    [...]

},

{
    animal: 'abc',
    id: 'fgh',
    species: 'idontknow',
    img: require('whatimdoing.JPG'),
    occurance: '',
    habitat: '',
    diet: '',
    reproduction: '',
    conservationStatus: '',
    funFactOne: ''
}
];

For now it works only when I'm on the animals card page and click the button, so the ID is present. If I'm somewhere else on the page and my ID is not declared yet, I receive an error that the data I want to display is undefined, which makes sense obviously. I've tried wrapping the return of the AnimalsDetails to log an error if the ID is not present but it didn't work.

I know there is some decent way to do that (probably with props or hooks defined differently), but I really tried many stuff and I'm utterly lost now. I wish I could pass the data to AnimalsDetails within clicking the button or something but have no idea how to do that. :-(

I'll be grateful for any help. I know it's basic stuff, but it's my first website ever.

Upvotes: 0

Views: 78

Answers (4)

Lakruwan Pathirage
Lakruwan Pathirage

Reputation: 657

when you want to pass your data with url, in the react router v6 has that capability.

Let say this is your Animals component,

import { Link } from "react-router-dom";

//let assume this is the data that you need to send
const elephant = {
  animal: "Słoń indyjski",
  id: "slon_indyjski",
  species: "mammals",
  img: require("../../../images/animals/mammals/elephant.jpg"),
  occurance: "",
  habitat: "",
  diet: "",
  reproduction: "",
  conservationStatus: "",
  funFactOne: "",
};

const Animals = props => {
  //elephant.id is dynamic value. You have to add that value dynamically.
  return (
    <Link to={`${elephant.id}`} state={{ singleAnimal: elephant }}>
      <div>this is single animal</div>
    </Link>
  );
};

In the Link component you have to mention your dynamic route and you cant send your data from state attribute.

In the receiving end,(Let say your receiving end component is SingleanimalData.

import { useLocation } from "react-router-dom";
const SingleanimalData = props => {
  const location = useLocation();
  //animal variable now has that recieving data.so console it to view data
  const animal = location.state.singleAnimal;

  return (
    <div>
      <div>My animal name:</div>
      <div> {animal.animal} </div>
    </div>
  );
};

In here you have to use useLocation hook to grab data. I hope you this make sense to you.

Upvotes: 1

DevAra
DevAra

Reputation: 530

Try this way. In the parent component add this part

<Route path="/:id"  render={()=> <AnimalsDetails item={...animaldata} />} />

In AnimalDetails component

    import React, { useEffect } from 'react';
    import {useParams} from 'react-router-dom';
    
   const { animalId  } = useParams();
     
    let animal;
    useEffect(()=> {
      
   
    if(animalId && props.item) {
       animal = props.item.filter(a => a.id === animalId)
    }
    
    
    }, [animalId]);

Upvotes: 0

Nikolay
Nikolay

Reputation: 12245

If you are using the latest react router (v6), use element instead of render (here):

<Route path="/:id"  element={<AnimalsDetails animal={yourAnimal} />} />

Upvotes: 0

Andy Halmon
Andy Halmon

Reputation: 21

I understand your problems. In my opinion you 'd like to focus useEffect method as below.

useEffect(() => {
 if(id != undefined)
    // console.log('error);
     return (
       <div>{'error'}</div>
     );
    const mammal = mammalsData.filter(thisMammal => thisMammal.id === id);
    setMammal(mammal);
    // console.log(mammal[0].animal);
 }, [id]);

Upvotes: 2

Related Questions