3

I want to attach a callback to a already created react component, is this possible?

This is my wrapper class, I want to call the callbackToCall from the existing children:

import React from 'react';
class MyComponent extends React.Component {

    callbackToCall() {
        console.log("callback called.");
    }    

    render() {
        const {children} = this.props;
        // Here I want to attach the callback to call
        // E.g. children.props.callback = callbackToCall;
        return (
        <div>
            MyStuff
            {children};
        </div>
        ); 
    }
}

Child class, which does not have any callback to the container class:

import React from 'react';
class Child extends React.Component {

    render() {
        return <button onClick={this.props.callback}>Click me</button>
    }
}

This is the call of my component, here I don't know how to reference the callback:

<MyComponent>
    <Child /* Here I cannot set the callback callback={...callbackToCall}*/ />
</MyComponent>
wake-0
  • 3,918
  • 5
  • 28
  • 45
  • This might be useful to you, especially the 2nd answer about React.Context https://stackoverflow.com/questions/32370994/how-to-pass-props-to-this-props-children – Chris Ngo Jan 06 '19 at 10:16
  • I am not sure I understood very well. By the way, if you create MyComponent as an HOC or render props? And the callback will be the prop you pass thorough – quirimmo Jan 06 '19 at 10:17
  • The wrapper component does not know which children it will get. This is done from outside the wrapper component itself. The unknown children component should be able to notify the wrapper about changes by calling some callback. – wake-0 Jan 06 '19 at 10:21

1 Answers1

3

Given that MyComponent is a wrapper that accepts the only child and supposed to provide callback prop to it, it should be:

class MyComponent extends React.Component {
    ...
    render() {
        const child = React.cloneElement(
          React.Children.only(this.props.children),
          { callback: this.callbackToCall }
        );

        return (
          <div>
            MyStuff
            {child};
          </div>
        ); 
    }
}

Alternatively, MyComponent can be provided with a component instead of an element through a prop, like:

class MyComponent extends React.Component {
    ...
    render() {
        return (
          <div>
            MyStuff
            <this.props.component callback={this.callbackToCall}/>
            {this.props.children};
          </div>
        ); 
    }
}

This way MyComponent can additionally accept children for other purposes like <MyComponent component={Child}>...</MyComponent>.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • I already thought about that but is it necessary to clone the children elements? – wake-0 Jan 06 '19 at 10:23
  • @KevinWallis Yes, because you're providing React *element* and not a *component* to MyComponent. If you need to modify props of existing element, you need to clone it. Another pattern that wouldn't require cloning is to make MyComponent accept a component, ``. So you could return it like `` in MyComponent render. Both patterns are used in React world. – Estus Flask Jan 06 '19 at 10:26
  • thanks for your detailed answer, could you please further describe the second approach on the answer above? :) – wake-0 Jan 06 '19 at 10:30