Search code examples
javascriptreactjscomponentsmappingchildren

How to map over an array of objects in another file in React?


I am trying to refactor my code and in doing so, I am extracting a single item and putting it into its own component. This MemberItem component has multiple functions state that influence its rendering, however, when I start passing props, the component breaks. I am passing all of the functions, properties and state the the child component needs, but I am still unable to get it to render properly.

// Members.js (Parent Component)

export const Members = () => {
  // BELOW ARE THE FUNCTIONS AND STATE THAT INFLUENCE THE CHILD COMPONENT
  const [memberName, setMemberName] = useState('')
  const [editingMemberName, setEditingMemberName] = useState(
    members.map(() => false)
  )
  
  // Update member name
  const editMemberName = async (_, index) => {
    let new_editing_members_state = members.map(() => false)
    new_editing_members_state[index] = true
    setEditingMemberName(new_editing_members_state)
  }
  
  // Cancel editing mode
  const cancelEditMemberName = async (_, index) => {
    let new_editing_members_state = members.map(() => false)
    new_editing_members_state[index] = false
    setEditingMemberName(new_editing_members_state)
  }

  // UPDATE name in database
  const updateMemberName = async (index, id) => {
    let new_editing_members_state = members.map(() => false)
    new_editing_members_state[index] = false
    setEditingMemberName(new_editing_members_state)
  }

  // BELOW, LOOPS OVER EACH ITEM
  const memberItems = members.map((member, index) => {
    return (
      <MemberItem
        member={member}
        index={index}
        editingMemberName={editingMemberName[index]}
        editMemberName={editMemberName}
        handleChangeName={handleChangeName}
        updateMemberName={updateMemberName}
        cancelEditMemberName={cancelEditMemberName}
        destroyMember={destroyMember}
      />
    )
  })

  return (
    // RENDER THE LIST OF ITEMS
    {memberItems}
  )
}

// Member.js (Child Component)
export const MemberItem = (
  member,
  index,
  editingMemberName,
  editMemberName,
  handleChangeName,
  updateMemberName,
  cancelEditMemberName,
  destroyMember
  ) => {
  
  return (
     <div
      key={member.id}
    >
      <div>
        {editingMemberName[index] ? (
          <input
            type="text"
            placeholder="Johnny Appleseed"
            onChange={handleChangeName}
          />
        ) : (
          <>
            <div>
              {member.name.substring(0, 1).toUpperCase()}
            </div>
            <h3>{member.name}</h3>
          </>
        )}
      </div>
      <div>
        {editingMemberName[index] ? (
          <button
            onClick={() => updateMemberName(index, member.id)}
          >
            <CgCheckO size=".75em" />
          </button>
        ) : (
          <button
            onClick={() => editMemberName(member.id, index)}
          >
            <FiTool size=".75em" />
          </button>
        )}
        <button>
          {editingMemberName[index] ? (
            <GiCancel
              onClick={() => cancelEditMemberName(member.id, index)}
              size=".75em"
            />
          ) : (
            <RiDeleteBinLine
              onClick={() => destroyMember(member.id)}
              size=".75em"
            />
          )}
        </button>
      </div>
    </div>
  )
}

Currently, I am getting an error of TypeError: editingMemberName is undefined and a warning of Each child in a list should have a unique "key" prop, but if you see, I do pass in an id into the key property.


Solution

  • In React, props are passed down to function components as a single object. Your component function assumes props are passed down as separate arguments and not in a single object.

    Fixed component definition (note the brackets around the argument list):

    MemberItem = ({
      member,
      index,
      editingMemberName,
      editMemberName,
      handleChangeName,
      updateMemberName,
      cancelEditMemberName,
      destroyMember
    }) => { ... }
    

    This method of unpacking properties is called object destructuring.