Reputation: 471
I'm just starting out with React, adapting the tic tac toe tutorial for my case. I'm trying to click on the grandchild component to change the state of the grandparent component . Code is as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: [
{
id: 1,
show: false
},
{
id: 2,
show: false
}
]
}
}
handleClick(i) {
const fields = this.state.fields.slice();
fields[i].show = true;
this.setState({fields: fields});
}
render() {return <Preview />}
}
const Preview = (props) => {
return (
<div className="preview">
{props.fields.map((field) => (
<Field data={field} key={field.id} onClick={ props.onClick(field.id) }/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};
I get a TypeError: Cannot read property 'state' of undefined from this line:
handleClick(i) {
const fields = this.state.fields.slice();
Upvotes: 2
Views: 1202
Reputation: 202618
this
of the App
class isn't bound to the handleClick
function. This is cause of TypeError: Cannot read property 'state' of undefined
error.fields[i].show = true;
mutates the object reference in state.fields
or onClick
props to Preview
.onClick
callback isn't called correctly in Preview
.Bind this
to the handler or convert to arrow function so it is automatically bound.
constructor(props){
...
this.handleClick = this.handleClick.bind(this);
}
or
handleClick = (i) => { ..... };
DON'T MUTATE STATE. Shallow copy state then update properties.
handleClick = (id) => {
this.setState(prevState => ({
fields: prevState.fields.map((field) => {
return field.id === id ? {
...field,
show: true,
} : field;
}),
}));
};
Pass fields
and handleClick
as onClick
to Preview
.
render() {
return (
<Preview
fields={this.state.fields}
onClick={this.handleClick}
/>
);
}
Call props.onClick
correctly with the id.
{props.fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => props.onClick(field.id)}
/>
))}
Upvotes: 2
Reputation: 4050
I've added some explanations, check the comments
// [...]
render() {
// Here you need to pass "fields" and "handleClick" as props:
return <Preview fields={this.state.fields} onClickField={this.handleClick} />
}
}
const Preview = (props) => {
// Here you get the props:
const { fields, onClickField } = props;
// Your onclick was a function call instead of just a function
return (
<div className="preview">
{fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => onClickField(field.id) }
/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};
Upvotes: 0