skip to Main Content

I’ve a parent component and a child component. I’m using array.map() method to call the child component for every value in the messages array.

now I want to do something inside the child component every time a button in the parent is clicked. so I’ve set up a useEffect with a dep array, inside the child.

Is this the right approach?
because if there are a 100 values inside the array the useEffect will be called those many times.
and if I’m doing something heavy such as using a for loop inside the effect, won’t it be a bad implementation.

is there a better approach for this scenario? especially when I only want to run the useffect for a few selected array values.

parent component:

import React, {useState} from 'react';

export function App() {

  let [clicked,setClicked] = useState(false);
  let messages = ['a','n','c','d'];
  

  let changer = () =>{
    setClicked(true);
  }

  return (
    <div className='App'>
      {messages.map((o) => (
                                            
        <div>
          <Childcomp clicked={clicked}/>
        </div>
        
       ))}
       <button onClick={changer}> click me </button>
    </div>
  );


}

child component:

import React, {useState, useEffect} from 'react';

export function Child({clicked}) {

  useEffect(()=>{

    if(clicked){
      console.log('parent tapped :)');   // do something useful/heavy

    }

  },[clicked])

 
  return (
    <div className='child'>
       <p className='class1'>hi i'm child</p>
    </div>
  );


}

2

Answers


  1. Try this, if component will render for first item of array then useEffect will called. for the other items of array useEffect will return and will not do anything.

    // App.jsx
    
    import { useState, useEffect} from 'react';
    
    export default function App() {
    
      let [clicked, setClicked] = useState(false);
      let messages = ['a', 'n', 'c', 'd'];
    
      let changer = () => {
        setClicked(!clicked);
      }
    
      return (
        <div className='App'>
          {messages.map((o, index) => (
            <div key={index}>
              <Child clicked={clicked} index={index} />
            </div>
          ))}
          <button onClick={changer}> click me </button>
        </div>
      );
    }
    
    
    // Child.jsx
    export function Child({ clicked, index }) {
    
      useEffect(() => {
        if (index !== 0) return;
        console.log('parent tapped :)'); 
      }, [clicked, index]);
    
      return (
        <div className='child'>
          <p className='class1'>hi i am child</p>
        </div>
      );
    }
    
    
    Login or Signup to reply.
  2. if there are a 100 values inside the array the useEffect will be called those many times.

    You are right, with your implementation when clicked state change value in the parent component, useEffect of each returned Childcomp will fire since clicked props is included in the dependecy array

    It seems like you want to run useEffect only for one specific Childcomp but which one? it is not clear with your implementation, since here clicked state is a simple boolean value.
    If I understood correctly what you want to achieve, you might need to store the index of the clicked Childcomp in clicked state because otherwise, you’ll have no information about which child component should run its useEffect hook:

    export function App() {
    
      let [clicked,setClicked] = useState();
      let messages = ['a','n','c','d'];
      
    
      let changer = (index) =>{
        setClicked(index);
      }
    
      return (
        <div className='App'>
          {messages.map((o, index) => (
                                                
            <div key={index} onClick={() => {changer(index)}}>  
              <Childcomp clicked={clicked} index={index}/> 
            </div>
            
           ))}
        </div>
      );
    }
    

    Now with this update, each time you click the wrapper div of one Childcomp you store its index in clicked state.

    Note: if it is possible, better to store o.id and use it as index.
    I kow thet o is a string here but maybe this is a simple example.

    <div key={o.id} onClick={() => {changer(o.id)}}> 
        <Childcomp clicked={clicked} index={o.id}/> 
    </div>
    

    In the child component, you still need to fire useEffect each time clicked props changes, you can’t avoid that, but you can make the code inside the effect run only when the clicked child component is that one, i.e. when index props is equal to clicked props

    export function Child({clicked, index}) {
    
      useEffect(()=>{
        console.log('useEffect is running')
        if(clicked === index ){ 
          console.log(`the code inside the effect is running only for the component with the index equal to ${index} `); 
        // do something useful/heavy
        }
      },[clicked])
    
      return (
        <div className='child'>
           <p className='class1'>hi i'm child</p>
        </div>
      );
    
    }
    

    codesandbox

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search