0

Okay so I have this large page of code but have simplified the problem below. Basically I am creating a matching app and have this component below named "Match", it gets a list of dicts from the state and I create new lists within the Match component so that I can randomize the definitions and words. The problem I face is that in the return section I want a different styling to be rendered depending on whether the correct word and definitions where pressed. I have gotten the "randomization and check if matching" code to work correctly. The only thing wrong is that after it returns true that both the definition and word match, I can't figure out how to re-render the return so only those two items are rendered with different styling.(in the code below only the definitions will change) Below I added a simple test key when creating a new list, to be the variable that will determine the styling. Look for ---> in the code below to see important notes/code.

I have tried using useEffect and placing randomArr in the form of useEffect(),[randomArr]; but no luck and also tried using useState but to no luck received error of to many renders. Just need help making sure that when test is updated to not null that the styling changes.

import React, { Fragment } from "react";
import { useSelector } from "react-redux"; //used
import { Grid, Button } from "@mui/material";

const Match = () => {
  const items = useSelector((state) => state.posts);

  let x = null;
  const wordArr = []; //New list created to randomize list taken in from state
  const randomArr = []; //same above
  const newList = []; //same above

  // ##### new list to mix up words and defs       #### //   
  for (let i = 0; i < items.length; i++) {
    newList.push(items[i]);
    wordArr.push({ _id: items[i]._id, definition: items[i].definition });
  }
  for (let i = 0; i < items.length; i++) {
    let temp2 = wordArr[Math.floor(Math.random() * wordArr.length)]; //gets random obj

    randomArr.push({ _id: temp2._id, definition: temp2.definition, test: null }); // ----> adds Null so it renders the first option below
    wordArr.splice(wordArr.indexOf(temp2), 1); //deletes random obj so does not get called again
  }
  // ####### //

  const checkId = (id) => {
    if (x) {
      if (id._id === x) {
        console.log("Match");
        console.log(id);
        console.log(x);
        newList.pop();
        console.log(newList);
        id.test = true // ---->Changes test to something so it returns true below
      } else {
        console.log("no match");
      }
      x = null;
    } else {
      x = id._id;
    }
  };

  return (
    <div>
      <Grid container spacing={2}>
        {items.map((item, i) => (
          <Fragment key={`word${item._id}`}>
            <Grid item xs={6}>
              <Button fullWidth onClick={() => checkId(item)}>
                <SimpleCard item={item.word}></SimpleCard>
              </Button>
            </Grid>
            {randomArr[i].test ? (
              <Grid item xs={6}>
                <div> Changed</div>
              </Grid>
            ) : (
              <Grid item xs={6}>
                <Button fullWidth onClick={() => checkId(randomArr[i])}>
                  <SimpleCard item={randomArr[i].definition}></SimpleCard>
                </Button>
              </Grid>
            )}
          </Fragment>
        ))}
      </Grid>
    </div>
  );
};

export default Match;

Edit changing the checkId to use state

const [x, setX] = useState(null); 
const [x, setX] = useState(null);
  const checkId = (id) => {
    if (x) {
      if (id._ID === x) {
        console.log("Match");
        console.log(id._id);
        console.log(x);
        newList.pop();
        console.log(newList);
        id.test = true
      } else {
        console.log("no match");
      }
      setX(null);
      console.log("reset");
    } else {
      setX(id._id);
      console.log("set X");
    }
  };

[MostUpdated] Error Code

  const randomfunc = () => {
    for (let i = 0; i < items.length; i++) {
      newList.push(items[i]);
      wordArr.push({ _id: items[i]._id, definition: items[i].definition });
    }
    for (let i = 0; i < items.length; i++) {
      let temp2 = wordArr[Math.floor(Math.random() * wordArr.length)]; //gets random obj

      randomArr.push({
        _id: temp2._id,
        definition: temp2.definition,
        test: null,
      }); // ----> adds Null so it renders the first option below
      wordArr.splice(wordArr.indexOf(temp2), 1); //deletes random obj so does not get called again
    }
  };
  const [x, setX] = useState(null);

  useEffect(() => {
    randomfunc();
    console.log("test");
  });
tester0001
  • 21
  • 1
  • 5
  • 1
    Because you are simply declaring variables at the top level of your component and not using State there is no mechanism to trigger a rerender (and if a rerender did occur all of your variables would be redeclared as empty arrays again). Instead, those variables should be held in state in the component and the `checkId` should setState in order to tell react to rerender. – pilchard May 23 '22 at 07:43
  • Okay so I set the checkId to use useState and it re renders the components, yet renders them in different order rather than style, code edited in original post bottom – tester0001 May 23 '22 at 15:49
  • I want the order from the randomized list to stay in the same order when the re-rendering happens, yet by setting the X and setX it causes the randomization code to trigger again thus the order of the components drawn are rendered in a different order – tester0001 May 23 '22 at 15:58
  • 1
    Yes, the randomized arrays should also be stored in state (or at least a ref), and the randomization code should be in a useEffect with empty dependency array so that it is only called on first mount. – pilchard May 23 '22 at 16:15
  • Okay so been playing around trying to figure it out and created state for the list I want to store, have not placed in the code because when I do it says to many re renders so Im guessing I cannot set the state anywhere in the component itself or in the useEffect. But before that I noticed that my randomization code is not running as I cannot access any of the variables inside. Keep receiving the error of undefined. Yet the console.log('test') does print, not sure why it is not running. Originally pasted it in the useEffect itself but then created handleFunc. See most updated above – tester0001 May 23 '22 at 17:10
  • 1
    I don't have time to write a proper answer, but here is a [sandbox](https://codesandbox.io/s/rendering-against-a-ref-matching-game-1glph7?file=/src/App.js) which illustrates a minimal implementation. I've used a more concise shuffle (see: [How to randomize (shuffle) a JavaScript array?](https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array)), made sure to avoid mutation of state/redux source objects, and added some verbose conditional styling, as well conditional `onClick`s. I hope it helps clarify some concepts (the – pilchard May 23 '22 at 22:52
  • 1
    Some relevant duplicates: [Multiple CSS conditional classes using React](https://stackoverflow.com/questions/53092209/multiple-css-conditional-classes-using-react) and [How to conditionally add or not onClick on a div in react?](https://stackoverflow.com/a/54039314/13762301) – pilchard May 23 '22 at 22:53
  • Wow, thanks for the quick demo I'll study it and try to make improvements on my code, thanks for the additional resources. I'll msg back if some other problem comes up. – tester0001 May 24 '22 at 03:13

0 Answers0