Anton Grigoryev
Anton Grigoryev

Reputation: 1219

How to call child component method from parent component in react?

There's an array of react components inside parent component. On some event on parent component I'd like to run through the array of child components and call a method for each. How can I achieve that? It seems it doesn't work as in vanilla JS where you have an instance for the class and simply call a method. So here's the simplified code:

const Item = props => {
  const value = () => 'result!';
  return (
        <div></div>
  )
}
 
const App = props => {
  const items = props.items.map(item => <Item key={uuid()} item={item} />)
  const run = e => {
    items.map(item => console.log(item.value()))
  }
  return (
    <button onClick={run} type="button">Run!</button>
  ) 
}

const items = [
  'Lorem ipsum dolor sit amet',
  'consectetur adipiscing elit'
];

ReactDOM.render(<App items={items} />, document.querySelector("#app"))

I've created a jsfiddle with this code - https://jsfiddle.net/jkLq26pg/37/ On the 11th string the method value() is called and it raises an error. But logging item.props works and outputs props for each item. Why methods are not called? Please provide me with an advice how to make it work.

Upvotes: 3

Views: 3119

Answers (2)

gdh
gdh

Reputation: 13702

In your case you have to map thru the array and attach a ref using callback ref. Render the child component using forwardRef and use the useImperativeHandle hook to expose your function to the parent.

Working demo

Code Snippet

const Item = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    value: () => "result!"
  }));

  return <div ref={ref} />;
});

const itemsz = ["Lorem ipsum dolor sit amet", "consectetur adipiscing elit"];
const App = props => {
  const ref = useRef({});

  const items = itemsz.map((item, index) => {
    return (
      <Item
        ref={r => (ref.current[index] = r)}
        key={index}
        item={item}
      />
    );
  });
  const run = e => {
    items.map((item, index) => console.log(ref.current[index].value()));
  };
  return (
    <>
      <h3>click run and see console log</h3>
      <button onClick={run} type="button">
        Run!
      </button>
      {items}
    </>
  );
};

Note - Try avoiding this kind of pattern.

Upvotes: 3

Hassaan Tauqir
Hassaan Tauqir

Reputation: 2752

One way to solve this problem is to write useEffect and call your item.value() logic inside that useEffect. Your function will then be automatically called once your list has mounted to the DOM. That is probably the right place to call your function because your list would have rendered inside the DOM and side effects can then be handled.

const Item = props => {
  const value = () => 'result!';

  // Handle your side effects here
  useEffect(() => {
    value();
  }, []);

  return (
        <div></div>
  )
}

This useEffect will work after a component will mount into DOM Node. You can always put your function in one of the lifecycle methods in class based components or hooks in functional components and call them from there according to your situation.

Upvotes: 0

Related Questions