Search code examples
javascriptreactjsdestructuring

Difference between destructuring in function vs React component


I am experiencing a very frustrating issue with regards to object destructuring in my function parameters. I have the following code in one of my components:

import React from "react";
import { range } from "../../utils";
import { checkGuess } from "../../game-helpers";

function Guess({ guess, answer }) {
  const guessEmpty = guess === undefined ? true : false;
  const letters = !guessEmpty && checkGuess(guess.value, answer);

  function renderLetter({ letter = undefined, status = undefined } = {}, num) {
    return (
      <span className={`cell ${status}`} key={num}>
        {letter}
      </span>
    );
  }

  return (
    <>
      {
        <p className="guess">
          {range(5).map((num) => renderLetter(letters[num], num))}
        </p>
      }
    </>
  );
}

export default Guess;

The letters constant is an array, which will either be set to false or consist of a list of objects depending on whether the guess input parameter for the Guess function is empty or not.

When I pass letters[num] (which can be an object, or undefined) into my renderLetter function, my letter and status input parameters are either converted to undefined or strings depending on the input through object destructuring. This all works great.

However, instead of rendering the <span> elements by using the renderLetter function, I now want to move this to a new component. The code for this component is:

import React from "react";

function Letter({ letter = undefined, status = undefined } = {}) {
  return (
    <>
      <span className={`cell ${status}`}>{letter}</span>
    </>
  );
}

export default Letter;

Similarly to the way I called the renderLetter function, I am now attempting to render the components:

return (
    <>
      {
        <p className="guess">
          {range(5).map((num) => (
            <Letter letter={letters[num]} num={num} />
          ))}
        </p>
      }
    </>
  );
}

However, when logging letter and status parameters inside the Letter component, the letter parameter returns an object, and the status parameter returns undefined. As such, I'm assuming I'm messing up in destructuring the input correctly.

I fail to see why however, since doing it this exact same way does work when using a function inside the same component, while it does not when transferring the code to a separate component.

Any help in figuring this out would be greatly appreciated!


Solution

  • The way your Letter component is defined, it takes two props: letter and status, both expected to be strings or undefined.

    But <Letter letter={letters[num]} num={num} /> does pass an object to the letter prop, nothing to the status prop, and a number to the non-existing (and therefore ignored) num prop. What you want instead is

    <Letter letter={letters[num]?.letter} status={letters[num]?.status} key={num} />
    

    or simpler

    <Letter {...letters[num]} key={num} />
    

    Your problem is not with the destructuring, but rather with the usage of the component which works different than a function call.


    Btw, in a function component you don't need to default the parameter to an empty object, and you don't have to default anything to undefined (though you probably do want to default the status to the empty string). It'll suffice to write

    export default function Letter({ letter, status='' }) {
      return (
        <span className={`cell ${status}`}>{letter}</span>
      );
    }