0

I have an array of components which are all separate views that I am injecting to it's parent via a prop

const steps = 
[ <StepOne wizardContext={shippingObj.from} onAction={this.handleFrom}/>,
  <StepTwo wizardContext={shippingObj.to} onAction={this.handleFrom} />,
  <StepThree wizardContext={shippingObj.weight} onAction={this.handleFrom} />,
  <StepFour wizardContext={shippingObj.shippingOption} onAction={this.handleFrom} />,
  <Confirm wizardContext={shippingObj} onAction={this.handleFrom} />
]
<Wizard steps={steps}/>

In the Wizard component I'm rendering each step based on the actual step

{this.props.steps[this.state.compState]}

Also in the wizard component I have a method

  handleFrom(val) {
    let state = val.stage,
    key = Object.keys(val)[0];
    this.setState({
      wizardContext: { ...this.state.wizardContext, [key]: val[key]}
    })
  }

However onAction is undefined meaning it doesn't have access to "this"

If I include each one of them individually within parent wizard component I do have access to this and it works

/*THIS WORKS */
switch (this.state.compState) {
      case 1:
        return <StepOne onAction={this.handleFrom} data={this.state.wizardContext.from} />
      case 2:
        return <StepTwo onAction={this.handleFrom} data={this.state.wizardContext.to} />
      case 3:
        return <StepThree onAction={this.handleFrom} data={this.state.wizardContext} />
      case 4:
        return <StepFour onAction={this.handleFrom} data={this.state.wizardContext.shippingOption} />
      case 5: 
        return <Confirm shippingLabel={this.state.wizardContext} labelkeys={this.shippingKeys} />
    }

however I want to decouple the steps from the actual wizard

Is there a way to access this or parent methods from the child if the child is an array of components? Is there a cleaner way to do this? the components will render just don't have access to "this" in props Any constructive feedback is appreciated

James Daly
  • 1,357
  • 16
  • 26

1 Answers1

1

The problem is that you are creating your array of steps outside of the Wizard component, so the this context does not point back to the Wizard (and therefore does not have the appropriate handleFrom method).

You can fix this problem by dynamically injecting the correct this.handleFrom method as a prop in the Wizard's render method. First, remove the onAction prop from your steps array:

const steps = 
[ <StepOne wizardContext={shippingObj.from} />,
  <StepTwo wizardContext={shippingObj.to} />,
  <StepThree wizardContext={shippingObj.weight} />,
  <StepFour wizardContext={shippingObj.shippingOption} />,
  <Confirm wizardContext={shippingObj} />
]
<Wizard steps={steps}/>

Then, replace this line:

{this.props.steps[this.state.compState]}

with this:

{
    React.cloneElement(
      this.props.steps[this.state.compState],
      {onAction: this.handleFrom}
    )
}

Make sure you have this.handleFrom = this.handleFrom.bind(this); in the Wizard's constructor so you bind the appropriate context.

What's happening here is that the context for the steps array has no this.handleFrom. So, you inject the onAction={this.handleFrom} prop inside the Wizard component, where the appropriate context is available. For more information on cloneElement, see this question and answer.

Jules Dupont
  • 7,259
  • 7
  • 39
  • 39