skip to Main Content

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!

3

Answers


  1. You’re very close. When you converted the renderLetter function into a full component, you forgot the first parameter of a React component is always the props object. Since you’re now passing letters[num] as a prop instead of an argument, it will arrive in the props object under the letter key.

    // A component like this:
    <Letter letter={letters[num]} num={num} />
    
    // Results in a props object like this:
    {
      letter: {
        letter: '',
        status: ''
      },
      num: 0
    }
    

    Your destructuring needs to account for this in your component:

    function Letter({ letter: { letter, status } }) {
     ...
    }
    
    Login or Signup to reply.
  2. You need to access the letter prop first from the props object. When you use a component like this:

    <Letter letter={letters[num]} num={num} />
    

    React will call your Letter function, passing it in an object roughly of this shape as the first argument to Letter:

    {
      letter: letters[num],
      num: num
      // this object has other properties such as `children`, but that's irrelevant
    }
    

    Mentally, you can think of React as calling your function as Letter({letter: letters[num], num: num});, but do be aware that this isn’t exactly how React calls your component under the hood (it does some other things such as binding the this, and passing in additional arguments and props).

    That means in your functional component, to access the object letters[num] you need to access it via the letters property. Without destructuring the function arguments, this would look like:

    function Letter(props) {
      const { letter, status } = props.letter ?? {};
      const num = props.num;
      ...
    }
    

    With destructuring the arguments, you’d need to do nested destructuring:

    function Letter({ letter: { letter, status } = {}, num }) {
      ...
    }
    
    Login or Signup to reply.
  3. 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} />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search