Currently trying to access the value within our Joblist object. We are unable to do joblist[0].company which should return google.
What is the best way to access the values of our object?
Our joblist object contains:
key: value
company: "Google"
datePosted: "2021-09-20T05:00:00.000Z"
location: "San Francisco"
position: "SWE Intern"
[{
"company" : "Google",
"datePosted" : "2021-09-20T05:00:00.000Z",
"id" : 0,
"location" :
"San Francisco",
"position" : "SWE Intern"
}]
App.js code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {joblist : []}
}
componentDidMount() {
firebase.database().ref("1h5GOL1WIfNEOtcxJVFQ0x_bgJxsPN5zJgVJOePmgJOY/Jobs").on("value", snapshot => {
let jobs = [];
snapshot.forEach(snap => {
// snap.val() is the dictionary with all your keys/values from the 'students-list' path
jobs.push(snap.val());
});
this.setState({ joblist: jobs });
});
}
render(){
const {joblist} = this.state;
// console.log(joblist[0].datePosted)
console.log(joblist);
return (
<div id='body'>
<Header />
<PhoneInput />
<TableHeader />
<Table joblist = {joblist}/>
<Footer />
</div>
);
}
}
export default App;
The one thing that jumps out to me is the commented out console.log(joblist[0].datePosted), which will give an error initially as the array will still be empty. To catch that you'll need to do:
if (joblist.length > 0) console.log(joblist[0].datePosted);
When you define your joblist, make sure that it is declared and defined as an empty array when your app initiates. this would prevent any issues of pushing items to it through the state manager.
Console logging the whole object from the snapshot val() may also yield inside to the object since Arrays are not natively supported, this could be the case if you are using UID's for each entry but are not unpacking the data first.
Related
I am currently working on my first react js project as a task list. It has a text input value which's value is passed into a function on submit to update a list element. My question is this, I want to make it that on submit it creates a new element which holds the value of the state. I have first tried using an object when it gave me an error that objects cannot be used for a child element. Which I opted by changing the state into an array but it still does not create a new element like I intended. Here is my code
import React, { Component } from "react";
import ReactDOM from "react-dom";
class Tests extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
object: [],
};
this.updateInputValue = this.updateInputValue.bind(this);
this.submit = this.submit.bind(this);
}
updateInputValue(evt) {
this.setState({
value: evt.target.value,
});
}
submit() {
this.setState({
object: this.state.value,
value: "",
});
}
render() {
return (
<div>
<label for="text">Text 1</label>
<input
type="text"
label="text"
onChange={(evt) => this.updateInputValue(evt)}
onSubmit={this.submit}
value={this.state.value}
/>
<br />
<button onClick={this.submit} style={{ height: 50, width: 60 }}>
Submit
</button>
<ul>
<li>{this.state.object}</li>
</ul>
</div>
);
}
}
export default Tests;
ReactDOM.render(<Tests />, document.getElementById("root"));
If there are any suggestions on how to improve this code I wrote I will read them and try to adjust my code to be better. Thanks
You are essentially overwriting the value of object on your setState. What you need to do, is keep the old array, and append the last item. Using spread syntax:
submit() {
this.setState({
object : [...this.state.object, this.state.value],
value : "",
});
}
This creates a new Array with your new item appended. Be careful not to mutate the state (in other words, change this.state):
// the above code is essentially doing this:
const newObject = [...this.state.object, this.state.value];
this.setState({ object: newObject });
// 👻 buggy version! This is mutating the state:
this.state.object.push(this.state.value);
On your render, you need to render a map of objects:
<ul>
{ this.state.object.map(item => <li>{ item }</li>) }
</ul>
The values of skillList is not showing in the drop down.it shows an empty dropdown.(the data fetched from api is pushed into the skillList array without any issue)
import React, { Component } from 'react'
let skillList = [];
export class DataCheck extends Component {
constructor(props) {
super(props)
this.state = {
skill: "",
skillId: "",
}
this. handleSkillChange=this. handleSkillChange.bind(this);
}
componentDidMount() {
// worker skill selection
fetch("http://localhost:3001/dataservices/getallskills")
.then (res=>res.json())
.then(res => {
console.log(res);
let temArray = {};
for (let i = 0; i < res.recordsets[0].length; i++) {
temArray["value"] = res.recordsets[0][i].SkillId;
temArray["label"] = res.recordsets[0][i].SkillTitle;
skillList.push(temArray);
console.log(skillList);
temArray = {};
}
})
.catch(function(error) {
console.log(error);
});
}
handleSkillChange(skill) {
this.setState({
skill: skill,
skillId: skill.value
});
}
render() {
return (
<div>
<form>
<select
value={this.state.skill}
onChange={this.handleSkillChange}
options={skillList}
placeholder="Skills"
/>
</form>
</div>
)
}
}
export default DataCheck
when I check the dev-tools, it shows like this:
options:Array
0:Object
label:"wood works"
value:6
(Array consists of 16 objects like this)
console says:
warning.js:36 Warning: Unknown prop options on tag. Remove this prop from the element.
Native select does not have an options props. You have to manually map the option tag:
<select
value={this.state.skill}
onChange={this.handleSkillChange}
placeholder="Skills"
>
{this.state.skillList.map((optionSkill) => (
<option value={optionSkill.value}>{optionSkill.label}</option>
)}
</select>
Currently you have added skillList as a normal variable with module scope. Even though you have changed skillList, the same will not be reflected in the UI because React does not detect this change. you will have to change skillList to a state variable for React.
this.state = {
skill: "",
skillId: "",
skillList: []
}
fetch("http://localhost:3001/dataservices/getallskills")
.then (res=>res.json())
.then(res => {
this.setState({
skillList: res.recordsets[0].map((recordSet) => ({
label: recordSet.SkillTitle,
value: recordSet.SkillId,
}))
});
});
You have to add option tag inside select. Loop array of items using .map and you will get your result.
This codepen can help you to make this work,
select example
Hope this can help you!
I'm fetching the data from an external API for food recipes, and getting the response in this format (JSON):
{
"count": 30,
"recipes": [
{
"publisher": "BBC Food",
"f2f_url": "http://food2fork.com/view/8c0314",
"title": "Chicken and cashew nut stir-fry",
"source_url": "http://www.bbc.co.uk/food/recipes/chickenandcashewnuts_89299",
"recipe_id": "8c0314",
"image_url": "http://static.food2fork.com/chickenandcashewnuts_89299_16x9986b.jpg",
"social_rank": 95.91061636245128,
"publisher_url": "http://www.bbc.co.uk/food"
},
{
"publisher": "Jamie Oliver",
"f2f_url": "http://food2fork.com/view/0beb06",
"title": "Roasted chicken breast with pancetta, leeks & thyme",
"source_url": "http://www.jamieoliver.com/recipes/chicken-recipes/roasted-chicken-breast-with-pancetta-leeks-and-thyme",
"recipe_id": "0beb06",
"image_url": "http://static.food2fork.com/466_1_1349094314_lrg2129.jpg",
"social_rank": 94.88568903341375,
"publisher_url": "http://www.jamieoliver.com"
},
{ ... more recipes ... }
]
}
And I'm trying to access that data and display, for the purpose of testing, just variables 'count', and the 'publisher' of the first recipe in array. This is my React code:
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { data: {} };
}
componentDidMount() {
fetch('https://www.food2fork.com/api/search?key=MY_KEY&q=chicken%20breast&page=2')
.then(response => {
return response.json();
})
.then(jsonData => {
this.setState({ data: jsonData }, function() {
console.log(jsonData);
});
});
}
render() {
return (
<div className="App">
<h1>{this.state.data.count}</h1>
<p>{this.state.data.recipes[0].publisher}</p> // Why this doesn't work?
</div>
);
}
};
If I remove the 'p' tag in the render() function, everything works as expected: the page loads at first, and then after fetching the data, displays '30' as 'h1'.
However, if I run the code with the 'p' tag, I get this error:
I'm searching for the answers for more than two hours and really can't find the answer. Why can I access the variable 'count', but not the variable 'publisher', which is inside of an array? I'm event logging out this.state after setting it, and object looks completely normal there. How can I access the elements in the 'recipes' array?
This is is because when you are fetching data at that time react render the component and you got error as this.state.data is still {} so this.state.data.recipes[0] is yet not defined as fetch request is not completed (it take some time). To resolve it you have to return on 2 conditions.
when fetch is running (Loading)
2) when fetch is completed
render() {
if(!this.state.data.recipes){
// if the fetch request is still not completed
return (
<div>
<h1>Loading .... </h1>
</div>
)
}
// run when fetch request is completed and this.state.data is now assigned some data
return (
<div className="App">
<h1>{this.state.data.count}</h1>
<p>{this.state.data.recipes[0].publisher}</p> // Why this doesn't work?
</div>
);
}
answer for your comment.
the error is can not read property 0 of undefined which means this.state.data.recipes is undefined and thus.state.data.recipes[0] is error right?.
But when you use this.state.data.count then you did not get error. As it will print undefined that is value of count (at that moment and you are not trying to print further like this.state.data.count.toString() if you do so it will through you error can not read property toString() of undefined).
and in then() when you use this.setState() it will update the state and react will re-render all affected components.
Relatively new to React, wrote a few components before (successfully), but this is the first time I'm getting something started from scratch. I use Rails with Cassandra to provide JSON responses, and everything on the back end side is working fine.
My data this.state.data looks like this:
0 {id: {n: 2.1751612473052575e+38}, email: "123#zeba.me", high_score: 73, shoe_size: 10.5, updated_at: "2018-11-06T01:23:36.611-08:00"}
1 {id: {n: 2.8024982600468778e+38}, email: "123#sotkamonlukio.fi", high_score: 13, shoe_size: 7.5, updated_at: "2018-11-06T01:24:55.791-08:00"}
2 {id: {n: 2.4227336868283995e+38}, email: "123#icloud.com", high_score: 99, shoe_size: 10.5, updated_at: "2018-11-06T01:24:07.858-08:00"}
And doing this.state.data[1] obviously returns
{id: {n: 2.8024982600468778e+38}, email: "123#sotkamonlukio.fi", high_score: 13, shoe_size: 7.5, updated_at: "2018-11-06T01:24:55.791-08:00"}
However, this.state.data[1].email throws this
TypeError: undefined is not an object (evaluating 'this.state.data[1].email')
What can I do to access email and the rest of the data?
full component code:
import React, { Component } from 'react';
export default class Table extends Component {
constructor() {
super();
this.state = {
data: [],
}
}
componentDidMount() {
fetch('http://localhost:3000/api/users')
.then(response => response.json())
.then(data => this.setState({ data }))
}
render() {
return(
<div className="table">
<h1>LOL</h1>
<p>{this.state.data[1].email}</p>
</div>
)
}
}
The problem is that, when your component starts the rendering, the this.state.data[1].email wasn't loaded already. Just check if the data was already loaded, like below:
render(){
if(this.state.data.length > 0)
return(
<div className="table">
<h1>LOL</h1>
<p>{this.state.data[1].email}</p>
</div>
)
else return <div/>
}
The other possible solution is to use the map function. It will only render something when the array it's filled. The code is below:
render(){
return(
<div className="table">
<h1>LOL</h1>
{ this.state.data.map(user => <p>{user.email}</p>) }
</div>
)
}
For each user in the data array, the component will render a tag <p> with the email. When there's nothing in the array, the component will render nothing.
its a common error the request didn't complete before the render method is called just add an if statement
render () {
const { data } = this.state
return (
<div className='table'>
<h1>LOL</h1>
{data.length > 0 && <p>{data[1].email}</p>}
</div>
)
}
This.state.data initially set to an empty array. So when your component first time renders it will be an empty array.
In componentDidMount you are making an api call and assigning api response to the state data. So this method gets called after first render
The reason you get that issue because you are accessing 1st index from this.state.data array which is obviously an empty array at the time of first render. So what you have to do is
Change
<p>{this.state.data[1].email}</p>
To
<p>{this.state.data.length > 0 && this.state.data[1].email}</p>
Or do iterate the array and render p elements
{this.state.data.length > 0 && this.state.data.map(d => (
<p key={d.id}>d.email}</p>
)}
If you don’t have unique id per object in array then use index as key
So, I have the exact same problem as our friend here :
How to render properties of objects in React?
The below (upvoted) solution by Nahush Farkande :
render() {
let user = this.props.user || {};
....
{user.email}
....
}
works for me... if user is an object. However, in my specific case, the data I fetch and want to render is an array of objects.
So, I return something like that :
<ul>
{
user.map( (el, idx) => {
return (
<li key = {idx}>
<div className="panel-body clearfix">
{el.title}
</div>
</li>
)
})
}
</ul>
That doesn't work. I get an error message that tells me that user.map is not a function (before [HMR] connected).
I expected that once the API fetches the user array of objects, the component would re-render and then the component would show the list of titles from each object of the user array (after [HMR] connected).
If your user (I recommend to rename to users) is an array, then you cannot use {} as the default. You should use [] as the default value:
const user = this.props.user || []
or, you can use a completely different branch to handle the loading case:
if (!this.props.user) {
return (
<div> ... my loading placeholder ... </div>
);
}
You already have correct answer but just wanted to give a running example.
Initialize your data in your state with the default values e.g
in case of object -> {}
in case or array -> []
Obviously in each case your rendering logic should be different e.g in case of array you need the map to loop over array and generate jsx element.
So when ever your component receives the updated data ( either it can be empty data or complete data) either via api call or via prop changes use the appropriate life cycle method such as componentWillReceiveProps or componentDidMount to get the latest data and again set the state with latest data.
For example when data is received via api call ->
constructor() {
this.state = {
data : []
}
}
componentDidMount()
{
this.getFunction();
}
getFunction = () => {
this.ApiCall()
.then(
function(data){
console.log(data);
// set the state here
this.setState({data:data});
},
function(error){
console.log(error);
}
);
}
So at the time of initial render your data will be either empty object or empty array and you will call appropriate rendering method for that accordingly.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
data : []
}
}
componentWillMount() {
this.setState({ data : this.props.dp });
}
renderFromProps() {
return this.state.data
.map((dpElem) =>
<h3>{dpElem.name}</h3>
);
}
render() {
return (
<div>
<h1> Rendering Array data </h1>
<hr/>
{ this.renderFromProps()}
</div>
);
}
}
const dynamicProps = [{ name:"Test1", type:"String", value:"Hi1" },
{ name:"Test2", type:"String", value:"Hi2" },
{ name:"Test3", type:"String", value:"Hi3" }
];
ReactDOM.render(
<Test dp={dynamicProps} />,
document.getElementById('root')
);
<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>
<div id="root">
</div>