Home > Mobile >  How to properly pass functions as props to child component in React?
How to properly pass functions as props to child component in React?

Time:04-25

I currently have a function that I want to pass to the a child that is two levels deep. My function is :

const addToTrip= (isChecked, coordinates, title, yelpID)=>{
  console.log("added to trip ")
  if(isChecked){
    setWaypoints((waypoints)=>[...waypoints, {name:title, yelp_id:yelpID, coordinates:coordinates}])
  } else if(!isChecked){
    const newWaypoints = waypoints.filter((waypoint)=>waypoint.yelp_id !== yelpID)
    setWaypoints(newWaypoints)
  }
}

My function is used in my Businesses component and it is rendered under certain conditions

{hikes.length>0 && (
     <Businesses
       hikes = {hikes}
       addToTrip = {addToTrip}
     />
)}

When I initially run my app and hikes is empty, I thought my function isnt supposed to be invoked until I call it, but it is being called because my waypoints state gets set twice and my console.log("added to trip ") triggers twice as well.

I tried to go about it with changing my render logic of Businesses to

{hikes.length>0 && (
       <Businesses
            hikes = {hikes}
            // addToTrip = {addToTrip}
            addToTrip = {()=>addToTrip}  
       />
)}

With the above code, I dont actually get my console log statements and my waypoints state remain untouched. Now the issue with this is I cant seem to use this function in my child component properly like I was able to in my first approach. I know that I am calling the addToTrip function successfully as my console.log will print as expected, but it's not detecting my input parameters any more.

Here is my first child component:

export const Businesses = (props)=>{
    const {hikes, addToTrip} = props
    return(<>
    <div className="businessesColumn">
    {hikes.map(hike=>(
        <BusinessCard ...
         />
)}

Here is my second child component:

export const BusinessCard = (props)=>{

    const {img, title, location, description, star, reviewCount, addToTrip, coordinates, yelpID} = props;
        
    const [isChecked, setIsChecked] = useState(false);
    useEffect(()=>{
        console.log(isChecked)
        addToTrip(isChecked, coordinates, title, yelpID)
    },[isChecked])
    const handleOnChange = ()=>{
        setIsChecked(!isChecked);
    }
    return (...)

Q1: How come my function is called even when I am not calling it when I am passing it as addToTrip = {addToTrip}? I know react creates the function on every render, unless I usecallback, but is it suppose to "call" it?

Q2: How am I able to pass parameters from child component if I were to pass function as props with addToTrip = {()=>addToTrip}

Q3: Does React create a new addToTrip object for each component it is passed to? Ive noticed that my addToTrip gets called for each component that requires it but doesnt even call it

I greatly appreciate any help.

CodePudding user response:

I can tell in this case that it may not be extremely relevant. But, if you have a huge state tree logic where you would need the same state being passed down from a grandfather component (parent of a parent) to a grandchild (child of a child), then React has a context that helps React JS Context Docs.

For Q1, I believe addToTrip is accurate since you are passing down the function reference (as opposed to passing down something like addToTrip()).

CodePudding user response:

Q1: How come my function is called even when I am not calling it when I am passing it as addToTrip = {addToTrip}? I know react creates the function on every render, unless I usecallback, but is it suppose to "call" it?

It actually does not trigger your function but passes down to child components only. In this context, you don't need to call useCallback, because your function is never modified.

Q2: How am I able to pass parameters from child component if I were to pass function as props with addToTrip = {()=>addToTrip}

addToTrip = {()=>addToTrip} is not a proper way to call your function. It should be

  • addToTrip = {()=>addToTrip()} (it will create a new function every time the component gets re-rendered). In this case, you can call useCalback to memoize your function
  • addToTrip = {addToTrip} (it won't create a new function but re-use the current function)

Q3: Does React create a new addToTrip object for each component it is passed to? Ive noticed that my addToTrip gets called for each component that requires it but doesnt even call it

The problem is from useEffect. When your component get rendered, useEffect will be triggered initially, and addToTrip(isChecked, coordinates, title, yelpID) will get called. For the fix, you should move addToTrip to handleOnChange which is only triggered when you tick on checkboxes

const handleOnChange = ()=>{
   setIsChecked(!isChecked);
   addToTrip(!isChecked, coordinates, title, yelpID);
}
  • Related