I am trying to build simple puzzle application for my personal react project.
Basically, I have made an array with objects and map through this array to render puzzle lists.
const puzzleData = [
{ id: Math.random() * 5, value: '<main>', key: 'main' },
{ id: Math.random() * 5, value: '</main>', key: 'main' },
{ id: Math.random() * 5, value: '<section>', key: 'section' },
{ id: Math.random() * 5, value: '</section>', key: 'section' },
{ id: Math.random() * 5, value: '<nav>', key: 'nav' },
{ id: Math.random() * 5, value: '</nav>', key: 'nav' },
{ id: Math.random() * 5, value: '<footer>', key: 'footer' },
{ id: Math.random() * 5, value: '</footer>', key: 'footer' },
{ id: Math.random() * 5, value: '<aside>', key: 'aside' },
{ id: Math.random() * 5, value: '</aside>', key: 'aside' },
{ id: Math.random() * 5, value: '<h1>', key: 'h1' },
{ id: Math.random() * 5, value: '</h1>', key: 'h1' },
{ id: Math.random() * 5, value: '<p>', key: 'p' },
{ id: Math.random() * 5, value: '</p>', key: 'p' },
{ id: Math.random() * 5, value: '<span>', key: 'span' },
{ id: Math.random() * 5, value: '</span>', key: 'span' },
];
const [puzzle, setPuzzle] = useState([...puzzleData]);
<ul className={styles.puzzle}>
{puzzle.map((item, i) => {
return (
<li
key={item.id}
onClick={() => {
filterHandler(item.key, item.id);
}}
style={{ backgroundColor: color.id === item.id ? 'white' : null }}
>
{item.value}
</li>
);
})}
</ul>
I want my filterHandler function applied onClick to filter puzzle array when user clicks same li elements with same key value. For example, when user clicks main and /main in li elements, filter puzzle array to remove those two from the rendered elements. Below is my current filterHandler function.
const filterHandler = (key, id) => {
setPuzzle((prevState) => {
const updated = prevState.filter(
(item) => item.key !== key || item.id !== id
);
return updated;
});
Right now, it is filtering out li element that user clicks but I want to filtering out li element when user clicks two same elements with same key values (like real puzzle game). Is there a way to apply this logic with filter method? Thanks in advance!
You will have to maintain a state variable in order to keep a track of which element the user has clicked before allowing the user to make the second click. In that way you will have the previously clicked element stored with you which you can compare with the user's second element click.
const [lastElementClick, setLastElementClick] = useState("")
Now within your filterHandler()
function, we can at any given time check if the last element clicked matches our current key. Also I removed the second condition inside your filter
condition (this was needed to remove both the elements, otherwise only the last clicked element will be removed)
const filterHandler = (key, id) => {
if(lastElementClick === key) {
setPuzzle((prevState) => {
const updated = prevState.filter(
(item) => item.key !== key
);
return updated;
});
setLastElementClick(key)
} else {
setLastElementClick(key)
}
};
UPDATE: In order to prevent removal of block by clicking the same element twice, we should add another check in our if condition. We are already matching if lastElement matches our current key, now we can add a condition to make sure that lastElement's id
does not match the current id
(i.e, same element is not clicked)
For this reason I'm going to update the state variable to hold both the key and id of the last element clicked
const [lastElementClick, setLastElementClick] = useState({ id: "", key: "" });
After that we can update the if condition by also checking for the id:
const filterHandler = (key, id) => {
if (lastElementClick.key === key && lastElementClick.id !== id) {
const updated = puzzle.filter((item) => item.key !== key);
setPuzzle(updated);
setLastElementClick({ key, id });
} else {
setLastElementClick({ key, id });
}
};
I have also updated the code in the below link, you can try that out too,
Here's a link to the full working code: https://codesandbox.io/s/modest-williams-lun0f?file=/src/App.js:1223-1539