Not able to render data from state into the React Component - reactjs

I am trying to call Firebase and fetch the data in the form of JSON and then pass the objects of the JSON array to some other react-native component.
I am able to successfully make a call to Firebase and then fetch the array of JSON objects. Then later I am storing that list of JSON objects inside the STATE variable and then later trying to pass the object one by one to another react-native component.
Code:
class DisplayCardInformation extends Component
{
const itemsRef = db.ref('firebaseLink/');
state = {
listDataFromDB: null,
lastKey: null
};
componentDidMount()
{
itemsRef.on('value', ( snapshot ) => {
var data = snapshot.val();
this.setState({
listDataFromDB : snapshot.val(),
});
var keys = Object.keys(data);
keys.forEach((key) =>
{
this.setState({
lastKey: key
});
});
});
}
render() {
this.state.listDataFromDB.map((listDataItem) => {
return(
<CardInformation listItem = {listDataItem} />
);
});
}
}
export default DisplayCardInformation;
I am trying to pass the information to the CardInformation.
Error:
TypeError: TypeError: null is not an object (evaluating 'this.state.listDataFromDB.map')
This error is located at:
in DisplayCardInformation (at Home.js:33)

You need to set the initial value of the state to any array like:
state = {
listDataFromDB: []
};
What is happing in your case is that, when your component renders for the first time, it passes null to the child component.

Related

ReactJS - Can't access properties in object from a fetch

I'm doing a fetch to an API and it's returning the data fine, but when I try access the properties, it returns:
Error: Objects are not valid as a React child (found: object with keys {breeds, categories, id, url, width, height}). If you meant to render a collection of children, use an array instead.
myFetch.jsx
import React, {Component} from "react"
class myFetch extends Component {
state={
data:[]
}
componentDidMount(){
const url = "xxxxxxxxxxxxxxxxxxxxxxx"
fetch(url)
.then(r=>r.json())
.then(data=>{
this.setState({data:data})
// console.log(data)
})
.catch(e=>console.log(e))
}
render(){
const {data} = this.state
console.log(data[0])
return (<p>{data[0]}</p>)
}
}
export default myFetch
EDIT
"data" in the state is initialized to an array. Therefore, I should have iterated through the array during the render as
{data.map(d => d.url)} and access whichever property I desire as shown below:
render(){
const {data} = this.state
console.log(data)
return (<p>{data.map(d=>d.url)}</p>)
}
Your data on the state doesn't have any element on 0 index. That's why you getting that undefined error. You can check if it exists before trying to render it.
Something like that:
render() {
const { data } = this.state;
if (data[0]) {
console.log(data[0]);
return <p>{data[0].url}</p>;
}
return null;
}

How can make component level variable as constant in ReactJS?

I want to have a component level variable as constant in ReactJS, Redux with Typescript.
export class MyComponent {
initialProps = {};
constructor() {
}
componentWillMount() {
this.initialProps = this.props; //Early tried without this. But this.props is getting changed because of the filter obj.name in the below function.
}
render() {
return(
<MyPanel>
//Another <select> widget
{this.getCustomObject()}
</MyPanel>
);
}
getCustomObject() {
if(this.props.selectedId == 2) {
let filteredTypes = [];
this.initialProps.obj.forEach((o) => {
if(this.props.obj.name !== 'ABC'){
filteredTypes.push(this.props.obj);
}
});
this.props.types = filteredTypes;
}
return this.props;
}
Note: Actually i want to do re-render the component and display the filtered object based on the dropdown selection. But once we filtered. Next time when we change the dropdown value we are getting the filtered the value in the props.
But we need the props which was passed first time to this componennt and filter the values everytime. SO tried with this.initialProps. But that also getting changed once this.props is updated.

ReactJS: why is pushing value into this.state array causing type error?

I tried to push a value into a state array but I get an issue TypeError: Cannot read property 'state' of undefined at this.state.rows.push(a);
Know why? I am trying to push a new value into the array after i click a button.
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
name: '',
rows: ['hello',<p>gfdfg</p>,'mello']
}
}
handle(e){
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
render() {
return (
<div className="App">
Button<br/>
<input type="submit" id="black" onClick={this.handle}/><br/>
<p>{this.state.rows}</p>
</div>
);
}
}
export default App;
There are couple of things that are wrong here:
you should NEVER change the state directly:
This is a big No No:
this.state.rows.push(a);
instead you should do something like this:
this.setState({ rows : [...this.state.rows, a] })
or without ES6:
const newArray = this.state.rows.slice();
newArray.push(a);
this.setState({ rows: newArray })
You should always replace the state with a new one.
this in a react component is not what you think it is, in order to make it work you can do one of two things:
a. change your method to an arrow function:
handle = (e) => {
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
b. bind this to the method:
constructor() {
super();
this.state = {
name: '',
rows: ['hello',<p>gfdfg</p>,'mello']
}
this.handle = this.handle.bind(this);
}
the method handle does not have access to the context of the class i.e this; consider writing it as a fat arrow function
// class definition
handle = () => {
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
render() {
// render logic
}
Having said this, mutating the state is not a good idea, consider using setState if you want your component to re-render as a result of state change
handle = () => {
e.preventDefault();
let { rows } = this.state;
var a = "h";
rows.push(a);
this.setState({
rows,
});
}
You are doing wrong, you have to use setState() method to push the value in the array:
handle = (e) => {
e.preventDefault();
var a = "h";
let tempRows = [...this.state.rows];
tempRows.push(a)
this.setState({rows:tempRows})
alert("hello");
}
You have two problems.
Event handlers require 'this' to be bound: https://reactjs.org/docs/handling-events.html So following this, you must either write: this.handle = this.handle.bind(this) in your contructor, or change handler to arrow function, if your build process supports transpilation of class fields.
React component will only update if component props change, or component state changes. Which is done by comparing references. In your case, when you push to the array, you are mutating the state, so the new reference is never created, and component does not re-render. If you want to verify that, just put console.log(this.state.rows) below this.state.rows.push(a) and you'll see that the array has received the new value, but component does not represent it. You need to use this.setState to create a new reference for your state, like so: this.setState({ rows: [...this.state.rows, a] })
Another way of returning a new array from the current array with additional elements and then pushing to state is to use the concat method.
Example :
this.setState({ users: this.state.users.concat(<Additonal Items Here>)}

Consume RESTful data with React and Axios

how can I retrieve the data from https://api.github.com/search/users?q=jason and render it in React? I tried something like:
constructor(){
this.state = {
data: []
};
}
componentDidMount(){
axios.get("https://api.github.com/search/users?q="+_searchTerm)
.then(res => {
this.setState({
data: _.values(res.data.items)
})
});
}
render() {
const listProducts = this.state.data.map((product) =>
<li key={product.toString()}>{product}</li>
);
return (
<div>
<ul>{listProducts}</ul>
</div>
);
}
but it didn't work. I get the error message:
Unhandled Rejection (Invariant Violation):
Objects are not valid as a React child (found: object with keys {login, id, avatar_url, gravatar_id, ...).
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.
so I guess is that I have to convert the response to an array. But I am not too sure how to do it.
You are returning an object rather than a component or a string.
Change this;
const listProducts = this.state.data.map((product) =>
<li key={product.toString()}>{product}</li>
);
to this
// you need to set key to a unique string like id
const listProducts = this.state.data.map((product) =>
<li key={product.id}>{JSON.stringify(product)}</li>
);

Rendering Firebase list on React

I followed this https://www.youtube.com/watch?v=p4XTMvagQ2Q introductory tutorial on Firebase and React. I am trying to take this further but I have been stuck for a while. Basically, I want to retrieve a list of child from a firebase node and render it as list/table. Currently, only the last child is being rendered since every other one is being overwritten.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state = {
id : 0,
latitude : 0,
longitude : 0
};
}
componentDidMount(){
const rootRef = firebase.database().ref().child('drivers');
rootRef.on('child_added', snap => {
console.log(snap.key);
this.setState({
id : snap.key,
latitude : snap.val().lat,
longitude : snap.val().lon
});
});
}
render() {
return (
<div className="App">
<h1>{this.state.id}</h1>
<h1>{this.state.latitude}</h1>
<h1>{this.state.longitude}</h1>
</div>
);
}
}
export default App;
How do I update the render function to display the whole list?
What is happening is you're retrieving the whole list, but for every element in the list, you're overriding your state with its value. Then, in your state, you'll always have the last values from the list.
What you can do in this case is store a list of elements, let's say positions:
this.state = {
listOfPositions: []
};
Then when a child is added, you append the contents of the new child to your current list:
rootRef.on('child_added', snap => {
const previousList = this.state.listOfPositions;
previousList.append({
id: snap.key,
latitude: snap.val().lat,
longitude: snap.val().lon
});
this.setState({
listOfPositions: previousList;
});
});
Finally, in your render method, you generate a list of react elements for every element in your listOfPositions and render it in the render method:
render() {
const listOfPositions = this.state.listOfPositions.map(position =>
<div>
<h1>{position.id}</h1>
<h1>{position.latitude}</h1>
<h1>{position.longitude}</h1>
</div>
);
return (
<div>{listOfPositions}</div>
);
}
Note that when you're setting your reference to the database and you get your value from this.state.listOfPositions, you can't guarantee that the state value is the most updated one. So if you want to be sure about it use the other definition for the this.setState() method like this:
rootRef.on('child_added', snap => {
const newPosition = {
id: snap.key,
latitude: snap.val().lat,
longitude: snap.val().lon
};
this.setState(previousState => {
listOfPositions: previousState.listOfPositions.append(newPosition);
});
});
This will make sure when you call this.setState() you will have the last value of state in your previousState variable.

Resources