简体   繁体   中英

Function passed as props not a function

I am trying to pass a function named addtodo as a props from a class. I have passed the function as below

export class Todo extends Component {
    constructor(props) {
        super(props);
        this.state = {todos:[]};

        this.addtodo = this.addtodo.bind(this);
        this.deleteTodo = this.deleteTodo.bind(this);

      }
    componentDidMount(){
axios.get('https://jsonplaceholder.typicode.com/posts').then(res=>{
    this.setState({
       todos:res.data

    })
})
    }
  addtodo(todo){
        todo.id=Math.floor(Math.random()*100)
        todo.completed=true
       const todos=[...this.state.todos,todo]
       this.setState({
         todos:todos
       })
         }
    deleteTodo(id){
        const todo=this.state.todos.filter(todo=>{
         return(
            todo.id!==id
         )
        }
            )
            this.setState({
                todos:todo
            })
    }
    render() {
        return (
            <div>
           <h1 className="center-align"> Todos</h1>
                   <Todolist todos={this.state.todos} deleteTodo={this.deleteTodo}/>
                   <Addtodo addtodo={this.addtodo}/>
            </div>
        )
    }
}

export default Todo

And in Addtodo.js, I am using the function as props as below

 class Addtodo extends Component {
      constructor(props) {
    super(props);
    this.state = { title:'',
    body:''
};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);

  }

handleChange(e){
    this.setState({
        [e.target.id]:[e.target.value]
    })
    }

    handleSubmit(e){
        e.preventDefault()
        this.props.addtodo(this.state)
        this.setState({
            title:'',
            body:''
        })
        }


    render() {
        return (
            <form className="col s12" onSubmit={this.handleSubmit}>
            <div className="row">
              <div className="input-field col s6">
                <input placeholder="Add title" id="title" type="text" className="validate" onChange={this.handleChange}/>
              </div>
              <div className="input-field col s6">
                <input placeholder="Add body" id="body" type="text" className="validate" onChange={this.handleChange}/>
              </div>
              <button type="submit">Submit</button>
              </div>
              </form>
        )
    }
}

export default Addtodo

But I am getting the error Uncaught TypeError: this.props.addtodo is not a function . I checked for spelling error as well but could not get why this is not working. Whereas if I pass it as a props to another class and simply log a hello message it is working.

I successfully run your code on the stackblitz, and there are no errors

and i found another bug,

before

handleChange(e) {
    this.setState({
      [e.target.id]: [e.target.value]
    });
  }

after

handleChange(e) {
    this.setState({
      [e.target.id]: e.target.value
    });
  }

check stackblitz demo

tip: Because i don't have enough points, so i cannot add comments, can only editor answer

update:

before:

function App() {
  return (
    <BrowserRouter>
      <div className="container">
        <Addtodo />
        <Switch>
          <Route exact path="/" component={Todo} />
          <Route path="/:id" component={Task} />
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;

function App() {
  return (
    <BrowserRouter>
      <div className="container">
        <Switch>
          <Route exact path="/" component={Todo} />
        </Switch>
      </div>
    </BrowserRouter>
  );
}

render(<App/>, document.getElementById('root'))

Please check the code is not more than one Addtodo?

You're not registering the function addtodo in the class, thus this.addtodo is not bound to your function addtodo but instead to null. Hence your error.

See this example from React docs - Handling events :

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Notice on line 6 the function is first bound to the class, then it can be defined and called through this.<function_name> .

In your code you're missing in the constructor:

this.addtodo = this.addtodo.bind(this);

Until so, you cannot call this.addtodo , for it's not bound to this , thus your error that this.props.addtodo is not a function.

You're missing binding in your constructor I guess. In your constructor, you need to declare it like this

this.addtodo = this.addtodo.bind(this);

Edited **

I have tested the code and seems like there was no error,

Todo.js

import React, { Component } from "react";
import axios from "axios";

import Addtodo from "./Addtodo";

export class Todo extends Component {
  state = {
    todos: [],
  };
  componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then((res) => {
      this.setState({
        todos: res.data,
      });
    });
  }
  addtodo = (todo) => {
    todo.id = Math.floor(Math.random() * 100);
    todo.completed = true;
    const todos = [...this.state.todos, todo];
    this.setState({
      todos: todos,
    });
  };

  render() {
    console.log(this.state.todos);
    return (
      <div>
        <h1 className="center-align"> Todos</h1>
        <Addtodo addtodo={this.addtodo} />
      </div>
    );
  }
}

export default Todo;

Addtodo.js

import React, { Component } from "react";
class Addtodo extends Component {
  constructor(props) {
    super(props);
    this.state = { title: "", body: "" };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e) {
    this.setState({
      [e.target.id]: [e.target.value],
    });
  }

  handleSubmit(e) {
    e.preventDefault();
    this.props.addtodo(this.state);
    this.setState({
      title: "",
      body: "",
    });
  }

  render() {
    return (
      <form className="col s12" onSubmit={this.handleSubmit}>
        <div className="row">
          <div className="input-field col s6">
            <input
              placeholder="Add title"
              id="title"
              type="text"
              className="validate"
              onChange={this.handleChange}
            />
          </div>
          <div className="input-field col s6">
            <input
              placeholder="Add body"
              id="body"
              type="text"
              className="validate"
              onChange={this.handleChange}
            />
          </div>
          <button type="submit">Submit</button>
        </div>
      </form>
    );
  }
}

export default Addtodo;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM