Coding has been a hobby of mine, since I started university. That means I always have a number of hobby projects I spend my free time on. Recently, a friend came with a new project idea: making a website full of classic games, like minesweeper, memory games, and more. The idea was to have a clean and simple UI, which most of the sites out there are missing.
I decided to create a solitaire game, and started with Klondike. The project uses Next.js so I started writing the thing in React, and it got complicated very fast. First I had to use an external package for drag and drop, and using that required some forwardRef, which I am unfamiliar with. Then I had the problem that my callback functions always had previous states and not the most current ones, so I needed to use useCallback, again a thing I don’t understand that well (okay maybe my problem is my React knowledge).
This is when decided to try and go to vanilla js. So I created a file called solitaire.ts, that exports a function:
export const createGame = () => {
const gameDiv = document.createElement('div');
// create piles, cards and more...
}
Then, in my React component, we call this function, that uses good old vanilla JS (or TS). This is where I realized how good simple HTML and JS really are. There is an attribute draggable! so no need for the external library (this may not be news to everyone). So here is code that basically does everything I needed the library for:
cardContainerDiv.draggable = card.isDraggable;
cardContainerDiv.addEventListener('drop', (e) => {
// dropping logic...
}
Free Cell
Now after creating this game I wanted to do Free cell. But I also wanted to have most of the game logic be reusable, this includes saving the game state, undo functionallity, dragging and dropping and all of that. The games really only need their own win condititon and if a move is valid.
So after each move, I call a function, saveState, this functions gets all piles by name and saves them to localStorage, (so if you refresh you don’t lose your progress). To have this function work on both games I simply get the state from the current HTML! So I use the HTML as state management.
export const getPileFromId = (pileId: string): Pile => {
const pile = document.getElementById(pileId);
const cards = [];
for (const item of pile.children) {
if (item.classList.contains('card')) {
cards.push(cardFromEl(item));
}
}
return cards;
};
Is this the best and most optimized way to hold state? no ofcourse not. There could also be a parent class that Klondike and FreeCell classes inherit. But these ways don’t use HTML as state management 🤓.
So my conclusion is: sometimes simple is better and sometimes stupid works.
Checkout classicgames.games to play some awesome games! Thank you for reading.
Top comments (1)
wtf