Working with a array of objects in React. Iterating error saying I cannot use object, use array instead - javascript

I'm working on a React portfolio project. I have an external data.js file with an array of objects, which I import into the JS file I'm working in. I want to iterate over the array and place each object into it's own div.
I already tried an If Loop and now working with Map(). This is my code:
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'><ProjectItem /></h1>
</div>
)
}
I don't get the problem of iterating trough an array of objects. This is the error:
Error: Objects are not valid as a React child (found: object with keys {-list of keys-}). If you meant to render a collection of children, use an array instead.
I must overlook a simple thing, but I'm a little stuck at the moment :-)

From the fact you're using project.id (and the error message from React), we can assume that project is an object, which means you're trying to use an object as a React child here:
<div>{project}</div>
The error is that you can't do that. Instead, use properties from the object to come up with how you want it displayed. For instance, if project has a description property:
<div>{project.description}</div>
Or if it has (say) a description and a number of days estimated for the project, you might do:
<div>{project.description} - {project.days} {project.days === 1 ? "day" : "days"}</div>
And so on. The fundamental thing is to provide React with something it can put in the DOM (loosely, "display"), such as strings, numbers, arrays, true (but not false)...

You need to return array from your ProjectItem i.e you need to do this:
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'>{ProjectItem()}</h1>
</div>
)
}

try JSON.stringy(project) like this or you should use your object property.Let's we say project has a property that called name
Here is a working example for you.
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{JSON.stringy(project)} || {project.name}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'><ProjectItem /></h1>
</div>
)
I hope it can work.

you can use project as object into div element
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project.text}</div>
</div>
))
)

Related

What should I use as a key for "row" elements in react?

I have a gallery that displays a number of books per row. This gallery takes an array of books as a prop and uses "itemsPerRow" prop to chunk the books into a 2 dimensional array and then loops through all the books to display the books in a grid-like structure.
export default function Gallery({ items, itemsPerRow, renderLink }) {
itemsPerRow = itemsPerRow ?? 3
const rows = chunk(items, itemsPerRow)
const renderEmptyItems = (itemsToRender) => {
const items = []
for(let n = itemsToRender; n > 0; n--) {
items.push(<GalleryItem key={`empty_${n}`} empty/>)
}
return items
}
return (
<div>
{
rows.map((row, index) => (
<div key={index} className="tile is-ancestor">
{row.map(item => <GalleryItem key={item.id} renderLink={renderLink} {...item}/>)}
{/* Add empty gallery items to make rows even */}
{index + 1 === rows.length && renderEmptyItems(itemsPerRow - row.length)}
</div>
))
}
</div>
)
}
However, unless I give each div representing a row a key, react complains about the lack of keys. As I understand it, using the index as a key doesn't really help react and should be avoided. So what should I use as a key here <div key={index} className="tile is-ancestor"> instead of the index?
Use a unique identifier (book.id, maybe book.title if it's unique) for the key props. If your data does not have a unique identifier, it's okay to use index.
You need to specify a value that uniquely identify the item, such as the id. You can read more about keys in the documentation.
Also it is not recommended to use indexes as keys if the order of your data can change, as React relies on the keys to know which components to re-render, the documentation I linked explains that further.
You can use the unique_identifier which differentiate each of the documents(probably, you should pass the document _id as a key prop in the row components)
<div className="row">
{notes.map((item) => {
return (
<div key={note._id} className="col-md-6">
<Component item={item} />
</div>
);
})}
</div>

Comparing Arrays using .map (ReactJS)

In my project, I have 2 arrays being read from Firestore.
courseList - A list of courses of which a user is enrolled in
courses - A list of all of the courses available in the project
I would like to compare these using a .map so that in my course portal, only the courses of which the user is enrolled in is rendered.
Here is what the arrays look like:
courseList:
courses
I know the arrays work, however, the .map doesn't seem to be working!
Here's my code:
const {courses} = this.state
const {courseList} = this.state
{
courses.length && courses.map (course => {
if (course.courseUrl === courseList.CourseCode) {
return (
<div className = "CourseTile" key = {course.courseName}>
<div className = "CourseTitle">
<h1> {course.courseName}</h1>
</div>
<div className = "CourseDescription">
<p> {course.courseSummary}</p>
</div>
<Link to={`/course/${course.courseUrl}/courseinformation`}> <button className = "LetsGoButton"> Take course</button> </Link>
</div>
)
}
else return null;
}
)
}
If I replace
if (course.courseUrl === courseList.CourseCode)
with
if (course.courseUrl === "websitedesign")
It renders the website design course only, So I believe there's something wrong with this line.
Any help would be appreciated.
You are correct in where the problem lies:
course.courseUrl === courseList.CourseCode
In this case course is a single item from a list, with a property courseUrl. That's fine. But courseList is an array of items, each of which has a CourseCode property. The Array itself does not (although, interestingly, it could).
It seems like what you are trying to do is pull the full course data (from courses) but filtered to only the ones the user has. In this case, you have to loop through one list, looking through the other list for each item. What you want is filter (or, more powerfully, reduce) but probably not map.
const filteredCourses = availableCourses.filter( availableCourse => studentsCourses.some( studentsCourse => studentsCourse.id === availableCourse.id ) );
You'll notice I renamed the variables to make it clear which of the two lists is being used at each part.
The outer function filter will return a new array containing only those items that return 'true' in the callback function.
The inner callback function some loops through another array (the student's enrolled courses) and returns true if it finds any that match the given condition.
So in English, "Filter this list of all the courses, giving me back only the courses that have a matching ID in the list of the student's enrolled courses."
if (course.courseUrl === courseList.CourseCode)
You can filter the courses array by checking if each course is included in the courseList array, matching an URL to a courseList element's CourseCode property. array.prototype.some is used to iterate the course list and check that at least one courseList item matches. Once filtered you can map the filtered result as per normal.
const {courses} = this.state;
const {courseList} = this.state;
...
{courses
.filter(({ courseUrl }) =>
courseList.some(({ CourseCode }) => CourseCode === courseUrl)
)
.map((course) => {
return (
<div className="CourseTile" key={course.courseName}>
<div className="CourseTitle">
<h1> {course.courseName}</h1>
</div>
<div className="CourseDescription">
<p> {course.courseSummary}</p>
</div>
<Link to={`/course/${course.courseUrl}/courseinformation`}>
{" "}
<button className="LetsGoButton"> Take course</button>{" "}
</Link>
</div>
);
})}

How to use property inside map in typescript?

I have an object where I want to reach properties inside. When I type list.activityType i get the error Property 'activityType' does not exist on type 'string'. How can i solve this?
const renderSurveyItem = (
items: SurveyActivityViewModel[]
): JSX.Element => {
return (
<div>
{Object.keys(items).map((list) => (
<div className="kpi" key={list}>
<div className="item text-x"></div>
<div className="item">
<div className="kpi-name">{list.activityType}</div>
</div>
</div>
))}
</div>
);
};
You're calling map on an array of the keys of the properties in items. Looking at your code, you just want to call map on items itself, using the item it provides and perhaps also its index (key):
const renderSurveyItem = (
items: SurveyActivityViewModel[]
): JSX.Element => {
return (
<div>
{items.map((item, index) => (
<div className="kpi" key={index}>
<div className="item text-x"></div>
<div className="item">
<div className="kpi-name">{item.activityType}</div>
</div>
</div>
))}
</div>
);
};
Note I changed list to item, if the items really are lists (their type suggests they aren't) and list makes more sense, there's no reason you can't change it back.
Note that using an array index as a key is usually not recommended, details in the documentation. It's fine for arrays whose entries are never moved around or removed, but otherwise you want to use some unique identifier that's intrinsic to the object itself (item) rather than where it is in the array.

TypeError: this.props.messages.map is not a function

I am using React-Redux and have problem to parse array.
I using selector to return data to component, but I am getting error TypeError: this.props.messages.map is not a function. But if I console.log values it returns me normal array. What I am doing wrong?
Components render method:
render() {
const {loading, error, messages} = this.props;
const mes = JSON.stringify(messages);
console.log(mes); //<------------- returns [{"key":"value"}]
return (
<div>
<MessageList
messages={mes}
/>
);
}
MessageList component map function:
{this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
}
Maybe someone could tell me what I am doing wrong?
What you can do is
render() {
const {loading, error, messages} = this.props;
return (
<div>
<MessageList
messages={messages}
/>
</div>
);
}
Looks like the messages are from redux state. So initially the messages props will be undefined. So before doing map check whether messages is not undefined using conditional operator like below
{this.props.messages && this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
/>
))}
In a comment you've said:
messages returns this: List, not array
JavaScript doesn't have a List type, and it's clear from the result of JSON.stringify that messages is an array, or at least something that JSON.stringify thinks is an array.
I think if you look more carefully, you'll find that messages is an array and you can just use it in your MessageList directly.
If messages really, really doesn't have a map method, you can convert it from whatever it is into an array using Array.from:
mes = Array.from(messages);
...or possibly (depending on what it is) spread notation:
mes = [...messages];
You certainly don't want JSON.stringify, which creates a string.

Remove duplicates when calling from Github API in React

I have a function where I am calling the list of Filetypes from the gist API of a user by looping through it (https://api.github.com/users/getify/gists) :
const FileTags = ({files}) => {
return(
<div>
{
Object.keys(files).map(function (key) {
return(
<ul>
<li> {files[key].language } </li>
</ul>
)
})
}
</div>
);
}
I am able to successfully call the list of languages but the list is with a lot of duplicates, for example:
Markdown
Markdown
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
How do I filter out unique languages without them repeating such as
"Markdown Javascript"
?
Maybe something like this:
const FileTags = ( {files } ) =>
<div>
<ul>
{
[...new Set(Object.keys(files).map(key => files[key].language))]
.map( el => <li>{el}</li>)
}
</ul>
</div>
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
The Set object lets you store unique values of any type, whether
primitive values or object references.

Categories