ta539tg70
ta539tg70

Reputation: 317

Getting "Uncaught TypeError" in a React app

I am new to React and I'm trying to make a "delete button" on my Twitter-ish app for my practice. It should look just like the real Twitter, there's a list of all of my tweets and each tweet has a delete button. I'm successfully showing all of my tweets, but I'm currently getting an error around my delete button saying Uncaught TypeError: this.props.onDelete is not a function and I have no idea how to fix it. How can I make it to work?

My codes are as follows:

Tweet.js

import React from 'react'

class Tweet extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tweet: this.props.tweet,
      id: this.props.id,
    };
    this.handleDelete = this.handleDelete.bind(this);
  }
  handleDelete(id) {
    this.props.onDelete(id);
  }
  render() {
    const tweet = this.state.tweet;
    return (
      <div>
        <p>{tweet.user.user_name} {tweet.created_at}</p>
        <p>{tweet.tweet}</p>
        <button onClick={this.handleDelete(tweet.id)}>DELETE</button>
      </div>
    );
  }
}

export default Tweet;

Tweets.js

import React from 'react'
import Tweet from './Tweet'

class Tweets extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tweet: this.props.tweet,
      id: this.props.id,
    };
  }
  render() {
    const tweets = this.props.tweets.map(function (tweet) {
      return <Tweet tweet={tweet} key={tweet.id} />;
    });
    return (
      <div>
        {tweets}
      </div>
    );
  }
}

export default Tweets;

TweetsPage.js

import React from 'react'
import Tweets from './Tweets'

class TweetsPage extends React.Component {
  constructor() {
    super();
    this.state = {
      tweets: [],
      id: '',
    };
    this.onSubmitDelete = this.onSubmitDelete.bind(this);
  }
  loadTweetsFromServer() {
    const url = '/tweets/index.json';
    $.ajax({
      url: url,
      dataType: 'json',
      type: 'GET',
      cache: false,
      success: (data) => {
        this.setState({
          tweets: data
        });
      },
      error: (xhr, status, err) => {
        console.error(url, status, err.toString());
      },
    });
  }
  onSubmitDelete(id) {
    const url = '/tweets/destroy';
    $.ajax({
      url: url,
      type: 'POST',
      cache: false,
      data: {
        id: id
      },
      success: (data) => {
        this.loadTweetsFromServer();
      },
      error: (xhr, status, err) => {
        console.error(url, status, err.toString());
      },
    });
  }
  componentDidMount() {
    this.loadTweetsFromServer();
  }
  render() {
    return (
      <div>
        <Tweets tweets={this.state.tweets} onDelete={this.onSubmitDelete} />
      </div>
    );
  }
}

export default TweetsPage;

Upvotes: 2

Views: 1147

Answers (2)

Dinesh Pandiyan
Dinesh Pandiyan

Reputation: 6299

You need to pass the handler to your child. Rest of your code is perfect.

TweetsPage.js - No change in this file

import React from 'react'
import Tweets from './Tweets'

class TweetsPage extends React.Component {
  constructor() {
    super();
    this.state = {
      tweets: [],
      id: '',
    };
    this.onSubmitDelete = this.onSubmitDelete.bind(this);
  }
  loadTweetsFromServer() {
    // load and set state
  }
  onSubmitDelete(id) {
    // submit delete req to server
  }
  componentDidMount() {
    this.loadTweetsFromServer();
  }
  render() {
    return (
      <div>
        <Tweets tweets={this.state.tweets} onDelete={this.onSubmitDelete} />
      </div>
    );
  }
}

export default TweetsPage;

Tweets.js - Note the changes

import React from 'react'
import Tweet from './Tweet'

class Tweets extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tweet: this.props.tweet,
      id: this.props.id,
    };
  }
  render() {
    let { onDelete, tweets } = this.props; // note this line
    // note the next line - arrow function and different variable name
    const renderedTweets = tweets.map((tweet) => {
      // note the next line
      return <Tweet tweet={tweet} key={tweet.id} onDelete={onDelete} />; 
    });
    return (
      <div>
        {renderedTweets}
      </div>
    );
  }
}

export default Tweets;

Tweet.js - No change in this file

import React from 'react'

class Tweet extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tweet: this.props.tweet,
      id: this.props.id,
    };
    this.handleDelete = this.handleDelete.bind(this);
  }
  handleDelete(id) {
    this.props.onDelete(id);
  }
  render() {
    const tweet = this.state.tweet;
    return (
      <div>
        <p>{tweet.user.user_name} {tweet.created_at}</p>
        <p>{tweet.tweet}</p>
        <button onClick={this.handleDelete(tweet.id)}>DELETE</button>
      </div>
    );
  }
}

export default Tweet;

Upvotes: 2

Leo Farmer
Leo Farmer

Reputation: 7910

You pass the onDelete function as a prop to your Tweets component but not to your Tweet component. Your Tweet component tries to call it in the handleDelete function. To fix this, pass the prop onto the Tweet component.

render() {
    let that = this
    const tweets = this.props.tweets.map(function (tweet) {
      return <Tweet tweet={tweet} key={tweet.id}  onDelete={that.props.onDelete} />;
    });
    return (
      <div>
        {tweets}
      </div>
    );
  }

Upvotes: 3

Related Questions