How do I change a variable inside a module pattern? - javascript

I’m stuck on writing this module pattern for a tic tac toe game. When I click on a tile, it doesn’t increment gameBoard.tileCounter
const gameBoard = (() => {
const tiles = document.querySelectorAll('.game-board > div')
let tileCounter = 0;
tiles.forEach(tile => tile.addEventListener('click', () => {
tileCounter++;
}))
return {tileCounter};
})();
If I do gameBoard.tileCounter++ it works, but shouldn’t I just be able to do tileCounter++

The idea of a module pattern is to access to the properties of that module instead of having a lot variables dangling, so in your scenario you can return a function to get the counter.
Something like this might work:
const gameBoard = (() => {
const tiles = document.querySelectorAll(".game-board > div");
let tileCounter = 0;
tiles.forEach((tile) =>
tile.addEventListener("click", () => {
tileCounter++;
})
);
const getTileCount = () => tileCounter
return { getTileCount };
})();
And by doing gameBoard.getTileCount() you will be able to get the actual count

Related

React 'Cannot set properties to undefined': expensive computation?

I have an issue while rendering the following.
I have a useBoard hook, which should create a new board: a 2d Array, which every cell is an object, in which some of these object have a value between 1-3 included, and the other 'empty cells' have the 0 value.
Cell object:
{
value: 0,
className: ''
}
My useBoard simply call the function to create the board.
export const useBoard = ({ rows, columns, pieces }: IBoardItems) => {
const [board, setBoard] = useState<ICell[][]>();
useEffect(() => {
const getBoard = () => {
const newBoard = createBoard({ rows, columns, pieces });
setBoard(newBoard);
}
getBoard();
}, [])
return [board] as const;
};
And here is the utility function to build it.
export const createBoard = ({ rows, columns, pieces }: IBoardItems) => {
let randomIndexes: number[][] = [];
while (randomIndexes.length < pieces) {
let randomIndex = [getRandomNumber(5, rows), getRandomNumber(0, columns)];
if (randomIndexes.includes(randomIndex)) {
continue;
} else {
randomIndexes.push(randomIndex);
};
};
let board: ICell[][] = [];
for (let i = 0; i < rows; i++) {
board.push(Array(columns).fill(defaultCell));
};
randomIndexes.forEach(([y, x]) => {
board[y][x] = {
value: getRandomNumber(1, 3),
className: ''
}
});
return board;
};
The fact is, that when I click start in my related component, which should render the <Game /> containing the <Board />, sometimes it works, sometimes not, and the console logs the error 'Cannot set properties to undefined'. I think I understand but I'm not sure: is it because could happen, that in some steps, some data is not ready to work with other data?
So, in this such of cases, which would be the best approach? Callbacks? Promises? Async/await?
NB. Before, I splitted the createBoard function into smaller functions, each one with its owns work, but to understand the issue I tried to make only one big, but actually I would like to re-split it.
EDIT: I maybe found the issue. In the createBoard I used getRandomNumber which actualy goes over the array length. Now the problem no longer occurs, but anyway, my question are not answered.

what's the difference in using closure and private class in javascript

const BUTTON = document.querySelector('.clickBtn');
const P = document.querySelector('.click');
const click = (() => {
let click = 0;
return () => ++click;
})();
BUTTON.onclick = () => {
P.textContent = click();
};
const BUTTON2 = document.querySelector('.clickBtn2');
const P2 = document.querySelector('.click2');
class Click {
#click = 0;
click = () => ++this.#click;
}
const c = new Click();
BUTTON2.onclick = () => {
P2.textContent = c.click()
};
<button class="clickBtn">CLOSURE</button>
<p class="click"></p>
<button class="clickBtn2">CLASS</button>
<p class="click2"></p>
say I have a code like above, trying to manage real-time state in vanilla JS and also wanting to hide the actual data from exposing.
I don't need multiple method
nor do I need to copy multiple instances
in case like this, would there be any difference between closure and class except class being slightly more verbose?

Move first item to last position of an array with setInterval() in React

I am trying to move the first item of an array to the last position of the array in React, using a setInterval function:
const [cardOrder, setCardOrder] = useState([card1, card2, card3, card4]);
setInterval(() => {
setCardOrder(cards.push(cards.shift()))
}, 3000);
The first time always works, but the second time I get
TypeError: cardsOrder.shift is not a function
How can I achieve this result? All help appreciated. Thanks.
Few issues with your code, e.g. cardsOrder is not defined. Also you are missing some essential stuff, like state management and lifecycle aka hooks. Also not sure why do you need the i variable. Anyways, heres a possible, alternative solution:
const { useEffect, useState } = React;
const App = () => {
const [arr, setFn] = useState(['first', 'second', 'third', 'fourth']);
useEffect(() => {
const i = setInterval(() => {
setFn(([first, ...rest]) => [...rest, first]);
}, 1000);
return () => clearInterval(i);
}, []);
return arr.join('\n');
}
ReactDOM.render(<App />, document.getElementById("root"));
body {
white-space: pre;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
You can implement swap as Array prototype
Array.prototype.swap = function (x, y) {
var z = this[x];
this[x] = this[y];
this[y] = z;
return this;
}
and then use it
const array= [{a:'a',b:'b'},{c:'c',d:'d'}];
const newArray = array.swap(0,1)
newArray if what are you looking for. Use insted .swap(0,3)
It's because, [].push() returns length of the array. So when you do setOrder(cards.push(cards.shift(i))) cards will be changed (Array) to Number(length of the Array), next time when you call this will become (number)1.shift which is not function. That's why you're getting that error.
Change your code to:
setInterval(() => {
cards.push(cards.shift())
setOrder([...cards]) // this will work.
// OR
const card = cards.shift()
setOrder([...cards, card])
}, 3000)

Should I add a quick return before forEach?

Is there any benefit to checking if an array has length before using forEach?
Consider the following:
const foo = () => {
const elements = [...document.querySelectorAll(".selector")];
elements.forEach(element => {
element.style.height = `${element._someProperty}px`;
});
};
This is what I have in my project. In some cases the elements array will be empty because the existence of such elements is based on user-input.
My question is:
Should I add something like
if (!elements.length) return;
before calling the forEach method like so
const foo = () => {
const elements = [...document.querySelectorAll(".selector")];
if (!elements.length) return;
elements.forEach(element => {
element.style.height = `${element._someProperty}px`;
});
};
Or would that be considered a micro-optimization?

Implement unregister after registration of callback

I wrote a simple piece of software that allows users to "register" a function when a state is set.
This was easily achieved by adding functions to an array.
I want to return a function that is able to "unregister" that particular function.
Note that a user might register the same function twice. This means that the "unregistering" function cannot be based on the function as a key in a map
The only thing that springs to mind is making the "register" function way more complex, where each item in the "callbacks" array is not just a function, but an object like this:
{
id: someId
fn: [the function]
}
And that the unregister function will filter the someId value. But I just can't like this.
Ideas?
const state = {}
const callbacks = []
const register = (fn) => {
callbacks.push(fn)
return () => {
console.log('Unregister function. HELP!!! How do I do this?')
}
}
const setState = async (key, value) => {
state[key] = value
for (const fn of callbacks) fn(key, value)
}
const getState = (key) => {
return state[key]
}
const f1 = () => {
console.log('f1')
}
const f2 = () => {
console.log('f2')
}
const unregF1a = register(f1)
const unrefF1b = register(f1)
const unregF2 = register(f2)
setState('some', 'a')
unregF1a()
setState('some', 'b')
Loop through your callbacks and remove the desired function (works if the same function is registered twice).
You could do a simple for loop:
function unregister(fn) {
for (let i = callbacks.length - 1; i >= 0; i--) {
if (callbacks[i] === fn) {
callbacks.splice(i, 1)
}
}
}
Or you can use let and replace the whole array:
let callbacks = [];
function unregister(fn) {
callbacks = callbacks.filter(cb => cb !== fn)
}
If you want to be able to register the same function more than once and be able to unregister them independently, then yes, you'll need to track some kind of id.
An id can be something simple, like an increasing integer, and you can store them in a different array, in the same index the function is in the callbacks array (that's hashing).
Something like this:
const state = {}
const callbacks = []
const ids = []
let nextId = 0
const register = (fn) => {
const id = nextId
callbacks.push(fn)
ids.push(nextId)
nextId++
return () => {
// find the function position using the ids array:
const fnIndex = ids.findIndex(cbId => cbId === id)
if (fnIndex === -1) return // or throw something
// Now remove the element from both arrays:
callbacks.splice(fnIndex, 1)
ids.splice(fnIndex, 1)
}
}
This way, the unregister function always looks for the exact index where the id/fn resides.

Categories

Resources