I am trying to render an objects key and value, however it just doesn't seem to work.
I have managed to display it in the console, but not the actual dom.
I am iterating through the object which has multiple entries.
What do I need to do to actually print out the values?
{attributes.map(items => {
{Object.keys(items).map((key) => {
console.log(key, items[key]);
})}
})}
Like this:
{attributes.map((items, index) => {
return (
<ul key={index}>
{Object.keys(items).map((key) => {
return (
<li key={key + index}>{key}:{items[key]}</li>
)
})}
</ul>
)
})}
Related
Running into the error message: Warning: Each child in a list should have a unique "key" prop.
Without stating the obvious, I've checked here and others have said to add the "key" prop.
I've done that and still get the error. Might be missing something totally obvious but could you be so kind as to point out what I'm missing please:
createPlaylist = () => {
return (
<>
<h2>Expected Result</h2>
<ul key={"playlist"}>
{
this.state.playlist.map((section, index) => (
<>
<li key={index}><h4>{section.sectionName}</h4></li>
<ul key={section.sectionId}>
{
section.lessons.map((lesson, i) => (
<li key={i}>
{lesson.name}<br/>
</li>
))
}
</ul>
</>
)
)
}
</ul>
</>
)
}
All IDs are unique and because it's only using a few items, I've swapped between the index and the uuid and still get the same error. The sections and lessons don't have duplicated uuids.
Stumped as to what's causing the error.
Another question to ask and one that may help the community greatly: how can I determine what is causing the error?
The message is very generic and doesn't specify which element in the list is missing the key prop or where the error lies.
Thank you in advance!
You need the key prop pass to the wrapper component. In this case
createPlaylist = () => {
return (
<>
<h2>Expected Result</h2>
<ul key={"playlist"}>
{
this.state.playlist.map((section, index) => (
<key={keyId}> // you need pass the key prop here.
<li ><h4>{section.sectionName}</h4></li>
<ul >
{
section.lessons.map((lesson, i) => (
<li key={i}>
{lesson.name}<br/>
</li>
))
}
</ul>
</>
)
)
}
</ul>
</>
)
}
Please pass key at root level element.
createPlaylist = () => {
return (
<>
<h2>Expected Result</h2>
<ul key={"playlist"}>
{
this.state.playlist.map((section, index) => (
<key={index}>
<li ><h4>{section.sectionName}</h4></li>
<ul >
{
section.lessons.map((lesson, i) => (
<li key={i}>
{lesson.name}<br/>
</li>
))
}
</ul>
</>
)
)
}
</ul>
</>
)
}
I have this response object from an api, and I want to loop it and render it as if it was a normal array, how can I render tshirt, jeans and furniture? I will not like to render the value of sneakers, Any suggestion?
const items = {
tshirt: "Model TS",
jeans: "ModelXW",
sneakers: "indcdsc54",
furniture: "Table31S"
};
{Object.keys(items).map=>{i =>
<Card>
{items[key]}
</Card>
}
}
Try this one implementation line:
{Object.entries(items).filter(v => v[0] !== 'sneakers').map((v, idx) => <Card key={idx}>v[1]</Card>)}
You can read properties of an object using dynamic key: objectName[keyName]:
{
Object.keys(items).map(key => <Card key={key}>{items[key]}</Card>)
}
and to filter out sneakers:
{Object.keys(items).filter(key => key !== 'sneakers').map((key) => (
<Card key={key}>{items[key]}</Card>
))}
Instead of multiple loops, add an if condition to your code:
Object.keys(items).map(key => {
if (key != 'sneakers') {
return(<Card>{items[key]}</Card>);
}
});
We can use destructuring and it is definitely more readable.
const { sneakers, ...rest } = items;
Object.keys(rest).map((item, id) => {
<Card key={id}>
{item}
</Card>
}
);
I am trying to pass item to onClick handler but it turns out that item is an object but it's just a string.
Here's my code:
const UploadMain = () => {
return (
<Fragment>
<div className="part">
<h2 className="part-h2">category: </h2>
<Options
items={["one", "two", "three", "four"]}
name={useSelector(state => state.upload.category)}
/>
</div>
</Fragment>
);
};
const Options = props => {
let [status, setStatus] = useState(false);
return (
<div className="options-container">
<div className="options-header" onClick={() => setStatus(!status)}>
<p>{props.name}</p>
</div>
<ul className={status ? "options-ul shown-ul" : "options-ul hidden-ul"}>
{props.items.map((item, index) => {
return (
<li
key={index}
value={item}
onClick={item => {
console.log(item) //this line logs some object
}}
>
{item}
</li>
);
})}
</ul>
</div>
);
};
Object logged:
That is not a item but it is an event object. That item does not refer to item of items but it behaves like an alias to the event object.
instead change to this and put the log as is:
onClick={e => {
You can try this code.
{props.items.map((item, index) => {
return (
<li
key={index}
value={item}
onClick={e => {
console.log(item) //this line logs some object
}}
>
{item}
</li>
);
})}
All you need to do is to change the item to e(event object differenct from item)
{props.items.map((item, index) => {
return (
<li
key={index}
value={item}
onClick={item => {
console.log(item) //this line logs some object
}}
>
{item}
</li>
);
})}
If you take a closer look there, you first declare the item variable in the upper scope (declared in the map method), and then you declare another item variable in the lower scope, in the onClick handler.
To fix your issue - don't use the same variable name in the scope twice, it's bug and error prone. A linter would catch that, setup one if you don't have one already.
The variable in the onClick handler with always be the Event.
If you want to send the item variable to the onClick use:
<li
onClick={(ev) => console.log(item)}
>
{item}
</li>
Here I don't redeclare item, so it works as you want it.
Don't worry about the fact that this isn't perfectly optimised for now - you could use the useCallback hook to prevent that function from being created on every render, but it should not matter in small examples.
Pass like this onClick={()=> handleClick(item)} and define function
const handleClick = item =>{
console.log(item);
}
Improved Code
const Options = props => {
const handleClick = item =>{
console.log(item);
}
let [status, setStatus] = useState(false);
return (
<div className="options-container">
<div className="options-header" onClick={() => setStatus(!status)}>
<p>{props.name}</p>
</div>
<ul className={status ? "options-ul shown-ul" : "options-ul hidden-ul"}>
{props.items.map((item, index) => {
return (
<li
key={index}
value={item}
onClick={()=> handleClick(item)}
>
{item}
</li>
);
})}
</ul>
</div>
);
};
I'm working on a react project to learn react.
In a component's render method, when I use .map to iterate over values and return an array of components, everything works as expected.
<ol className="books-grid">
{
books && books.map((book, index) => {
if (book.shelf === shelf) {
return (
<Book key={book && book.id ? book.id : index} changeShelf={this.props.changeShelf} book={book} />
);
}
})}
</ol>
But when I use filter:
<ol className="books-grid">
{
books && books.filter((book, index) => {
if (book.shelf === shelf) {
return (
<Book key={book && book.id ? book.id : index} changeShelf={this.props.changeShelf} book={book} />
);
}
})}
</ol>
I get the error (which I've researched)
Uncaught (in promise) Error: Objects are not valid as a React child
I don't understand why filter is throwing this error vs map? Is there something unique to react and .map? Both return an array.
Array.filter does not allow you to transform the data into components. That is the job of Array.map.
You should instead filter first, then chain the map call afterward:
{
books && books
.filter(book => book.shelf === shelf)
.map((book, index) => {
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
If you want to avoid a second pass over your list of books, you can return null as well, though this is "less good" because you're forcing React to render null when it doesn't need to do any work at all:
{
books && books
.map((book, index) => {
if (book.shelf !== shelf) {
return null;
}
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
There is nothing unique to React and map() or filter().
In the first example when using map() you are returning an array of React components which are rendered in the DOM. You are transforming (mapping) each plain JavaScript object in the array into a React component. As a matter of fact, you are also going to return some undefined elements in the resulting array, if the condition book.shelf === shelf is falsy. Your array may look like [<Book />, <Book />, undefined, <Book />, undefined]. That's not such a big deal, since React won't render falsy values (null or undefined elements will just be skipped).
The second example won't return the same result (an array of React components), but an array of plain JavaScript objects (of type book). This is because no matter what are you returning from the filter function, it's going to be cast to a Boolean value - true or false and that value is going to decide if the current element is going to be filtered or not. The result of your .filter() function is going to be something like this (imagine shelf === 'Science'):
Original array: [{ shelf: "Science" }, { shelf: "Thrillers" }, { shelf: "Informatics" }]
Filtered array: [{ shelf: "Science" }]
As you can see, the items in the array won't be React components (<Book />) and React won't be able to render them in the DOM, thus the error it throws.
If you only want to use one pass over the array you can use reduce:
books && books
.reduce(
(all,book, index) => {
if (book.shelf !== shelf) {
return all;
}
return all.concat(
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
}
,[]
)
However; I think using filter and map makes for code that's easier to read.
The answer to me seems good but I needed to use includes() in order to work, also maybe a good idea to use toLowerCase():
{
books && books
.filter(book => book.shelf.toLowerCase().includes(shelf.toLowerCase()))
.map((book, index) => {
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
I also facing the same error. then, I also wrap my map method on filter method which helps me to solve the error and accept it as react valid child.
<ul className="menu">
{navigationLinks?.filter((eachNavigation) => {
if (eachNavigation.select.length < 1) {
return eachNavigation;
}
}).map((eachNavigation, index) => {
return (
<li key={index} className="menu__list menu__list--noSelect">
<a href={eachNavigation.link}>{eachNavigation.title}</a>
</li>
)
})
}
</ul>
<ul className="menu">
{navigationLinks?.filter((eachNavigation) => {
if (eachNavigation.select.length < 1) {
return eachNavigation;
}
}).map((eachNavigation, index) => {
return (
<li key={index} className="menu__list menu__list--noSelect">
<a href={eachNavigation.link}>{eachNavigation.title}</a>
</li>
)
})
}
</ul>
I have an array of objects.
Each object has a few strings and an array of strings.
I want to render out the array of strings part of the object with an unordered list.
Right now I'm using:
const renderOut = this.props.data.filter(obj => {
return this.state.keyToFind.includes(obj.name);
}).map((obj, idx) => {
return (
<div key={idx}>
<h2>{obj.name}</h2>
<ul>
<li>{obj.arrayItems}</li>
</ul>
</div>
);
});
The problem with the code above is that it renders out obj.arrayItems all in a row. For example...
[ "cup", "ball", "toy" ]
renders out as....
cupballtoy
but I'm trying to get it to look like:
cup
ball
toy
Is there a way I can render out obj.arrayItems one at a time?
Yes, create a map function to return the strings into DOM elements inside the ul. React does require a unique key prop to help keep track of these components, but for simplicity, in this case, you can use the array index.
{ obj.arrayItems.map( (item, i) => <li key={i} >{ item }</li> ) }
You can use a nested map function.
const renderOut = this.props.data.filter(obj => {
return this.state.keyToFind.includes(obj.name);
}).map((obj, idx) => {
return (
<div key={idx}>
<h2>{obj.name}</h2>
<ul>
{obj.arrayItems.map(item => <li key={item}>{item}</li>)}
</ul>
</div>
);
});