John Rogers
John Rogers

Reputation: 35

React states are undefined after calling parent function with onClick

in the child component, whenever I click one of the mapped Span elements, the onClick triggers the switchLaunch function in the parent component and triggers the debugger I have there now for testing purposes. However, all the states I had set are now undefined. So when I call this.state.allPastLaunches, it's now undefined but if I call this.allPastLaunches, the data is there. I'm confused as to where I'm going wrong

Parent Component (App)

class App extends Component {
  //api call for latest space launch
  constructor(props) {
    super(props);
    this.state = {
      selectedLaunch: "",
      launchData: "",
      videoURL: null,
      allPastLaunches: [],
      showVideo: false
    };
  }

  componentWillMount() {
    this.getLaunchData()
  }

  switchLaunch(launch) {
    debugger
    // ALL MY STATES ARE EMPTY HERE WHEN I TRY TO LOG THEM AFTER THE FUNCTION 
    IS TRIGGERED BY THE CHILD'S ONCLICK
  }


  getLaunchData() {
    // getting latest launch
    fetch('https://api.spalta.launch/launch')
      .then(response => {
        return response.json();
      })
      .then(json => {
        this.setState({ 
          launchData: json,
          videoURL: json.links["video_link"],
          missionPatch: json.links["mission_patch"]
        });
      });

    //getting all previous launches
    fetch('https://api.spalta.launch/prevLaunches')
      .then(response => {
        return response.json();
      })
      .then(json => {
        this.setState({ 
          allPastLaunches: json,
        });
      });

  }
  render() {
    let dataReady = this.state.videoURL;

    if (this.state.launchData != null) {
      return (
        <div className="App">
          {this.state.allPastLaunches ? 
             <Header 
               key="header"
               missionPatch = {this.state.missionPatch}
               allPastLaunches = {this.state.allPastLaunches}
               switchLaunch = {this.switchLaunch}
             />
           :
              <div>Loading...</div>
           }

Child Component (Header)

class Header extends Component {

  componentDidMount() {
  }


  render() {

    var launches = this.props.allPastLaunches;

    var imgClass = classNames({
      'img-container': true,
      'animated': true,
      'fadeInDownBig': true
    });

    const component = this;

    return (
      <div key = "container" className="header-container">
        <div key = "img-container">
            {launches.map((launch, index) => 
                <span key = {index} onClick= {() => { component.props.switchLaunch(index) }} > 
                    {launch["rocket"].rocket_id}
                </span>

            )}

Upvotes: 3

Views: 2077

Answers (1)

mikeb
mikeb

Reputation: 11287

You need to bind your method so it has the correct context, you can read more about it here: https://www.andreasreiterer.at/web-development/bind-callback-function-react/

class App extends Component {
  //api call for latest space launch
  constructor(props) {
    super(props);
  // This line makes sure that "this" is the correct 
  // thing when this method is called via callback
  this.switchLaunch = this.switchLaunch.bind(this);
    this.state = {
      selectedLaunch: "",
      launchData: "",
      videoURL: null,
      allPastLaunches: [],
      showVideo: false
    };
  }

  componentWillMount() {
    this.getLaunchData()
  }

  switchLaunch(launch) {
    debugger
    // ALL MY STATES ARE EMPTY HERE WHEN I TRY TO LOG THEM AFTER THE FUNCTION 
    IS TRIGGERED BY THE CHILD'S ONCLICK
  }
...

Old Answer:

Try this:

const component = this
return (
      <div key = "container" className="header-container">
        <div key = "img-container">
            {launches.map((launch, index) => 
                <span key = {index} onClick= {() => { component.props.switchLaunch(index) }} > 
                    {launch["rocket"].rocket_id}
                </span>

            )}

The code (launch, index) => creates a new function, so this is the context of the new function (i.e. the original this is hidden). You need to save this as component and use it in the inner function.

Upvotes: 3

Related Questions