Reputation: 3
I want to make a simple memory game with react for a neat portfolio project. I have an array of 20 items and 10 sets of colors
const data = [
// Manchester Blue
{
ID: 1,
Color: "#1C2C5B"
},
{
ID: 2,
Color: "#1C2C5B"
},
// Seafoam Green
{
ID: 3,
Color: "#93E9BE."
},
{
ID: 4,
Color: "#93E9BE."
},
// Candy Apple Red
{
ID: 5,
Color: "#ff0800"
},
{
ID: 6,
Color: "#ff0800"
},
// Lake Placid Blue
{
ID: 7,
Color: "#3f5c9"
},
{
ID: 8,
Color: "#3f5c9"
},
// Safety Orange
{
ID: 9,
Color: "#ff6700"
},
{
ID: 10,
Color: "#ff6700"
},
// Vivid Orchard
{
ID: 11,
Color: "#cc00ff"
},
{
ID: 12,
Color: "#cc00ff"
},
// Apple Green
{
ID: 13,
Color: "#8db600"
},
{
ID: 14,
Color: "#8db600"
},
// Old Moss Green
{
ID: 15,
Color: "#867e36"
},
{
ID: 16,
Color: "#867e36"
},
// Canary Yellow
{
ID: 17,
Color: "#ffef00"
},
{
ID: 18,
Color: "#ffef00"
},
// Pink
{
ID: 19,
Color: "#ff1493"
},
{
ID: 20,
Color: "#ff1493"
}
];
<div>
<div className='flexbox-container'>
<div id='1' className='flexbox-item-one'></div>
<div id='2' className='flexbox-item-one'></div>
<div id='3' className='flexbox-item-one'></div>
<div id='4' className='flexbox-item-one'></div>
<div id='5' className='flexbox-item-one'></div>
</div>
<div className='flexbox-container'>
<div id='6' className='flexbox-item-two'></div>
<div id='7' className='flexbox-item-two'></div>
<div id='8' className='flexbox-item-two'></div>
<div id='9' className='flexbox-item-two'></div>
<div id='10' className='flexbox-item-two'></div>
</div>
<div className='flexbox-container'>
<div id='11' className='flexbox-item-three'></div>
<div id='12' className='flexbox-item-three'></div>
<div id='13' className='flexbox-item-three'></div>
<div id='14' className='flexbox-item-three'></div>
<div id='15' className='flexbox-item-three'></div>
</div>
<div className='flexbox-container'>
<div id='16' className='flexbox-item-four'></div>
<div id='17' className='flexbox-item-four'></div>
<div id='18' className='flexbox-item-four'></div>
<div id='19' className='flexbox-item-four'></div>
<div id='20' className='flexbox-item-four'></div>
</div>
</div>
.flexbox-item-one {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: darkcyan;
margin: 1.5rem;
}
.flexbox-item-two {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: greenyellow;
margin: 1.5rem;
}
.flexbox-item-three {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: slateblue;
margin: 1.5rem;
}
.flexbox-item-four {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: magenta;
margin: 1.5rem;
}
.flexbox-container {
display: flex;
justify-content: center;
}
I also have done some research and procured the following way to shuffle the array.
let shuffledColors = colors
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value)
So my React skills are just not to the level where I can build this. I want to have an useEffect hook that loads the array when the project is rendered and uses the shuffle method. It would then have to dynamically add the colors to the HTML elements. SOmething which I'm not entirely sure how to do as well. Maybe an ForEach statement that uses the document.GetElementByID to add the css dynamically.
I would then have to get a way with positions to have two divs on top of each other which I can have the top one dynamically have its opacity altered on a onClick event to show the 'real colors'
Then there would have to be a check if the ID's of the colors match and a counter which keeps track of how many matches have been made.
For now I just want to get my head around how to have this array of colors randomised and assigned to the divs when loaded with the useEffect hook.
Upvotes: 0
Views: 74
Reputation: 2675
Ok, let's do it.
First, create a FlexboxContainer component that return a div with flexbox-container
className:
const FlexboxContainer = ({ children }) => {
return <div className="flexbox-container">{children}</div>;
};
Next, create a FlexboxItem component, where the show/hide logic resides. Here, we don't need a stack of two divs. Instead, we use conditional backgroundColor styling that depends on actualColor state on a div. Here, we do have a onClick handler that would set the actualColor by using setActualColor setter once the user clicks the color card div. The show/hide (toggle) action would be based on the previous state of actualColor.
const FlexboxItem = ({ id, color, group }) => {
const [actualColor, setActualColor] = useState();
const handleClick = () => {
setActualColor((prevState) => (!prevState ? color : undefined));
};
return (
<div
id={id}
onClick={handleClick}
className={`flexbox-item-${group}`}
style={actualColor ? { backgroundColor: color } : null}
></div>
);
};
At the main component, we need to define currentColors state where we put our shuffledColors.
const [currentColors, setCurrentColors] = useState();
and we use useCallback hooks on shuffledColors
const shuffledColors = useCallback(() => {
return data
.map((value) => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
}, []);
since the shuffledColors going to be used inside a memoized groups constant where we re-structure the shuffledColors into groups of colors. The aim of this grouping is to provide an array that is going to be used to render a dynamics group of five color divs by using the iteration of FlexboxContainer component.
const groups = useMemo(() => {
if (!currentColors) return [];
const chunkSize = 5;
const groupedItems = [];
for (let i = 0; i < currentColors.length; i += chunkSize) {
const chunk = currentColors.slice(i, i + chunkSize);
groupedItems.push(chunk);
}
return groupedItems;
}, [currentColors]);
...
return (
...
<div>
{currentColors &&
groups.map((items, index) => {
return (
<FlexboxContainer key={index}>
{items.map((item) => {
return (
<FlexboxItem
id={item.id}
color={item.color}
group={groupColors[index]}
key={item.id}
/>
);
})}
</FlexboxContainer>
);
})}
</div>
...
)
}
Next, we define a shuffleColors method, by which we can use it to set the initial colors inside a useEffect hook.
const shuffleColors = useCallback(() => {
const colors = shuffledColors();
setCurrentColors(colors);
}, [shuffledColors]);
useEffect(() => {
shuffleColors();
}, [shuffleColors]);
If we want to re-shuffle the colors, just call it from onCLick handler. Here we do a minor modification at FlexboxItem component and add a currentColor watcher.
const FlexboxItem = ({ id, color, group, show }) => {
const [actualColor, setActualColor] = useState();
// add show props change watcher
useEffect(() => {
setActualColor(show ? color : undefined);
}, [color, show]);
...
}
...
// current color watcher will reset showAll state on any currentColors state change
useEffect(() => {
setShowAll(false);
}, [currentColors]);
return (
...
<button onClick={shuffleColors}>Re-shuffle Colors</button>
...
)
It does not fully answer your question, at least, will give you new insight into how React works to achieve your objectives.
Here is the complete code of my explanation:
Upvotes: 1