Search code examples
reactjsreact-routerreact-reduxreact-router-redux

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?


Solution

  • 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>