Meteor + React - unable to attach a callback function to a dynamically loaded React component [Solved]


#1

Greetings to the awesome Meteor community!

** This has been solved see below **

Summary: I am unable to attach a callback function to a dynamically loaded React component

Details:
Github rep based on the Meteor example: Material UI Leaderboard

I have a component called Actionable. When the user clicks “Yes” or “No” it should notify the parent component by calling:

this.props.callback('Actionable.' + val);  //possible return values are "Actionable.Yes" and "Actionable.No"

The problem I’m facing, when trying to do this dynamically is that callback (in this.props.callback) is undefined.

Actionable = React.createClass({
    handleClick(val){
        this.props.callback('Actionable.' + val); //error happens here
    },
    render(){
        return <MultiStepYesNo question="Is this actionable?" callback={this.handleClick}/>
    }
});

I have all my steps setup as constants. For example, one constant called ActionableStep contains the above component.

const ActionableStep = {
     "component" : <Actionable callback={MultiStep.handleClick} />,
     "icon" : "run"
}

There are many steps and the user can take many paths to completion. So, I’ve configured a simple associative array.

Here is where the above ActionableStep fits in.

steps = {
"MultiStep.Expand" : {
        "currentstep" : StartStep,
        "nextstep" : ActionableStep,
        "parameters" : "",
        "nextsteplevel" : 1,
        "mintotalsteps" : 3,
        "maxtotalsteps" : 8,
        "maxpctdone" : 0.00,
        "minpctdone" : 0.00,
        "avgpctdone" : 0.00
    },

    . . .

}

In the parent component, MultiStep, the component that is displayed is driven by the state’s key value. Initially, it is “MultiStep.Expand” which will load the “next step” of ActionableStep. This all works. The problem is how can I assign MultiStep’s handleClick function as a callback function to the dynamically loaded component (in this case Actionable)?

MultiStep = React.createClass({
    getInitialState() {
        return {
            key: "MultiStep.Expand"
        }
    },
    handleClick(val){
        this.setState({ key: val });
    },
    render(){
        let key = this.state.key;
        let comp = this.props.steps[key].nextstep.component;
        
        return <div>
            {comp}
        </div>
    }
});

I tried this, but, it didn’t work:

const ActionableStep = {
     "component" : <Actionable callback={MultiStep.handleClick} />,
     "icon" : "run"
}

I tested it like so, and this worked, but, it is not what I am aiming for …

function helloworld() {
     console.log('hello');
}

const ActionableStep = {
     "component" : <Actionable callback={helloworld} />,
     "icon" : "run"
}

One more thing to mention is that all the components and the associative array are in individual .JSX files. I don’t think this matters though.

Any help or advice would be greatly appreciated. I am even willing to re-architect the whole thing if a better approach is presented.


#2

I solved it! I used a previously unknown React function React.cloneElement which allowed me to tack on the parent’s callback function. I’m so happy! :blush:

let comp = React.cloneElement(this.props.nextstep[key].nextstep.component, { callback: this.handleClick });