1

I'm trying to output data from an array one per page.

This code throws an error Uncaught TypeError: Cannot read properties of undefined (reading 'word')

Although both the array newArr and the data newArr[i].word are output to the console.

const shuffle = (arr) => arr.sort(() => Math.random() - 0.5);

function Home({ data, num }) {
  const [newArr, setNewArr] = useState([]);
  const [i, setI] = useState(0);

  useEffect(() => {
    setNewArr(shuffle(data));
  }, []);

  const randomCard = () => {
    if (i < newArr.length - 1) {
      setI(i + 1);
      console.log(i);
      console.log(newArr);
      console.log(newArr[i].word);
    } else {
      setNewArr(shuffle(data));
      setI(0);
    }
  };
  return (
    <div>
      <div className="home-inner">
        {num >= 1 ? (
          <div className="home-stat">
            <div onClick={() => randomCard} className="home-random">
              <div className="hr-card">
                <p className="randomword">{newArr[i].word}</p>
                <p className="randomtranslate">{newArr[i].translate}</p>
              </div>
            </div>
          </div>
        ) : (
          <div>
            {" "}
              <Link className="main-btn" to="/addcard">
                Add 
              </Link>
          </div>
        )}
      </div>
    </div>
  );
}

export default Home;
1
  • 1
    you can change newArr[i].word to newArr[i]?.word. It might solve your problem Commented Jan 21, 2023 at 16:02

1 Answer 1

1

Your error is happening on the first render of your component. It is happening due to i is 0 and newArr is [] which is an empty array, and when you try to render it - newArr[0] - it returns undefined.

To fix that - initialize newArr with actual value and add an additional check at rendering place.

function Home({ data, num }) {
  // Initialize newArr with initial data instead of useEffect
  const [newArr, setNewArr] = useState(shuffle([...data]));
  const [i, setI] = useState(0);

  // just a utility
  const randomCard = useMemo(() => {
    // you can add some console.logs here for debugging purposes, just in case
    if (!newArr || i < 0 || i >= newArr.length) return undefined;
    return newArr[i];
  }, [newArr, i]);

  // changed naming here
  const randomizeCard = () => {
    if (i < newArr.length - 1) {
      setI(i + 1);
    } else {
      setNewArr(shuffle([...data]));
      setI(0);
    }
  };

  return (
    <div>
      <div className="home-inner">
        {/* Added additinal check */}
        {num >= 1 && randomCard ? (
          <div className="home-stat">
            {/* fixed onClick binding */}
            <div onClick={randomizeCard} className="home-random">
              <div className="hr-card">
                <p className="randomword">{randomCard.word}</p>
                <p className="randomtranslate">{randomCard.translate}</p>
              </div>
            </div>
          </div>
        ) : (
          <div>
            {" "}
            <Link className="main-btn" to="/addcard">
              Add
            </Link>
          </div>
        )}
      </div>
    </div>
  );
}

Note: The sort() method sorts the elements of an array in place and returns the reference to the same array, so you are mutating data parameter which usually should not happen AND in same time React will not detect the changes if you setState with same "value", meaning Array object has the same reference all the time. Some details here: Difference between setting array equal to another one, or using three dots. Solution to that is shuffle([...data]) which will create a shallow copy of data.

Note2: calling setI(i + 1); and console.log(i); just after it - will not work in the way you expect it due to state value will be updated on next render only

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Sergey for your detailed help! Appreciate it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.