I want to render Object "genres" but my console returns to me :
Error: Objects are not valid as a React child (found: object with keys {genre}).
If you meant to render a collection of children, use an array instead.
My code
function Movie({title,summary,year,poster,genres}){
return(
<div className="movie">
{genres.map(genre=>{return {genre}})} //doesn't work
{genres.map(genre=>{return <li>{genre}</li>})}
</div>
)
}
the first code
{genres.map(genre=>{returns {genre}})}
doesn't work
But the second code below works well.
{genres.map(genre=>{returns <li>{genre}</li>})}
What's the difference between these two things?
This line
{genres.map(genre=>{returns {genre}})}
returns an object, out of each item in the array: { genre: 'drama' }
However, this other line
{genres.map(genre=>{returns <li>{genre}</li>})}
The { inside the JSX for li won't turn it into an Object, but will access its value: <li>drama</li>
The first code returns an object with key "genre" which its value is the genere value, and react can't render it.
The second one return the value of genre, because the braces here aren't an object braces but the correct syntax of putting variables inside html in react.
If you want to return the value without li, do: {genres.map(genre=> genre)}
Related
This is a React Component which was given an array of objects(main_object) in which one of it's elments was another array of objects(secondary_object). When printing the main object in console.log the array is visible but when trying to print the array of secondary objects it returns undefined but if I access another variable of the main object it returns it.
Code:
render(){
const fleets = this.props.fleets;
console.log(fleets[1]);//works
console.log(fleets[1].name);//works
console.log(fleets[1].ships);//undefined
}
Output:
Console Output
Edit: Used my actual code instead of example code
It is difficult to recreate your environment. Here I mimicked the this by referring the global window object. And, as you can see, it works in the way that the .ships property is listed as the array it actually is.
window.props={fleets:[
{},
{f_faction:1,
id:1,
members:["one"],
name:"Capital Fleet",
owners:["owner1"],
ships:[{a:1,b:2},{a:5,b:4},{a:7,b:8}],
x:789, y:2897, z:-23}
]};
function render(){
const fleets = this.props.fleets;
console.log(JSON.stringify(fleets[1]));//works
console.log(JSON.stringify(fleets[1].name));//works
console.log(JSON.stringify(fleets[1].ships));//lists array!
}
render()
#trincot's advice that console.log output of objects is asynchronous might be very relevant here. Therefore, if you want to get the content of an object at a specific time, you should take a "snapshot" of it. One way of doing that would be through JSON.stringify().
try this out
render(){
const fleets = this.props.fleets;
console.log(JSON.stringify(fleets[1]));
console.log(JSON.stringify(fleets[1].name));
console.log(JSON.stringify(fleets[1].ships));
}
By using JSON.stringify I was able to see that the problem was with the async creation of the array.
I fixed up all my async code and now it's working fine.
I'm not looking for the keys that this object contains but the key of the object itself (the key in the array containing the object).
I have this JSON:
{
"Object name (text)": {
"raw": "Some more text.",
},
"Another name": {
"raw": "Some other text.",
}
}
and would like to get "Object name (text)" for the first item.
My Vue code is:
<CustomComponent
v-for="object in objects"
:key="getKey(object)"
:object="object"
/>
I'm not sure if the getKey-method approach is how one is intended to get unique identifiers for iterating through the JSON array. Its code currently is:
getKey(object) {
return Object.keys(object)[0];
}
Now I'd like to somehow pass the name of the object to the CustomComponent ("Object name (text)" in the first case).
One temporary workaround that I intended to use until I find something more appropriate was getting the keys from the objects array like so:
:objectName="getObjectName(object)" and itemNumber: -1 in data and this method:
getObjectName(object) {
this.itemNumber = this.itemNumber + 1;
var objectName = Object.keys(this.objects)[this.itemNumber];
console.log("Object name: ", objectName);
}
However, the first line of this method causes it to run hundreds of times instead of only two times (why is that?; it works in the first 2 executions of the method and when commenting out that line) and I think this is unlikely the proper method to simply retrieve the object's name/key.
It also didn't work when putting the above code into the getKey method which would make more sense (and I had the code in that method before creating a separate method to debug). Then the key could be accessed in the component with this.$vnode.key However, it keeps being undefined. This might be a separate problem even though it could resolve this problem here as well - I might create a new question for it. It enters the methods "getKey" and "getObjectName" 6 times each even though it only renders two items on the page, like it should.
-> How to get the JSON object's key in JavaScript?
(Preferably from the object itself after iterating through a JSON array with a loop with Vue instead of only indirectly by checking the objects array.)
Edit: as a workaround I have now done this:
var keys = Object.keys(this.objects);
keys.forEach(element => {
this.objectsWithKeys.push({
object: this.objects[element],
key: element
});
});
<CustomComponent
v-for="objectWithKeys in objectsWithKeys"
:key="objectWithKeys.key"
:object="objectWithKeys.object"
>
</CustomComponent>
this.$vnode.key
This is solved, I used var objectsWithKeys = data[Object.keys(data)]; and {{ $vnode.key }}.
This is a static website with hundreds of pages. I need to render elements like a topnav or a newsletter or a strap of content and changing those contents periodically, from JS.
This is what I tried:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
const ids = ['newsletterEs', 'compartirEnFlex', 'infoArticulo', 'infoDeLaWebEnFlexIzq']
function renderComponents(objWithComp, idsArr){
return idsArr.map(function(id){
for(let component in objWithComp){
let arrOfIds = Object.keys(objWithComp);
arrOfIds.map(key => key)
if(id === key){
document.getElementById(id).insertAdjacentHTML('afterbegin', objWithComp[id])
}
}
})
}
renderComponents(components, ids);
Each id has its counterpart in the HTML structure. When I do this individually it works. However, I have to handle this in an elegant way (and there is no possibility for a JS framework like React in this project).
Thanks for the help!
When you run your code, you'll see the error Uncaught ReferenceError: key is not defined in the console.
That's because key in if(id === key) is not defined. The line arrOfIds.map(key => key) returns the same exact array as arrOfIds because Array.prototype.map "returns a new array populated with the results of calling a provided function on every element in the calling array."
Here, you don't assign that new array to a variable, so nothing happens. Even if it was, that new array would be a copy of arrOfIds because your mapping function (key) => key returns key for every key -- meaning that the output is the same as the input.
However, that's not an issue here. If I understand your question correctly, then this demo should show an example of what you're trying to accomplish. If that's what you want to achieve, then here's a solution:
First, you don't need to iterate for component in objWithComponent inside idArr -- you're already doing that in the idArr. You don't need the ids array either, because you can get the keys of the components from the components object using Object.keys().
Let's say your HTML looks something like this:
<div>
<div id="newsletterEs"></div>
<div id="compartirEnFlex"></div>
<div id="infoArticulo"></div>
<div id="infoDeLaWebEnFlexIzq"></div>
</div>
Then, using Object.keys(components) to get an array of the ids of the components that you have, you can map those to HTML tags. In fact, map is not necessary here because map returns a new array, and unless you need that array later, there's no reason to use map. Instead, you can use Object.prototype.forEach.
Here's what that would look like:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
function renderComponents(objWithComp) {
Object
.keys(components)
.forEach((id) => {
const element = document.getElementById(id)
const component = objWithComp[id]
if (component && element) {
element.insertAdjacentHTML('afterbegin', component)
}
})
}
renderComponents(components)
Then, when you call renderComponents, you can pass just the components argument, and only render the components for which divs with corresponding ids exist with an if statement.
I was learning React and wanted to use map() function. Here i the code:
class App extends Component {
state = { car:[
{carTitle:'Toyota'},
{carTitle: 'Honda'},
{cartitle: 'Chevrolet'}
] };
render() {
return ( <div>
{this.state.car.map((item, index)=>
<h1>{item.carTitle}</h1>
)}
</div> );
}
}
The question is why if I use
{this.state.car.map((item, index)=>
<h1>{item[index].carTitle}</h1>
I get an error. Since I have array of objects, I think it is logical to use {item[index].carTitle}. But if I use <h1>{item.carTitle}</h1>, all works ok. Why? Did I misunderstood something?
When you use .map() you need to pass a function as a callback, like the one you are already passing: .map( (item, index) => ... )
The first argument of this callback function, item, is the current element that is been processed in the array. index is the current position.
item already returns the content of each position of the array, in this case each object that you defined in car.
So, your second example, item[index].carTitle, is incorrect because you are trying to access an array position that doesn't exist inside each object.
Also, everytime you use a .map() in ReactJS applications, you need to specify a key to the element you are returning inside this function, in this case: <h1 key={index}>{{car.carTitle}}</h1>.
Golden tip: to make your code clearer and avoid confusion you should rename car to cars, as it is a list of cars, and instead of using item you should use car. It will make your code more legible and anybody that reads your code can understand it easier than if you are using generic names. So it would look something like this:
cars: [
{carTitle:'Toyota'},
{carTitle: 'Honda'},
{cartitle: 'Chevrolet'}
]
and:
{this.state.cars.map( (car, index) => {
<h1 hey={index}>{{car.carTitle}}</h1>
})}
Further readings:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://reactjs.org/docs/lists-and-keys.html
Clean Code, Book by Robert Cecil Martin
I have an array or objects named "properties"
This properties array has objects in the following format:
{
"some-unique-key-A": {
name:"someName1",
value: {
"some-unique-key-B": {
name:"someName11",
value : "someValue2"
}
}
}
}
Here we don't know what is the nesting-level of that key value pairs. We just know one thing, data is to be updated for key : "some-unique-key-X"
In this case how can we update the x-level nested data immutably?
Use a flat one level state of props ! Immutable is not easy to recurse, normalize your state with one level, easy to connect ;)
This is how I solved this
I created a recursive function which will accept a 'deep copy' (not shallow copy) of some object and the unique key in which value needs to be updated. It updation is done for the given key, it returns the updated object to be stored as the value of previous recursive object's value which was waiting for the result. In the end it just replaces the original state's object with the new one. I don't know if it mutates the state or not (perhaps it do mutates), but it works.