0
    setProfiles({
      ...profiles,
      idx: { ...idx, tag: "hey" },
    });

I am making a copy of profiles object, I want to replace the idx object with a copy of idx and tag appended to it. Where did I go wrong? I am using React Hooks btw

Here is my whole code. I'm not sure why your suggestions aren't working. I must've messed up the format somehow. I would appreciate it if someone could help me out on this, I have been stuck on this for about an hour right now and it's just irritating me. I found another way to do it but it's not so efficient.

  const ProfileCard = ({ profiles, setProfiles }) => {
  const [isCardExpanded, setIsCardExpanded] = useState(false);

    // Handles the logic of adding a tag
    const onKeyDownHandler = (e, idx) => {
      let updatedProfile = profiles[idx];

      if (e.keyCode === 13) {
        setProfiles({
          ...profiles,
          idx: { ...profiles.idx, tag: "hey" },
        });
      }
    };

    return Object.keys(profiles).map((idx) => {
      return (
        <div className="profile-container" key={profiles[idx].id}>
          <div className="profile-img-and-content">
            <div className="profile-img">
              <img
                src={profiles[idx].pic}
                alt={`${profiles[idx].firstName} ${profiles[idx]}'s Profile`}
              />
            </div>
            <div className="profile-content">
              <h1>
                {`${profiles[idx].firstName.toUpperCase()} ${profiles[
                  idx
                ].lastName.toUpperCase()}`}
              </h1>
              <div className="profile-content-no-header">
                <p>{`Email: ${profiles[idx].email}`}</p>
                <p>{`Company: ${profiles[idx].company}`}</p>
                <p>{`Skill: ${profiles[idx].skill}`}</p>
                <p>{`Average: ${average(profiles[idx].grades)}%`}</p>
                <div className={`${!isCardExpanded ? null : "ghost"}`}>
                  <div className="tags">tag 1</div>
                </div>
                <input
                  onKeyDown={(e) => {
                    onKeyDownHandler(e, idx);
                  }}
                  className={`input-tag ${!isCardExpanded ? null : "ghost"}`}
                  type="text"
                  placeholder="Add a tag"
                />
                <div
                  className={`${isCardExpanded ? null : "ghost"}`}
                  style={{ marginTop: "40px" }}
                >
                  {renderGrades(profiles[idx].grades)}
                </div>
                <div className={`${isCardExpanded ? null : "ghost"}`}>
                  <div className="tags">h</div>
                </div>
                <input
                  className={`input-tag ${isCardExpanded ? null : "ghost"}`}
                  type="text"
                  placeholder="Add a tag"
                />
              </div>
            </div>
          </div>
          <button onClick={expandCard}>{isCardExpanded ? "-" : "+"}</button>
        </div>
      );
    });
  };

  return <div>{renderProfiles(profiles)}</div>;
};
Andy
  • 303
  • 1
  • 9
  • 3
    change `...idx` to `...profiles.idx` – Nicholas Tower Dec 07 '20 at 17:22
  • I tried that but it doesn't work. I think my whole state is getting replaced with that subset of data somehow – Andy Dec 07 '20 at 17:29
  • What is the structure of the profile object? Also, can you give a before and after example of what structure you want? – Garrett Motzner Dec 07 '20 at 17:40
  • Just posted a picture, inside each index, I would like to add a property of tag to a person based on user input – Andy Dec 07 '20 at 17:46
  • Ok. Notice that `profiles` is an array, not a regular object, but when you are calling `setProfiles` you are passing an _object_ (`{…}`) literal, not an array. Take a look here for some techniques to update an array: https://stackoverflow.com/questions/38060705/replace-element-at-specific-position-in-an-array-without-mutating-it – Garrett Motzner Dec 07 '20 at 17:57

1 Answers1

0

Given that Profiles is an array of arbitrary objects, and you want to update a specific item in the array by index to set a property, you can do something like this:

  const onKeyDownHandler = (e, idx) => {
      if (e.keyCode !== 13) {
        return // bail if not the right key code
      }

      // since we are updating the state based on previous state, 
      // use the functional update style, to prevent bugs from stale data
      setProfiles(profiles => {
        let originalProfile = profiles[idx]
        let updatedProfile = {...originalProfile, tag: "hey"}
        // One of several methods to duplicate the array and update the current index
        return Object.assign(
          [],
          profiles,
          {[idx]: updatedProfile }
        )
      })
    }

(See this answer and the React useState Documentation for more detail)

This works as a naive solution, but is somewhat problematic. React recommends using the useReducer hook instead of the useState hook for these cases (More info in this blog post: https://www.robinwieruch.de/react-usereducer-vs-usestate/). In that case, you would the dispatch to your component, and call it something like dispatch({action: "updateProfile", index: idx, changes: {tag: "hey"}}).

Garrett Motzner
  • 3,021
  • 1
  • 13
  • 30