Support
Quality
Security
License
Reuse
Coming Soon for all Libraries!
Currently covering the most popular Java, JavaScript and Python libraries. See a SAMPLE HERE.
kandi's functional review helps you automatically verify the functionalities of the libraries and avoid rework.
A card matching game built on HTML5, CSS3 and Javascript (no frameworks used).
Creating an array in one function and then using it somewhere else
const ImportedArray = useMemo(() => ImportedWords().shuffledWords, []);
QUESTION
Creating an array in one function and then using it somewhere else
Asked 2022-Jan-19 at 22:45TL:DR, I am creating a randomly-ordered array in one React component, that I need to use in another component - but the second component keeps re-rendering and therefore re-shuffling my array - but I need its order to be fixed once it gets imported for the first time.
First things first - if I am doing this in a needlessly roundabout way, please do say so, I'm not set on this way.
I am making a flashcard program, and I want to give users the option to play games with random selections of their cards.
The way I am currently trying to do this, is that I have a functional component (because I need to do things like dispatch in it) which works as follows - I've added comments to explain what each bit does:
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getPosts } from "../../actions/posts";
export function WORDS() {
const dispatch = useDispatch();
const [localUser, setLocalUser] = useState();
//get all users
useEffect(() => {
dispatch(getPosts());
}, []);
const users = useSelector((state) => state.posts);
//set user to match the user in the local storage
const [user, setUser] = useState();
useEffect(() => {
setLocalUser(JSON.parse(localStorage.getItem("profile")));
}, [localStorage]);
useEffect(() => {
setUser(users.filter((user) => user.code == localUser?.code)[0]);
}, [users, localUser]);
//create an array of 8 random words from the users cards object
let RandomArray = []
if (user) {
for (let i = 0; RandomArray.length < 8; i++) {
let RanNum = Math.floor(Math.random() * user.cards.length);
!RandomArray.includes(user.cards[RanNum]) && RandomArray.push(user.cards[RanNum])
}
}
//create duplicates of each word and make an array of them all, once with the front of the card in first place and once with the back in first place
let shuffledWords = [];
RandomArray.map((word) => {
let newWord = { Q: word.front, A: word.back };
let newWord2 = { Q: word.back, A: word.front };
shuffledWords.push(newWord);
shuffledWords.push(newWord2);
});
//shuffle that array
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
shuffleArray(shuffledWords);
//return this array so I can call it in other functions
return { shuffledWords }
};
The game is then a kind of 'memory' game, where users try and match words with their translation. That's currently running like this, again with comments (please excuse the excessive inline styling - I will move that all to the stylesheet eventually but I've left it in here just in case any of that is what's causing the problems.):
import React, { useState } from "react";
import { WORDS as ImportedWords } from "./WORDS";
export const Memory = () => {
//import words from function
const ImportedArray = ImportedWords().shuffledWords
//create state variables for the first and second card a user pics
const [selectL1, setSelectL1] = useState("");
const [selectL2, setSelectL2] = useState("");
//create state variables for whether to display a card or not and whether or not it was correct
const [show, setShow] = useState([]);
const [correct, setCorrect] = useState([]);
//variable to make cards unclickable while they're spinning over
const [clickable, setClickable] = useState(true);
//if user has picked two cards, check if they match and either set them to correct or turn them back over
if (selectL1 && selectL2) {
clickable == true && setClickable(false);
let selectQ = ImportedArray.filter((word) => word.Q == selectL1)[0];
console.log("selectQ: ", selectQ);
selectQ && selectL2 == selectQ.A
? correct.push(selectL1, selectL2)
: console.log("Incorrect!");
setTimeout(function () {
setSelectL1("");
setSelectL2("");
setShow([]);
setClickable(true);
//correct.length == shuffledWords.length * 2 && alert("You win!");
}, 800);
}
//show the card a user's clicked
const selectCard = (wordId) => {
!selectL1 ? setSelectL1(wordId) : setSelectL2(wordId);
show.push(wordId);
};
return (
<div className="memory-game-wrapper">
<div
style={{ perspective: "2000px", pointerEvents: !clickable && "none" }}
>
{/* filter through the imported array and display them */}
{ImportedArray.map((word) => {
return (
<div
className={
show.includes(word.Q) || correct.includes(word.Q)
? "card flip"
: "card"
}
id={word.Q}
style={{
borderRadius: "5px",
display: "inline-block",
width: "100px",
height: "180px",
backgroundColor: "rgb(185, 204, 218)",
margin: "5px",
}}
onClick={() =>
!correct.includes(word.Q) &&
!show.includes(word.Q) &&
selectCard(word.Q)
}
>
<div
className="back-face"
style={{
position: "absolute",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<center>
<span style={{ userSelect: "none" }}></span>
</center>
</div>
<div
className="front-face"
style={{
position: "absolute",
height: "100%",
display: "flex",
width: "100%",
borderRadius: "5px",
border: "5px solid rgb(185, 204, 218)",
boxSizing: "border-box",
backgroundColor: correct.includes(word.Q)
? "white"
: "rgb(185, 204, 218)",
justifyContent: "center",
alignItems: "center",
}}
>
<h3 style={{ userSelect: "none" }}>{word.Q}</h3>
</div>
</div>
);
})}
</div>
</div>
);
};
I suspected that what was happening was that the whole array is being re-rendered any time a user clicks on any of the cards, which means the order gets shuffled again - and makes the game unplayable, so I whacked in a console.log(ImportedArray[0])
to check and yes, that is definitely happening. But I can't work out how to stop it?
Any ideas??
ANSWER
Answered 2022-Jan-19 at 22:45If the Memory component is not conditionally mounted/unmounted in the parent, like {condition && <Memory}
, you can use the useMemo hook to memoize the imported words at the first render.
const ImportedArray = useMemo(() => ImportedWords().shuffledWords, []);
Anyway the WORDS
component is a candidate to be a custom hook where you can encapsulate the words logic. it should be named useWords
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
No vulnerabilities reported
Explore Related Topics