I am making a basic app in which images are rendered to the screen. The objective is to not click the same image more than once. I'm using parent state in App comp to count the score (number of first time clicks) and local component state in Image component to see if the image has been clicked already. App Component is such:
class App extends React.Component {
state = { score: 0 };
array = [1, 2, 3]
handleScoreIncrement = (e) => {
this.setState({ score: this.state.score + 1 })
console.log(this.state.score)
}
handleRestart = () => {
this.setState({ score: 0 })
}
render() {
return (
<div>
<Header
score={this.state.score} />
{this.array.map((cv, i, arr) => {
return (
<div key={cv}>
<Image
score={this.state.score}
increaseScore={this.handleScoreIncrement}
restart={this.handleRestart}
/>
</div>
)
})
}
</div>
)
}
};
export default App;
I passed the callbacks in the App component as props to the image component to change the state as you can see below. Just stay with me. Image Component:
class Image extends React.Component {
state = {
clickedAlready: false,
score: this.props.score
}
handleRestart = () => {
this.props.restart();
if (this.props.score === 0) {
this.setState({ clickedAlready: false })
}
}
handleClickEvent = (e) => {
if (this.state.clickedAlready) {
alert('Clicked Already')
this.handleRestart()
this.setState({ clickedAlready: false })
} else {
this.props.increaseScore();
this.setState({ clickedAlready: true })
}
console.log(this.state.score)
}
render() {
return (
<div
style={{ border: '1px solid red', marginTop: 5 }}
onClick={this.handleClickEvent}>Image Would Go Here
</div>
)
}
}
export default Image;
NOW FOR THE PROBLEM: The problem I have is that each time I click an image, it increments correctly, but once I've pressed the same image twice, to reset the game and start over, only THAT specific image changes it's state back to {clickedAlready: false} (so as to restart the game) while the rest still say they have been clicked. It is impossible for me to restart the game if, on reset, all components other than the double-clicked one retain their previous state. I expected that my event handler in my Image component would reset the state of both the Image comp and App component, thus re-rendering both components, but maybe I am missing something. Please HELP
Essentially, I want, when one Image component has been double-clicked, for all Image components to re-set their state to {clickedAlready: false}.
Main things to update:
Image
Use componentDidUpdate
to reset the clickedAlready
state when the score
prop value is updated to 0
. This allows all Image
components to "reset" when the score
state is reset in the parent component.
componentDidUpdate(prevProps) {
if (prevProps.score !== this.props.score && this.props.score === 0) {
this.setState({ clickedAlready: false });
}
}
Update handleClickEvent
to check the clickedAlready
state to either callback to reset the game or bump the score and update the clicked status.
handleClickEvent = (e) => {
const { clickedAlready } = this.state;
const { increaseScore, restart } = this.props;
if (clickedAlready) {
alert("Clicked Already");
restart();
} else {
increaseScore();
this.setState({ clickedAlready: true });
}
};
Full code in sandbox demo
const Header = ({ score }) => <h1>Score: {score}</h1>;
class Image extends React.Component {
state = {
clickedAlready: false
};
componentDidUpdate(prevProps) {
if (prevProps.score !== this.props.score && this.props.score === 0) {
this.setState({ clickedAlready: false });
}
}
handleClickEvent = (e) => {
const { clickedAlready } = this.state;
const { increaseScore, restart } = this.props;
if (clickedAlready) {
alert("Clicked Already");
restart();
} else {
increaseScore();
this.setState({ clickedAlready: true });
}
};
render() {
const { clickedAlready } = this.state;
return (
<div
style={{ border: "1px solid red", marginTop: 5 }}
onClick={this.handleClickEvent}
>
Image Would Go Here {clickedAlready && " - clicked"}
</div>
);
}
}
class App extends React.Component {
state = { score: 0 };
array = [1, 2, 3];
componentDidUpdate() {
const { score } = this.state;
console.log(score);
if (score === this.array.length) {
alert("Won");
this.handleRestart();
}
}
handleScoreIncrement = (e) => {
this.setState({ score: this.state.score + 1 });
};
handleRestart = () => {
this.setState({ score: 0 });
};
render() {
return (
<div>
<Header score={this.state.score} />
{this.array.map((cv, i, arr) => {
return (
<div key={cv}>
<Image
score={this.state.score}
increaseScore={this.handleScoreIncrement}
restart={this.handleRestart}
/>
</div>
);
})}
</div>
);
}
}
export default App;