How to get value from array of objects? - javascript

I have an react app, and using redux and props to get array of objects into my component, and i am getting them. But i can't access particular property inside of one of objects that are in that array.
With this:
console.log(this.props.users)
I get listed array with all objects inside it. But when i need to access particular object or property of that object, for example:
console.log(this.props.users[0])
console.log(this.props.users[0].name)
I am getting error:
Cannot read property '0' of undefined
But when I iterate through array with map() method i have access to it, it works. Why can't i access it normally?

You are trying to access properties of this.props.users before it has loaded. Your component renders without waiting for your data to fetch. When you console.log(this.props.users) you say that you get an array, but above that, it probably logs undefined at least once when the component renders before this.props.users has loaded.
You have a couple of options. You can either do this at the very top of your render method to prevent the rest of the code in the method from executing:
if (!this.props.users) return null;
Once the data is fetched and props change, the render method will be called again.
The other option is to declare a default value, of an empty array for users in your reducer.

Might be when you are executing that line this.props.users is undefined. Check the flow where you have added console.log(this.props.users[0])

const App = () => {
const example = () => {
const data =[{id:1 ,name: "Users1", description: "desc1"},
{id:2 ,name: "Users2", description: "desc2"}];
return (
<div>
{data.map(function(cValue, idx){
console.log("currentValue.id:",cValue.id);
console.log("currentValue.name:",cValue.name);
console.log("currentValue.description:",cValue.description);
return (<li key={idx}>name = {cValue.name} description = {cValue.description}</li>)
})}
</div>
);
}
return(
<p style = {{color:'white'}}>
{example()}
</p>
);
}
export default App;

Related

Having trouble displaying object property

I'm building a small pokedex using PokeAPI. I'm retrieving data from an endpoint and building my own object. The function that I've built for the latter runs on page load as follows:
$: pokemon = []
//fetch pokemon from PokeAPI
function getPokemon(){
let index = 0
fetch("https://pokeapi.co/api/v2/pokemon?limit=151") //
.then(response => response.json()) //
.then(allpokemon => {
allpokemon.results.map((poke) =>
pokemon = [...pokemon, {
index: index++,
name: poke.name
}]
)})
}
//populate pokemon array on page load
getPokemon()
I'm able to console log the object and traverse it in devtools, and also able to JSON stringify it and display within html. But whenever I try to retrieve a specific attribute, I receive a 500 error. For instance, I'm trying to display the current pokemons name in an h1 tag :
<h1>{pokemon[currentPokemon].name</h1> // this doesn't work
whereas
<h1>{pokemon[currentPokemon]</h1> //this does
This can't be too complicated of a fix, I just don't know what I'm missing, because I've been traversing objects for what I thought was the correct way for years now. Thank you.
This line is missing a curly brace at the end
<h1>{pokemon[currentPokemon].name</h1>
Besides that pokemon will initially be an empty array, so independent of currentPokemon the value will be undefined and accessing the key name will give the error Cannot read properties of undefined (reading 'name') which could be solved by adding a questionmark (Optional chaining)
<h1>{pokemon[currentPokemon]?.name}</h1>
There's no need for the $:, just let pokemon = []
.map() returns an array, so instead of setting pokemon inside the function better assign it the result. The index would be available as second parameter inside .map()
.then(allpokemon => {
pokemon = allpokemon.results.map((poke, index) => {
return {
name: poke.name, // or ...poke, to keep all fields
index
}
})
Writing the index in the data might not be neccessary if an #each loop is used to display the data
{#each pokemon as p, index}
<p>
{index} - {p.name}
</p>
{/each}
Try this:-
<h1>{pokemon[currentPokemon]['name']}</h1>

Array not rendering as a collection of children Reactjs

I have a hook, Prompt. one of the props is an Object containing some field names as keys.
I create a form using these fields and some <input>s. This is what the hook Fields returns.
It is rendered from an array. I log this array at log-A. Here is what I get:
This makes me believe that this is an array of react components.
There is a function that I am trying to make that gets the results of this form.
To do this, I want to go through the children of the <div> with ref={fieldsRef}. Since this just contains a list of react components, I thought I could just get the children of it and use React.Children.toArray().
This does not work.
const Prompt = (props) => {
//...
const Fields = () => {
let fieldKeys = Object.keys(props.fields);
let fields = [];
for ( let fieldKey of fieldKeys ) {
fields.push(
<div key={fieldKey.toString()}>
<div className={"field-name"}>
{props.fields[fieldKey]}
</div>
<input spellCheck="false"/>
</div>
);
}
console.log(fields); //log-A
return (
<div ref={fieldsRef}>
{fields}
</div>
);
}
const getForm = () => {
console.log(fieldsRef); //log-B
let current = fieldsRef.current.children;
let children = React.Children.toArray(current);
return 0;
}
//...
}
at log-B, the output is a div. It's current has children that are in a .
I get an error when trying to access the children of this.
Uncaught Error: Objects are not valid as a React child (found: [object HTMLDivElement]). If you meant to render a collection of children, use an array instead.
What am I doing wrong, to not be able to access these children and convert to an array using React.Children.toArray(<children>)?
It looks like getForm() is considering {fields} itself to be an object, rather than the array you are trying to reference. Try moving your fields variable declaration into the Prompt() scope instead of the nesting it under Fields().
Also, consider using the React Developer Tools Extension for Chrome, it can really help with debugging issues with props and hooks.

An array with objects, within an object is undefined

I have an array that contains objects, inside an object.
I can console.log the first object and the array, but when i try to access the objects within the array or use the map-function on the array i get an error that says "Can't read property of undefined".
I have thoroughly searched SO and other sites for similar problems and found some but no answers seems to work for me.
The object looks like this:
{
answers: [{…}],
createdAt: "2019-01-23T10:50:06.513Z",
nested: {kebab: "jjjj", sås: 2, sallad: "kkk"},
text: "weaxcc",
/* etc... */
}
And i can access it using: this.state.data
I want to access objects inside the answers-array like:
this.state.data.answers[0].text
or even :
this.state.data.answers.map().....
But that gives me 'Cannot read property '0' of undefined. The answers-array is not empty.
Any help is appreciated!
EDIT
This is how the objects ends up in my state.
getQuestionFromDb = () => {
axios.get(`http://localhost:3000/questions/${this.state.id}`)
.then(res => this.setState({
data: res.data
}));
};
This function is called in the ComponentDidMount()-method.
Here is my render function (the console.log is causing the error):
render() {
return (
<div className="main-content">
<h2>{this.state.data.text} </h2>
{console.log(this.state.data.answers[0].text)}
<p>Introducing <strong>{this.state.id}</strong>, a teacher who loves teaching courses about <strong>{this.state.id}</strong>!</p>
<input
type="text"
onChange={e => this.setState({ message: e.target.value })}>
</input>
<button onClick={() => {this.handleAnswerPost(this.state.message)}}>Answer</button>
</div>
);
}
}
componentDidMount is getting called when your component becomes part of the DOM but the call you do to populate your state is async due to XHR, which may take 100-300ms more to get the data in your component, so this.state.data.answers won't be available in the initial render() cycle.
since you mentioned using a loop, I suggest setting an initial state shape like
this.state = {
data: {
answers: []
}
}
your initial render won't have anything to loop but as soon as it resolves the data and sets the new state, it will render correctly.
alternatively you can
return this.state.data.answers.length ? loopItemsHere : <div>Loading..</div>
obviously, loopItemsHere can be anything you write to show the answers.
This might not be working when data doesnt contain answers[] at the very first mount for a component.
You may wanna check for your array's existance as following:
const { data } = this.state;
data.hasOwnProperty('answers') && console.log(data.answers[0]);
The reason we can't access object key in Javascript, usually it is because the variable/value is not the type of Object. Please ensure that this.state.data type is Object. You can try to console.log( typeof this.state.data ). We should expect that it is output object. If the type is string, you should parse it first with JSON.parse().
There may be a number of reasons that could cause the data object not to be shown:
The object was not in state at the time of it being called. This may be caused by an async operator not loading in the data. E.g. if you are requesting for the object from the database, chances are that at the time of making a call to retrieve the data (this.state.data), the response had not been given.
The object may not have been parsed into string. Try running console.log(typeof this.state.data). If the output is string, then you may have to parse it. If the output is undefined, then point one is valid

React- FLUX - Pass Object as Param - Undefined

I have a simple react app, I get a list of contacts from a web api and i want to display them. Each contact has a name, last name, phone etc
My app class gets the contacts then renders as
render(){
var contact= this.state.contacts[0];
return( <div><Contact label='First Contact' person={contact}/></div>
)
}
Then in my Contact class
render(){
return(
<div> {this.props.label} : {this.props.person.Name}</div>)
}
When I debug on chrome, I see that person is passed as prop, object has all the parameters, however when I run the code, on the {this.props.person.Name} I get error
Cannot read property Name of undefined
If I remove that, {this.props.label} is displayed without issue. So I can pass text as prop but not the object.
Any idea?
Edit: I can also pass the Name property as
Contact personName= {contact.Name}/>
This works but not passing the contact as object and then reading properties in the render of
my guess is (since you're using flux) that upon loading the page (initial load) the contacts on your state represents an empty array.
Try
var contact= this.state.contacts[0] || {};
and for some good tips => don't use vars, use const :-)
let your component listen to your flux store:
Make sure your flux store holds an addChangeListener and removeChangeListener function that you can call in your component so your component gets updated automatically
componentDidMount(){
myStore.addChangeListener(this._onChange);
}
componentWillUnmount(){
myStore.removeChangeListener(this._onChange);
}
_onChange = () => {
this.setState({contacts: myStore.getContacts()});
}

Attempt to render state value from ajax call in componentDidMount failing (React)

I've made an AJAX call in React with Axios, and I'm a bit confused about how the response is dealt with.
Here is my code:
componentDidMount() {
axios.get('https://jsonplaceholder.typicode.com/users')
.then( res => {
const users = res.data
this.setState({ users });
});
}
render() {
console.log(this.state.users)
return (
<div>
{this.state.users.map( user => <p>{user.name}</p>)}
</div>
)
}
When I console.log the results, two values are returned:
[] - the empty array assigned to the initial state
The expected array from the API.
This presumably can be explained by the state value being returned once on initialisation, and then again with componentDidMount.
My problem arises in how I access the this.state.users value. Obviously any time I access this, I want the values returned from the AJAX response, but if, for example I try to render the name property from the first object in the array...
{this.state.users[0].name}
...it attempts to access [], and thus returns nothing.
However, if I try to iterate through the arrays elements...
{users.map( user => <p>user.name</p> )}
...it returns the values as expected.
I don't know why it works with the second example but not the first. Surely if the first is attempting to pull data from the initial state value (an empty array), then when mapping through this.state.users it would also be attempting to map through an empty array and return nothing, or an error.
I'm obviously not fully understanding the React rendering process here and how componentDidMount works in the component lifecycle. Am I wrong in trying to assign the response value directly to state?
Would appreciate if anyone could help to clear this up.
When you use map() on this.state.users initially, it will do so over an empty array (which won't render anything). Once the response arrives, and this.setState({ users }) is called, render() will run again. This time, the array of users is no longer empty and the map() operation will be performed on each of the users in the array.
However, when you use {this.state.users[0].name} initially on the empty array, you're trying to access the name property of an object that doesn't yet exist — which will result in an error. To prevent the error, you could do a check before accessing the properties of the object:
{this.state.users.length > 0 && this.state.users[0].name}
Now it will behave the same way as in the first example. I.e. the first render() won't actually do anything, since this.state.users.length = 0. But as soon as this.setState() is called with new users, render() will run again, and this time this.state.users[0].name is available.
There is another way with constructor. just add your state with empty array users as-
constructor(props) {
super(props);
this.state = {
users: []
};
}
With this component first rendered with empty users array. When you trigger state update, component rerender with new array.

Categories

Resources