AnApprentice
AnApprentice

Reputation: 110950

How to pass a prop to a child component that is a function?

Given:

Welcome.js

import React from 'react';
import WorkPlacePage from '../../components/welcome/WorkPlacePage';
import SkillPage from '../../components/welcome/SkillPage';

class Welcome extends React.Component {

  constructor(props) {
    super(props);
    switch (props.match.params.welcomeId) {
      case "workplace":
        this.state = { step: 1 };
        break;
      case "skills":
        this.state = { step: 2 };
        break;
      default:
        this.state = { step: 1 };
        break;
    }
    console.log(this.state)
  }

  nextStep(props) {
    console.log('nextStep')
    console.log(this)
    this.setState({
      step : this.state.step + 1
    })
  }

  showStep(props) {
    const {history} = this.props

    switch (this.state.step) {
      case 1:
        return <WorkPlacePage history={history}
                              nextStep={this.nextStep} />
      case 2:
        return <SkillPage history={history}
                          nextStep={this.nextStep} />
      default:
        return (
          <div>
            <h1>Case: Default</h1>
          </div>
        );
    }
  }

  render() {
    var style = {
      width : (this.state.step / 4 * 100) + '%'
    }
    return (
      <main>
        <span className="progress-step">Step {this.state.step}</span>
        <progress className="progress" style={style}></progress>
        {this.showStep()}
      </main>
    )
  }
}



export default Welcome;

WorkPlacePage.js

import React from 'react';
import createBrowserHistory from 'history/createBrowserHistory';

class WorkPlacePage extends React.Component {

  saveAndContinue = (e) => {
    //e.preventDefault()
    this.props.nextStep()
  }

  render() {

    const history = createBrowserHistory();

    return (
      <div>
        <h1>Workplace</h1>
        <span>
        survey things
        // <button onClick={() => history.push('/welcome/skills')}>next page</button>
        </span>

        <button onClick={() => this.saveAndContinue() }>XXX page</button>

      </div>
    );
  }
}

export default WorkPlacePage;

When I click the button, I'm getting the error below for this.props.nextStep()

Uncaught TypeError: Cannot read property 'props' of null

What am I doing wrong?

Upvotes: 0

Views: 2014

Answers (2)

Ravindra Ranwala
Ravindra Ranwala

Reputation: 21124

Here you need to bind the context. Otherwise React can't identify it. Here I have changed the code in a way it works now.

import React, { Component } from 'react';
import WorkPlacePage from './WorkPlacePage'

class Welcome extends Component {
  nextStep() {
      console.log('Next Step callback is invoked !');
  }

  render(){
    return <WorkPlacePage nextStep={this.nextStep.bind(this)} />
  }
}

export default Welcome



import React, { Component } from 'react';

    class WorkPlacePage extends Component {
      saveAndContinue() {
        console.log('Invoking function passed from the parent');
        this.props.nextStep()
      }

      render() {
        return (
          <div>
            <button onClick={ this.saveAndContinue.bind(this) }>Save and Continue</button>
          </div>
        );
      }
    }

    export default WorkPlacePage

Hope this helps. happy coding !

Upvotes: 1

Laith Azer
Laith Azer

Reputation: 619

You need to bind this of the component to your function.

You can either use the ES6 fat arrow syntax:

saveAndContinue = (e) => {
  e.preventDefault()
  this.props.nextStep()
}

Or you can bind it in the constructor function:

constructor(props) {
  super(props);
  this.saveAndContinue = this.saveAndContinue.bind(this);
}

Edit: To answer your remaining bugs:

Anytime you use this.props or this.state in a function you must ensure you are binding this from the component.

So in your Welcome.js, change your nextStep and showStep functions to use ES6 fat arrow syntax.

Regarding the error of Uncaught TypeError: Cannot read property 'preventDefault' of undefined, well it is because you are not passing the event to your function. So you will need to do the following change:

<button onClick={() => this.saveAndContinue() }>XXX page</button>

change to:

<button onClick={ (e) => this.saveAndContinue(e) }>XXX page</button>

Upvotes: 4

Related Questions