Adding items to an array in javascript - javascript

Admit it. Being new to JavaScript in 2018 is difficult. Coming from languages like C#, Java and Typescript(yeah subset of js..) where type safety is key, Javascript just keep f***** me over. I struggle with something simple like updating an array..
So I have this React component, where the state is defined like this:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
show: false,
shoes: []
};
}
....
...
}
The shoes is an array of undefined(?)
This array is passed to a stateless component which looks like this
const Shoelist = props => {
return (
<Wrapper>
{props.shoes.map((shoe, i) => (
<div key={i}>
<Shoe shoe={shoe} />
<Separator />
</div>
))}
</Wrapper>
);
};
I in my Form component, I have a method which is supposed to react(doh) on onClick methods. In this method I get a parameter with a new shoe to add in this list. This is very it stops for me in javascript - something which is faaaaairly easy in all other languages that we've being using for the past years..
I've tried several ways:
1#
addShoe(shoe) {
this.setState(state => {
const list = state.shoes.push(shoe);
return {
list
};
});
}
This results in an error: Uncaught TypeError: Cannot read property 'push' of undefined Do I need to define shoes as an Array? I thought the [] was enough
2#
I googled, I do that alot. I found one blog post saying something about react-addons-update. I installed this by running yarn add and code looks like this:
addShoe(shoe) {
this.setState(update(this.state, { shoes: { $push: [shoe] } }));
}
which results in Uncaught Error: update(): expected target of $push to be an array; got undefined.
Help! How difficult can this be?
EDIT
I pass this method into another component like this:
<ShoeModal onClose={this.addShoe} />
in the ShoeModal component this is bound to a onClick method:
<FinishModalButton
onClick={this.props.onClose.bind(this, this.state.shoe)}>
....
</FinishModalButton>
ShoeModal.propTypes = {
onClose: PropTypes.func.isRequired
};

You can do it this way:
this.setState({
shoes: [...this.state.shoes, newShoe]
})
... adds all elements from this.state.shoes

With your updates we can see that the issue is the way the addShoe callback is passed. It's being invoked as a function instead of a method of an object, so it loses context.
Change it to:
<ShoeModal onClose={this.addShoe.bind(this)} />
or
<ShoeModal onClose={shoe => this.addShoe(shoe)} />
In addition, .push returns the count of the array, so the following line won't give you what you expect:
const list = state.shoes.push(shoe);
See #merko's answer for a solution.

Firstly, your addShoe method is not an arrow function.
Using arrow functions because the context this is of the component.
Second, you are returning the object {list}. This sets the variable list in state.
Also push to the new list variable instead of mutating state.
Change your function to
addShoe = (shoe) => {
this.setState(state => {
let list = state.shoes;
list.push(shoe);
return {
shoes : list
};
});
}

Related

Uncaught Error: Objects are not valid as a React child (found: object with keys {nombre, email}) [duplicate]

In my component's render function I have:
render() {
const items = ['EN', 'IT', 'FR', 'GR', 'RU'].map((item) => {
return (<li onClick={this.onItemClick.bind(this, item)} key={item}>{item}</li>);
});
return (
<div>
...
<ul>
{items}
</ul>
...
</div>
);
}
everything renders fine, however when clicking the <li> element I receive the following error:
Uncaught Error: Invariant Violation: Objects are not valid as a React
child (found: object with keys {dispatchConfig, dispatchMarker,
nativeEvent, target, currentTarget, type, eventPhase, bubbles,
cancelable, timeStamp, defaultPrevented, isTrusted, view, detail,
screenX, screenY, clientX, clientY, ctrlKey, shiftKey, altKey,
metaKey, getModifierState, button, buttons, relatedTarget, pageX,
pageY, isDefaultPrevented, isPropagationStopped, _dispatchListeners,
_dispatchIDs}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from
the React add-ons. Check the render method of Welcome.
If I change to this.onItemClick.bind(this, item) to (e) => onItemClick(e, item) inside the map function everything works as expected.
If someone could explain what I am doing wrong and explain why do I get this error, would be great
UPDATE 1:
onItemClick function is as follows and removing this.setState results in error disappearing.
onItemClick(e, item) {
this.setState({
lang: item,
});
}
But I cannot remove this line as I need to update state of this component
I was having this error and it turned out to be that I was unintentionally including an Object in my JSX code that I had expected to be a string value:
return (
<BreadcrumbItem href={routeString}>
{breadcrumbElement}
</BreadcrumbItem>
)
breadcrumbElement used to be a string but due to a refactor had become an Object. Unfortunately, React's error message didn't do a good job in pointing me to the line where the problem existed. I had to follow my stack trace all the way back up until I recognized the "props" being passed into a component and then I found the offending code.
You'll need to either reference a property of the object that is a string value or convert the Object to a string representation that is desirable. One option might be JSON.stringify if you actually want to see the contents of the Object.
So I got this error when trying to display the createdAt property which is a Date object. If you concatenate .toString() on the end like this, it will do the conversion and eliminate the error. Just posting this as a possible answer in case anyone else ran into the same problem:
{this.props.task.createdAt.toString()}
I just got the same error but due to a different mistake: I used double braces like:
{{count}}
to insert the value of count instead of the correct:
{count}
which the compiler presumably turned into {{count: count}}, i.e. trying to insert an Object as a React child.
Just thought I would add to this as I had the same problem today, turns out that it was because I was returning just the function, when I wrapped it in a <div> tag it started working, as below
renderGallery() {
const gallerySection = galleries.map((gallery, i) => {
return (
<div>
...
</div>
);
});
return (
{gallerySection}
);
}
The above caused the error. I fixed the problem by changing the return() section to:
return (
<div>
{gallerySection}
</div>
);
...or simply:
return gallerySection
React child(singular) should be type of primitive data type not object or it could be JSX tag(which is not in our case). Use Proptypes package in development to make sure validation happens.
Just a quick code snippet(JSX) comparision to represent you with idea :
Error : With object being passed into child
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--item object invalid as react child--->>>{item}</div>;
})}
</div>
Without error : With object's property(which should be primitive, i.e. a string value or integer value) being passed into child.
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--note the name property is primitive--->{item.name}</div>;
})}
</div>
TLDR; (From the source below) : Make sure all of the items you're rendering in JSX are primitives and not objects when using React. This error usually happens because a function involved in dispatching an event has been given an unexpected object type (i.e passing an object when you should be passing a string) or part of the JSX in your component is not referencing a primitive (i.e. this.props vs this.props.name).
Source - codingbismuth.com
Mine had to do with forgetting the curly braces around props being sent to a presentational component:
Before:
const TypeAheadInput = (name, options, onChange, value, error) => {
After
const TypeAheadInput = ({name, options, onChange, value, error}) => {
I too was getting this "Objects are not valid as a React child" error and for me the cause was due to calling an asynchronous function in my JSX. See below.
class App extends React.Component {
showHello = async () => {
const response = await someAPI.get("/api/endpoint");
// Even with response ignored in JSX below, this JSX is not immediately returned,
// causing "Objects are not valid as a React child" error.
return (<div>Hello!</div>);
}
render() {
return (
<div>
{this.showHello()}
</div>
);
}
}
What I learned is that asynchronous rendering is not supported in React. The React team is working on a solution as documented here.
Mine had to do with unnecessarily putting curly braces around a variable holding a HTML element inside the return statement of the render() function. This made React treat it as an object rather than an element.
render() {
let element = (
<div className="some-class">
<span>Some text</span>
</div>
);
return (
{element}
)
}
Once I removed the curly braces from the element, the error was gone, and the element was rendered correctly.
For anybody using Firebase with Android, this only breaks Android. My iOS emulation ignores it.
And as posted by Apoorv Bankey above.
Anything above Firebase V5.0.3, for Android, atm is a bust. Fix:
npm i --save firebase#5.0.3
Confirmed numerous times here
https://github.com/firebase/firebase-js-sdk/issues/871
I also have the same problem but my mistake is so stupid. I was trying to access object directly.
class App extends Component {
state = {
name:'xyz',
age:10
}
render() {
return (
<div className="App">
// this is what I am using which gives the error
<p>I am inside the {state}.</p>
//Correct Way is
<p>I am inside the {this.state.name}.</p>
</div>
);
}
}
Typically this pops up because you don't destructure properly. Take this code for example:
const Button = text => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
We're declaring it with the = text => param. But really, React is expecting this to be an all-encompassing props object.
So we should really be doing something like this:
const Button = props => <button>{props.text}</button>
const SomeForm = () => (
<Button text="Save" />
)
Notice the difference? The props param here could be named anything (props is just the convention that matches the nomenclature), React is just expecting an object with keys and vals.
With object destructuring you can do, and will frequently see, something like this:
const Button = ({ text }) => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
...which works.
Chances are, anyone stumbling upon this just accidentally declared their component's props param without destructuring.
Just remove the curly braces in the return statement.
Before:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return {rows}; // unnecessary
}
After:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return rows; // add this
}
I had the same problem because I didn't put the props in the curly braces.
export default function Hero(children, hero ) {
return (
<header className={hero}>
{children}
</header>
);
}
So if your code is similar to the above one then you will get this error.
To resolve this just put curly braces around the props.
export default function Hero({ children, hero }) {
return (
<header className={hero}>
{children}
</header>
);
}
I got the same error, I changed this
export default withAlert(Alerts)
to this
export default withAlert()(Alerts).
In older versions the former code was ok , but in later versions it throws an error. So use the later code to avoid the errror.
This was my code:
class App extends Component {
constructor(props){
super(props)
this.state = {
value: null,
getDatacall : null
}
this.getData = this.getData.bind(this)
}
getData() {
// if (this.state.getDatacall === false) {
sleep(4000)
returnData("what is the time").then(value => this.setState({value, getDatacall:true}))
// }
}
componentDidMount() {
sleep(4000)
this.getData()
}
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { this.state.value } </p>
)
}
}
and I was running into this error. I had to change it to
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { JSON.stringify(this.state.value) } </p>
)
}
Hope this helps someone!
If for some reason you imported firebase. Then try running npm i --save firebase#5.0.3. This is because firebase break react-native, so running this will fix it.
In my case it was i forgot to return a html element frm the render function and i was returning an object . What i did was i just wrapped the {items} with a html element - a simple div like below
<ul>{items}</ul>
Just remove the async keyword in the component.
const Register = () => {
No issues after this.
In my case, I added a async to my child function component and encountered this error. Don't use async with child component.
I got this error any time I was calling async on a renderItem function in my FlatList.
I had to create a new function to set my Firestore collection to my state before calling said state data inside my FlatList.
My case is quite common when using reduce but it was not shared here so I posted it.
Normally, if your array looks like this:
[{ value: 1}, {value: 2}]
And you want to render the sum of value in this array. JSX code looks like this
<div>{array.reduce((acc, curr) => acc.value + curr.value)}</div>
The problem happens when your array has only one item, eg: [{value: 1}].
(Typically, this happens when your array is the response from server so you can not guarantee numbers of items in that array)
The reduce function returns the element itself when array has only one element, in this case it is {value: 1} (an object), it causes the Invariant Violation: Objects are not valid as a React child error.
You were just using the keys of object, instead of the whole object!
More details can be found here: https://github.com/gildata/RAIO/issues/48
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class SCT extends Component {
constructor(props, context) {
super(props, context);
this.state = {
data: this.props.data,
new_data: {}
};
}
componentDidMount() {
let new_data = this.state.data;
console.log(`new_data`, new_data);
this.setState(
{
new_data: Object.assign({}, new_data)
}
)
}
render() {
return (
<div>
this.state.data = {JSON.stringify(this.state.data)}
<hr/>
<div style={{color: 'red'}}>
{this.state.new_data.name}<br />
{this.state.new_data.description}<br />
{this.state.new_data.dependtables}<br />
</div>
</div>
);
}
}
SCT.propTypes = {
test: PropTypes.string,
data: PropTypes.object.isRequired
};
export {SCT};
export default SCT;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
If you are using Firebase and seeing this error, it's worth to check if you're importing it right. As of version 5.0.4 you have to import it like this:
import firebase from '#firebase/app'
import '#firebase/auth';
import '#firebase/database';
import '#firebase/storage';
Yes, I know. I lost 45 minutes on this, too.
I just put myself through a really silly version of this error, which I may as well share here for posterity.
I had some JSX like this:
...
{
...
<Foo />
...
}
...
I needed to comment this out to debug something. I used the keyboard shortcut in my IDE, which resulted in this:
...
{
...
{ /* <Foo /> */ }
...
}
...
Which is, of course, invalid -- objects are not valid as react children!
I'd like to add another solution to this list.
Specs:
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-scripts": "^1.0.17",
"redux": "^3.7.2"
I encountered the same error:
Uncaught Error: Objects are not valid as a React child (found: object
with keys {XXXXX}). If you meant to render a collection of children,
use an array instead.
This was my code:
let payload = {
guess: this.userInput.value
};
this.props.dispatch(checkAnswer(payload));
Solution:
// let payload = {
// guess: this.userInput.value
// };
this.props.dispatch(checkAnswer(this.userInput.value));
The problem was occurring because the payload was sending the item as an object. When I removed the payload variable and put the userInput value into the dispatch everything started working as expected.
If in case your using Firebase any of the files within your project.
Then just place that import firebase statement at the end!!
I know this sounds crazy but try it!!
I have the same issue, in my case,
I update the redux state, and new data parameters did not match old parameters, So when I want to access some parameters it through this Error,
Maybe this experience help someone
My issue was simple when i faced the following error:
objects are not valid as a react child (found object with keys {...}
was just that I was passing an object with keys specified in the error while trying to render the object directly in a component using {object} expecting it to be a string
object: {
key1: "key1",
key2: "key2"
}
while rendering on a React Component, I used something like below
render() {
return this.props.object;
}
but it should have been
render() {
return this.props.object.key1;
}
If using stateless components, follow this kind of format:
const Header = ({pageTitle}) => (
<h1>{pageTitle}</h1>
);
export {Header};
This seemed to work for me
Something like this has just happened to me...
I wrote:
{response.isDisplayOptions &&
{element}
}
Placing it inside a div fixed it:
{response.isDisplayOptions &&
<div>
{element}
</div>
}

Trying to add to React State Array using spread or push, Error: TypeError: array is not iterable

I've created an array in the initial state object and I'm trying to add items to the array whenever a checkbox is checked and remove the item whenever it's unchecked.
The function I'm using is pretty simple and I don't see any reason why it shouldn't work. I'm using setState to spread the items in the array and add the new item on the end, but I keep getting a TypeError on the browser saying that the array is not iterable. I've tried using typeof to see if it's an array and it results in undefined.
Click here to see the error I receive
My code is:
class Step3 extends React.Component {
constructor() {
super()
this.state = {
missingList: [],
}
}
handleChangeArray = e => {
this.setState({
missingList: [...this.state.missingList, e.target.name]
})
console.log(this.state.missingList)
}
render() {
const { currentProductType, ProductSubtype } = this.props
return (
<div className="row mt-4">
<h4 className='subtitle'>Missing List</h4>
{
systemData[`${currentProductType}`][`${ProductSubtype}`].parts.map((item, _id) =>
<CustomCheckbox
col={3}
key={_id}
name={item}
text={item}
onChange={this.handleChangeArray}
/>
)
}
</div>
)
}
}
The systemData points to a nested object where the list comes from and uses the map method to render the checkbox
Well it is because the handleChange is not bound to the Step3 class.
So, when CustomCheckBox invokes the handleChange, the context of this would be referring to CustomCheckBox and not Step3. So, this.missingList is correctly undefined. It's simply the scope of this.
Check this minimal reproducer of your use case. Commenting the bind line will result in the error you have posted.
https://codesandbox.io/embed/upbeat-smoke-q0iei?fontsize=14&hidenavigation=1&theme=dark
Further reading: https://reactjs.org/docs/handling-events.html

"TypeError: Cannot read property 'map' of undefined" but can console log result completely fine?

I have two pieces of state currently. One is an empty array called "subproducts", the other is called "additionalprod".
subproducts is an array which pulls information from my backend database using Axios. It is mounted inside of a componentDidMount for the API call.
additionalprod(uct) is a piece of state that gets its value from a prop from a parent, it is a number, in this case for my testing purposes, the number is 5. Number 5 links to a small array containing 3 properties. Product, img_url and id.
When I console.log the exact code
console.log(this.state.subproducts[5])
I get the response I want, that specific array, so I am 100% targeting it correctly. It currently has 2 arrays inside of it with their own props.
However, when I try and map through that state, using the code
{this.state.subproducts[5].map((product) => {
<h1>{product.img_url}</h1>
})}
I get the error
TypeError: Cannot read property 'map' of undefined
Anybody know whats wrong?
Code in question:
import React from "react"
import axios from "axios"
class SubProducts extends React.Component {
constructor(props) {
super(props)
this.state = {
subproducts: [],
stage: "3",
selected: this.props.selected,
additionalprod: this.props.additionalprod,
}
}
componentDidMount() {
axios.get("http://localhost/api/subproducts.php").then((res) => {
this.setState({ subproducts: res.data })
console.log(this.state.subproducts[5])
})
}
render() {
return (
<div>
{this.state.subproducts[this.state.additionalprod].map((product) => {
;<h1>{product.img_url}</h1>
})}
</div>
)
}
}
export default SubProducts
EDIT:
After using Sean Rileys fix, I am trying to console log {product} by itself and now I am being hit with this in the console, still can't get it to output though by putting {product.img_url}, there is an error, 'Failed to compile'
On the initial render, this.state.subproducts is empty so it can't run the map function on the array. It is only after the API call that the array is filled with data.
This is a very common React issue which was a very simple solution of checking that the array isn't empty before running the method on it.
{this.state.subproducts && this.state.subproducts[this.state.additionalprod].map((product) => {
<h1>{product.img_url}</h1>
})}
this.state.subproducts &&... is just checking if the array returns truthy which it will if it isn't empty and then, if so, it will run the next bit of code.
Another option is "optional chaining" which pretty much does the same thing.
{this.state.subproducts[this.state.additionalprod]?.map((product) => {
<h1>{product.img_url}</h1>
})}
Notice the question mark right before map ?.map. By adding the question mark after the array that we're calling the function on, it will only run if the array is defined otherwise it will return undefined but you won't get an error.
You have to wait for a response before render, just add checking on length for your array:
componentDidMount() {
this.setState({additionalprod: this.props.additionalprod}, () => {
axios.get("http://localhost/api/subproducts.php").then((res) => {
this.setState({ subproducts: res.data })
console.log(this.state.subproducts[5])
});
}
}
render() {
return (
<div>
{this.state.subproducts.length > 0 ?
this.state.subproducts[this.state.additionalprod].map((product) =>
<h1>{product.img_url}</h1>
}) : null
</div>
)
}

Loop through objects React

My React Component has the following render
componentWillMount () {
var url = 'https://gist.githubusercontent.com/hart88/198f29ec5114a3ec3460/raw'
Request.get(url)
.then(data => {
this.setState({cakes: data.text});
})
}
render() {
return(
<div>
{this.state.cakes} //prints this ok
{
this.state.cakes.map(cake =>{ // error here
return <p>{cake.title}</p>;
})
}
</div>
);
}
i am trying to loop through this.state.cakes which is an array of objects.
What am i doing wrong here ?
Update - an abbreviated example of this.state.cakes:
[
{
"title": "Lemon cheesecake",
"desc": "A cheesecake made of lemon",
"image":"https://s3-eu-west-1.amazonaws.com/s3.mediafileserver.co.uk/carnation/WebFiles/RecipeImages/lemoncheesecake_lg.jpg"
},
{
"title":"Banana cake",
"desc":"Donkey kongs favourite",
"image":"http://ukcdn.ar-cdn.com/recipes/xlarge/ff22df7f-dbcd-4a09-81f7-9c1d8395d936.jpg"
}
]
Thanks
If the state is set as the resutl of a fetch you might not be able to access the data immediately due to the async operation. You can catch this by inspecting the state and if it has no length return a message or a spinner component to indicate the data's on its way.
Once state.cakes is updated with the data from the fetch operation the component will re-render.
constructor(props) {
super(props);
this.state = { cakes: [] };
}
componentDidMount() {
fetch('/cakes')
.then(res => res.json())
.then(cakes => this.setState({ cakes }));
}
render() {
if (!this.state.cakes.length) return <Spinner />
return (
<div>
{this.state.cakes.map(cake => {
return <p>{cake.title}</p>;
})};
</div>
)
}
As the others have mentioned it's also good practice to add keys to your iterated elements.
Here:
{this.state.cakes.map((cake, i) => <p key={i}>{cake.title}</p>;)}
Do not forget to add the key attribute.
Ps: It would be better to use an unique Id instead of the array index. SO if you have an id for each array item, better write:
{this.state.cakes.map(cake => <p key={cake.id}>{cake.title}</p>;)}
I believe that you've used curly braces (understandably) where React actually requires parentheses. Since you're getting the data from a fetch, be sure to set your constructor with a preliminary cakes object as well. Try this:
constructor(props) {
super(props)
this.state = {
cakes: []
}
}
render() {
if (this.state.cakes.length > 0){
return(
<div>
{
this.state.cakes.map(cake => (
return <p>{cake.title}</p>;
))
}
</div>
);
}
return null
}
The issue is that the component is rendering and you're telling it to do something with an array called this.state.cakes, but this.state.cakes hasn't been defined yet because the fetch hasn't returned yet. Setting your constructor like this passes an empty array to the render so it doesn't freak out, and then when your data loads and your state updates, it will re-render with your data.
The reason {this.state.cakes} was, on its own, rendering just fine is because for the first split second of the component's existence, that value was undefined, which means that React basically just ignored it - once the data loaded, it rendered. However, the map method failed because you cannot pass an undefined array into map.
And as Ha Ja suggested, you should probably add a key attribute to the <p> elements.
You missed brackets inside of your map
{this.state.cakes.map(cake =>{ // errors here
return <p> {cake.title} </p>;
})}

Get user object properties from Firebase in React

I have a user db in Firebase that uses the unique user id as the Key and within that key/value pairs like name: 'jane doe', email: 'jane#doe.com', etc. What I want to do is map the object and get the key values within. I'm able to get the Key (user id), but not the object key value pairs. Here's my code:
export default class Home extends Component {
constructor(props) {
super(props);
var users = {};
this.state = { users };
}
componentDidMount() {
const dbRoot = firebaseDb.database().ref().child('users');
dbRoot.on('value', snap => {
const dbUsers = snap.val();
this.setState({
users: dbUsers
});
});
}
render() {
return (
<div>
<div>
{Object.keys(this.state.users).map(function(user, i) {
return <div key={i}>Key: {user}, Value: {user.name}</div>;
})}
</div>
</div>);
}
}
user.name comes back undefined. I've tried using this.state.users.name but I get a "state is undefined" message. Can someone point me in the right direction. Thanks!
You have two main problems with your code. The first is that you cannot access this.state inside the callback of your map() function because this is bound to that function and not to the whole class. There are a couple of ways to keep the this variable bound to the class but the cleanest way is to use the Arrow function syntax.
Once you do that, you will still have a problem. You are using Object.keys which will only map over the keys of your result, but you are treating it as if it will pass the whole user object to the function. In order to access the user object inside of your callback function, you will need to use the bracket notation with the key.
With those two things in mind, your code should look something like this:
{Object.keys(this.state.users).map(key => {
return <div key={key}>Key: {key}, Value: {this.state.users[key].name}</div>;
})}

Categories