1
votes

I have an object that contains array of arrays. I was able to make it render the way I wanted. However, the React key is giving me trouble; throwing this "Warning: Each child in a list should have a unique "key" prop."

I have tried giving a unique 'id' property within the original array object for each element in each array but did not help.

I also looked through these but I think I have all of the suggestions there: Each child in an array or iterator should have a unique "key" prop in reactjs Warning: Each child in a list should have a unique "key" prop Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`

My object

const courses = 
[
    {
      name: 'Half Stack application development',
      id: 'Half Stack application development',
      parts: [
        {
          name: 'Fundamentals of React',
          exercises: 10,
          id: 'Fundamentals of React'
        },
        {
          name: 'Using props to pass data',
          exercises: 7,
          id: 'Using props to pass data'
        },
        {
          name: 'State of a component',
          exercises: 14,
          id: 'State of a component'
        },
        {
          name: 'Redux',
          exercises: 11,
          id: 'Redux'
        }
      ]
    }, 
    {
      name: 'Node.js',
      id: 'Node.js',
      parts: [
        {
          name: 'Routing',
          exercises: 3,
          id: 'Routing'
        },
        {
          name: 'Middlewares',
          exercises: 7,
          id: 'Middlewares'
        }
        ]
    }
]


ReactDOM.render(<App course={courses}/>, document.getElementById('root'));

This is the callback order:

App -> Courses -> Course -> {Header, Content -> Part}

const App = ({course}) => {
  return (
    <>
      <Courses course={course} />
    </>
  )
}

const Courses = ({course}) => {
    console.log("Starting.....")
    const courseList = course.map(nameConent => nameConent)
    // console.log(courseList)
    // const idList = courseList.map(eachCourse =>eachCourse.id)
    const mapEverything = () => courseList.map(a => <Course name={a.name} partsList={a.parts} id={a.id}/>)
    // const mapEverything = () => courseList.map(a => console.log(a.id))
    // console.log("CourseID",idList)
    return (
        <>  
            {mapEverything()}
        </>

    )
}

const Course = ({name,partsList,id}) => {
    // console.log(name)
    console.log("CourseID", id)

    return (
        <>
            <div key={id}>
                <Header header={name} id={id}/>
                <Content contents={partsList} id={id+"======"}/>
            </div>
        </>
    )
}

const Content = ({contents,id}) => {
    console.log("contentsID",id)
    const partList = () => contents.map(part =>
        <Part
            name={part.name}
            id={part.id} 
            exercises={part.exercises}
        />
    )

    const getSumofArray = (accumulator, currentValue) => accumulator + currentValue

    const exeList = contents.map(content => content.exercises).reduce(getSumofArray)
    // console.log(exeList)
    return (
        <>
            <ul key={id}>
                {partList()}
            </ul>
            <b>total exercises: {exeList}</b>
        </>
    )
}

const Header = ({header, id}) => {
    console.log("HeaderID", id)
    return (
        <>
            <h1 key={id}>
                {header}
            </h1>
        </>
    )
}

const Part = ({name, id, exercises}) => {
    console.log("PartID", id)
    return (
        <>
            <li  key={id}>
                {name}: {exercises}
            </li>
        </>
    )
}


It's having trouble at the Courses and Content component.

screenshot of warnings: https://drive.google.com/open?id=1kcLlF0d90BG6LXPojDtfSlBFGafC9HC_

2

2 Answers

2
votes

I think a problem is here:

const mapEverything = () => courseList.map(a => <Course name={a.name} partsList={a.parts} id={a.id}/>)

You should try add key here:

const mapEverything = () => courseList.map(a => <Course name={a.name} partsList={a.parts} id={a.id} key={a.id}/>)

And here too:

const partList = () => contents.map(part =>
        <Part
            name={part.name}
            id={part.id} 
            exercises={part.exercises}
            key={part.id}
        />)

Here is a good explanation of why you need to do it this way.

0
votes

This is the topic of Key Props. Whenever we render an array or list in Reactjs, it expects to have a key property available.

The presence of this key property is for performance concerns, it makes it easier for Reactjs to render a list of items.

All you have to do is provide another prop to any other elements in the array. The most unique identifier you have in your course list seems to be name. So you may want to simplify your code to something like this to get yourself going:

const CourseList = () => {
  const RenderedCourses = courses.map(function(course) {
    return <CourseDetail key={course.name} course={course} />;
  });
  return <ul>{RenderedCourses}</ul>;
};

Then once you got that working you can make the code concise how you had it with arrow function and all:

const CourseList = () => {
  const RenderedCourses = courses.map(course => (
    <CourseDetail key={course.name} course={course} />
  ));
  return <ul>{RenderedCourses}</ul>;
};