Reputation: 250
TLDR- I want to change the backgroundColor of 1 square onClick. I am not sure how to differentiate this 1 square from the other 63 since they are created dynamically
I am dynamically creating a Board composed of 64 squares and I want a square's background color to change if selected under the proper conditions (preferably under the selectPiece function) and then revert back after a move is made or the piece on the square is unselected. My problem is identifying the dynamically created square to change its state.
Board.js
let [squareStyle] = useState('squares ');
function Row(i){
const newRow = [];
let count = i * 8;
let squareImp;
for (let j = 0; j<8; j++){
if ((i + j) % 2 === 1){
squareImp = squareStyle + "g";
}else {
squareImp = squareStyle + "y";
}
newRow.push(<div key={count} id={i * 10 + j} className={squareImp} onClick={isMove ? selectMove : props.data[count].name !== null ? selectPiece : undefined }>
{ (props.data[count].name != null) &&
<img src={images[props.data[count].name]}
className="icons"
alt="chess piece" />
}
</div>)
count++;
}
return <div className="rows" key={i}>{newRow}</div>;
}
If I was using class based Components, then I could use this.setState to change the individual square, using something like -
this.setState({className: "squares b"})
but with Function Components, the setState is unique to each property. So while I can use the id (currently not used for CSS) to grab the selected square, I can't create a [state, setState] variable for each square. Is it possible to dynamically create a variable? aka something like
function setBackground(int id){
let StateChange = id + ""
setStateChange({backgroundColor:"blue"})
It doesn't seem to be.
So after I have spent quite a bit of time reading the multitudes of posts on here about how to dynamically change the CSS in React I tried following the only article that I thought might apply to me here- How to add a CSS class to an element on click - React and set the className with a function like so -
Board.js
function Row(i){
const newRow = [];
let count = i * 8;
for (let j = 0; j<8; j++){
newRow.push(<div key={count} id={i * 10 + j} className={getClassName(i, j)} onClick={isMove ? selectMove : props.data[count].name !== null ? selectPiece : undefined }>
{ (props.data[count].name != null) &&
<img src={images[props.data[count].name]}
className="icons"
alt="chess piece" />
}
</div>)
count++;
}
return <div className="rows" key={i}>{newRow}</div>;
}
//function getClassName(e, i, j){ //can't read e
function getClassName(i, j){
console.log(i, j)
//console.log(e.currentTarget)
let base = "squares ";
if (clicked){
// if (clicked && e.currentTarget.id == i * 10 + j){ //this is what I want but I can't figure out how to grab the target to compared the id to
return base + "b";
} else {
if ((i + j) % 2 === 1){
return base + "g";
}else {
return base + "y";
}
}
}
but as you can probably tell from the commented out text, I can't figure out how to grab the event and thus use the id to compare so that I am only changing the background color of the selected square rather than the whole board.
I don't understand the nuances of event handling but I am guessing that because I am calling the function from within the className attribute of the div element rather than from the element itself, that the event just isn't available. I am not sure how to get around it.
Any help would be appreciated
Upvotes: 2
Views: 1183
Reputation: 973
So this would be how I would do it. I abstracted Square to be its own component and it is being create via your for loop. Each of these Squares can manage their own local state so when they are clicked they can update their own state and you dont need to have messy centralized code.
FYI you seem to be using useState
incorrectly as it does have its own function to update its local state. The below code is not 100% correct as Im not sure what you are doing, but it should give you a general outline.
I would also review the React docs: https://reactjs.org/docs/hooks-state.html
Just so you can get clear about the useState
hook and how it can be utilized.
const { useState } from 'react'
const Square = (i, j, data, count, isMove, selectMove) => {
const [ squareStyle, setSquareStyle ] = useState('square') // then use setSquareStyle('STYLING) to update local state
const selectedPiece = data[count].name
return (
<div key={count}
id={i * 10 + j}
className={getClassName(i, j)}
onClick={isMove ? selectMove : selectedPiece ? selectPiece : undefined }>
{ (selectedPiece.name != null) &&
<img src={images[props.data[count].name]}
className="icons"
alt="chess piece" />
}
</div>
)
}
function Row(i){
const newRow = [];
let count = i * 8;
for (let j = 0; j<8; j++){
newRow.push(
<Square
i={i}
j={j}
count={count}
isMove={isMove}
selectMove={selectMove}
/>
)
count++;
}
return <div className="rows" key={i}>{newRow}</div>;
}
//function getClassName(e, i, j){ //can't read e
function getClassName(i, j){
console.log(i, j)
//console.log(e.currentTarget)
let base = "squares ";
if (clicked){
// if (clicked && e.currentTarget.id == i * 10 + j){ //this is what I want but I can't figure out how to grab the target to compared the id to
return base + "b";
} else {
if ((i + j) % 2 === 1){
return base + "g";
}else {
return base + "y";
}
}
}
Upvotes: 2