When I try to call the passed function from the child function, I get the following error
Uncaught TypeError: this.props.addHours is not a function
Here's a codepen with the issue: example
Here's the code with my components:
class Application extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.addHours = this.addHours.bind(this)
this.state = {
flies:[{
name: 'Elk Hair Caddis',
hours: 10,
fish: 12
},
{
name: 'Adams',
hours: 6,
fish: 4
}
]
};
}
handleSubmit(event) {
alert('A fly was submitted');
event.preventDefault();
let subName = document.getElementById("subName").value
let subHours = document.getElementById("subHours").value
let subFish = document.getElementById("subFish").value
document.forms[0].reset()
function flyMaker(name, hours, fish) {
this.name = name
this.hours = hours
this.fish = fish
}
let myFly = new flyMaker(subName, subHours, subFish)
let tempState = this.state.flies
tempState.push(myFly)
this.setState(tempState)
}
addHours(e){
e.preventDefault()
alert('hey')
console.log('hey')
}
render() {
return <div>
<h1>Fly List</h1>
<ul>
{this.state.flies.map(function(fly){
return <li><Fly addHours={this.addHours} name={fly.name} hours={fly.hours} fish={fly.fish} /></li>;
})}
</ul>
<div id='addFly'>
<h1>Add a Fly</h1>
<form onSubmit={this.handleSubmit}>
<p>Name:</p>
<input id='subName' type='text'/>
<p>Hours:</p>
<input id='subHours' type='text'/>
<p>Fish Caught:</p>
<input id='subFish' type='text'/>
<br/>
<input type='submit' value='submit' />
</form>
</div>
</div>;
}
}
class Fly extends React.Component {
constructor(props) {
super(props);
this.doAddHours = this.doAddHours.bind(this)
}
doAddHours() {
this.props.addHours()
}
render() {
return <div>
<p>{this.props.name}</p>
<div>Hours fished: {this.props.hours}</div>
<div className='increment' onClick={this.doAddHours}>+</div><div className='increment'>-</div>
<div>Fish Caught: {this.props.fish}</div>
<div className='increment'>+</div><div className='increment'>-</div>
</div>;
}
}
Basically, I'm passing the child component a function so I'm not sure why it doesn't think the prop is one. I'm pretty sure I've bond everything correctly, which was my first guess, but perhaps not. It would be greatly appreciated if someone could point out what I'm doing wrong!
you do not use an arrow function in this.state.flies.map
so it does not have the scope needed to get the context for this
{this.state.flies.map( fly => {
return <li><Fly addHours={this.addHours} name={fly.name} hours={fly.hours} fish={fly.fish} /></li>;
})}
This is happening because you are using this
inside a function() {}
. which means the addHours
function is not in scope for the available this
. Depending on if you are compiling this or not you can do one of the following:
If you are open to using an arrow function:
{
this.state.flies.map(fly => {
return (
<li>
<Fly
addHours={this.addHours}
name={fly.name}
hours={fly.hours}
fish={fly.fish}
/>
</li>;
})
}
if you want to continue using function() {}
:
// At the top of the render function somewhere
var _this = this;
// In your return
{
this.state.flies.map(function(fly) {
return (
<li>
<Fly
addHours={_this.addHours}
name={fly.name}
hours={fly.hours}
fish={fly.fish}
/>
</li>;
})
}
The this
inside your this.state.flies.map
is not the one you are expected. You should defined varibable before calling map to reference the correct this:
render() {
const _this = this;
return <div>
<h1>Fly List</h1>
<ul>
{this.state.flies.map(function(fly){
return <li><Fly addHours={_this.addHours} name={fly.name} hours={fly.hours} fish={fly.fish} /></li>;
})}
</ul>
...
You have 2 main issues here:
The map function is shadowing the this
context. You can extract it to an external method and bind it as well in the constructor
Or use an arrow function so it will use a lexical context for the this
:
this.state.flies.map((fly) => { return <li><Fly addHours={this.addHours} name={fly.name} hours={fly.hours} fish={fly.fish} /></li>; })}
Another issue (error) you will face later is that you are not passing the event ( e
) from the Fly
method to the parent:
doAddHours(e) { this.props.addHours(e) }
class Application extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); //this.addHours = this.addHours.bind(this) this.state = { flies: [{ name: 'Elk Hair Caddis', hours: 10, fish: 12 }, { name: 'Adams', hours: 6, fish: 4 } ] }; } handleSubmit(event) { //alert('A fly was submitted'); event.preventDefault(); let subName = document.getElementById("subName").value let subHours = document.getElementById("subHours").value let subFish = document.getElementById("subFish").value document.forms[0].reset() function flyMaker(name, hours, fish) { this.name = name this.hours = hours this.fish = fish } let myFly = new flyMaker(subName, subHours, subFish) let tempState = this.state.flies tempState.push(myFly) this.setState(tempState) } addHours = (e) => { e.preventDefault() //alert('hey') console.log('hey') } render() { return <div> <h1>Fly List</h1> <ul> {this.state.flies.map((fly) => { return <li><Fly addHours={this.addHours} name={fly.name} hours={fly.hours} fish={fly.fish} /></li>; })} </ul> <div id='addFly'> <h1>Add a Fly</h1> <form onSubmit={this.handleSubmit}> <p>Name:</p> <input id='subName' type='text' /> <p>Hours:</p> <input id='subHours' type='text' /> <p>Fish Caught:</p> <input id='subFish' type='text' /> <br /> <input type='submit' value='submit' /> </form> </div> </div>; } } class Fly extends React.Component { constructor(props) { super(props); this.doAddHours = this.doAddHours.bind(this) } doAddHours(e) { this.props.addHours(e) } render() { return <div> <p>{this.props.name}</p> <div>Hours fished: {this.props.hours}</div> <div className='increment' onClick={this.doAddHours}>+</div><div className='increment'>-</div> <div>Fish Caught: {this.props.fish}</div> <div className='increment'>+</div><div className='increment'>-</div> </div>; } } /* * Render the above component into the div#app */ ReactDOM.render(<Application />, document.getElementById('app'));
html, body { height: 100%; } body { background: #333; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; font-family: Helvetica Neue; } h1 { font-size: 2em; color: #eee; display: inline-block; } a { color: white; } p { margin-top: 1em; text-align: center; color: #aaa; } .increment { display: inline-block; padding: 10px; background-color: black; color: white; margin: 4px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="app"></div>
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.