Reactjs - Assigning json response to default array - arrays

I'm using react-image-gallery for displaying gallery. I need to load images from json response. My code follows,
let imagesArray = [
{
original: 'images/products/4.jpg'
},
{
original: 'images/products/2.jpg'
},
{
original: 'images/products/3.jpg'
}
];
export default class Products extends React.Component {
loadGallery () {
var requestUrl = 'http://myurl';
var myInit = { method: 'GET',
mode: 'cors',
cache: 'default' };
fetch(requestUrl).then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
let imagesArray = imagesArray.map((img,i)=>{ return {original: res.data[i].path.split(':')[1]}})
}));
}
render() {
return (
<div className="products-page" onLoad={ this.loadGallery() }>
<ImageGallery
ref={i => this._imageGallery = i}
items={imagesArray}/>
</div>
);
}
}
I got an error of Uncaught (in promise) TypeError: Cannot read property 'map' of undefined
if I use let newArray = imagesArray.map((img,i)=>{ return {original: res.data[i].path.split(':')[1]}}) it will assign the value to newArray
Here how can I assign the json response to imagesArray?

If what I understand is correct you want to load a set of images and pass the data as array to ImageGallery component.
There is also something wrong/undesired with your code:
When you do this,
<div className="products-page" onLoad={this.loadGallery()}>
You will actually invoke the function loadGallery() on each render, instead you should only pass the function prototype.
<div className="products-page" onLoad={this.loadGallery}>
Below you will see another approach to implement your requirement. Here we will load the images and update Product component's state with new imagesArray from JSON response. So when the state updates, component re-renders and will pass the new data to ImageGallery component.
const dummyImagesArray = [
{
original: 'images/products/4.jpg'
},
{
original: 'images/products/2.jpg'
},
{
original: 'images/products/3.jpg'
}
];
export default class Products extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesArray: dummyImagesArray
}
}
loadGallery () {
var requestUrl = 'http://myurl';
var myInit = { method: 'GET',
mode: 'cors',
cache: 'default' };
fetch(requestUrl).then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
const imagesArray = this.state.imagesArray.map((img,i) => { return { original: res.data[i].path.split(':')[1] }; });
this.setState({ imagesArray });
}));
}
render() {
return (
<div className="products-page" onLoad={this.loadGallery}>
<ImageGallery
ref={i => this._imageGallery = i}
items={this.state.imagesArray}
/>
</div>
);
}
}

So JSON does not actually exist in React. What you will likely need to do here is set up a class that represents the JSON data and returns it to the map. What map is basically doing is peeling back the layers of your object one by one. So something like,
class ImagesArray extends React.Component() {
render() {
return {
<image src={this.props.original}/>
}
}
}

Related

how to create refs for content that gets created later

I have a component that fetches data from an api upon user input. This data then gets rendered onto the screen as <li/> tags. I want those <li/> tags to have a ref.
I tried creating an object of refs that I create after the data is fetched:
this.singleRefs = data.reduce((acc, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
and then later assign these refs to the <li/> tag: <li ref={this.singleRefs[element.id]}>
but when I print them out I always have {current:null} Here is a demo
what am I doing wrong?
With dynamic ref data, I'd propose that you should use callback refs.
import React from "react";
import "./styles.css";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.singleRefs = {};
}
componentDidMount() {
const data = [
{ value: "val1", id: 1 },
{ value: "val2", id: 2 },
{ value: "val3", id: 3 }
];
this.myFunc(data);
//you don't need this anymore
// this.singleRefs = data.reduce((acc, value) => {
// acc[value.id] = React.createRef();
// return acc;
// }, {});
}
myFunc = async (data) => {
await sleep(3000);
this.setState({ data });
};
renderContent() {
return this.state.data.map(
function (element, index) {
return (
<li key={index} ref={(node) => (this.singleRefs[element.id] = node)}>
{element.value}
</li>
);
}.bind(this)
);
}
render() {
console.log(this.singleRefs);
return <ul>{this.renderContent()}</ul>;
}
}
Sandbox

React. Map thru this.state.data.response[0] to return it as component with props

Trying to call component with props from state but getting error:
[Error] TypeError: undefined is not an object (evaluating
'this.state.data.response[0].map')…
import React, { Component } from "react";
import MenuItem from "./MenuItem";
export default class AllMenuItems extends Component {
constructor() {
super();
this.state = {
isLoading: false,
data: {
response: [],
},
};
this.getItems();
this.listItems();
}
backend_server = "http://localhost:5000/allitems_detailed";
getItems() {
fetch(this.backend_server)
.then((response) => response.json())
.then((data) => this.setState({ data: { response: data } }))
.catch((error) => {
console.log("AllMenuItems getItems() fetch error", error);
});
// returns array:
// 0 [name1, name2, …, nameN]
// 1 [desc1, desc2, …, descN]
// 2 [url1, url2, …, urlN]
}
listItems() {
return this.state.data.response[0].map((item_name, count) => {
return <MenuItem item_name={item_name}
item_description={this.state.data.response[1][count]}
item_link={this.state.data.response[2][count]}
/>
}
);
};
}
render() {
return <div>{this.listItems()}</div>;
}
}
In the future I will use axios, but for now I really want to do it with "fetch" for learning
this.state.data.response[0] is throwing that error. You are initializing your state with empty an array. So when your component mounts for the first time, it's still empty and you can't access any elements from the empty array.
You can add a null check before rendering or optional chaining.
listItems() {
const { data } = this.state;
return data.response.length > 0 && data.response[0].map((item_name, count) => {
return <MenuItem item_name={item_name}
item_description={this.state.data.response[1][count]}
item_link={this.state.data.response[2][count]}
/>
});
};
/// or
return this.state.data.response?.[0]?.map
or simple you can do like this.
render() {
return <div>{this.state.data.response?.length > 0 && this.listItems()}</div>
}

How to make React.js fetch data from api as state and pass this state data from its parent to child component

I am working on a React.js + D3.js project. I wanted App.js to fetch data from a json file and save this data into state and pass this parent sate data down to my child component state through the property. I found if I use static data in App.js works fine, but once fetching from a json file, it failed because no data can be stored into property. My App.js like this:
import React, { Component } from 'react';
import SandkeyGraph from './particle/SandkeyGraph';
class App extends Component {
state = {
data : null
}
// works fine in this way!
// state = {
// data: {
// "nodes":[
// {"node":0,"name":"node0"},
// {"node":1,"name":"node1"},
// {"node":2,"name":"node2"},
// {"node":3,"name":"node3"},
// {"node":4,"name":"node4"}
// ],
// "links":[
// {"source":0,"target":2,"value":2},
// {"source":1,"target":2,"value":2},
// {"source":1,"target":3,"value":2},
// {"source":0,"target":4,"value":2},
// {"source":2,"target":3,"value":2},
// {"source":2,"target":4,"value":2},
// {"source":3,"target":4,"value":4}
// ]}
// }
componentWillMount() {
this.getData('./data/sankey.json');
}
getData = (uri) => {
fetch(uri)
.then((response) => {
return response.json();
})
.then((data) => {
// successful got the data
console.log(data);
this.setState({ data });
});
}
render() {
// failed
const { data } = this.state;
return (
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div>
);
}
}
export default App;
parrt of my is like this:
class SankeyGraph extends Component {
displayName: 'SankeyGraph';
state = {
sankeyData : null
}
constructor(props) {
super(props);
this.state.sankeyData = props.sankeyData || null;
}
PropTypes : {
id : PropTypes.string,
height: PropTypes.number,
width: PropTypes.number,
sankeyData : PropTypes.object,
}
componentDidMount () {
// will be null, if using fetch from App.js
//console.log(this.state.sankeyData);
this.setContext();
}
//...
Does anyone know how to handle this situation? Thank you so much in advanced!
After working out the problem, it turned out that there was no problem with fetch. It just didn't account for null in any of the components in the program (It would crash after using a null value.
For example in render:
render() {
if (this.state.data) {
return (
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div>
);
}
else {
return <div/>
}
}
Or, the use of a ternary operator would work as well to be more concise (answer by #Eliran):
return (
{this.state.data ?
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div> : <div>No Data Available</div>
);
You can add in your render function a condition:
render() {
// failed
const { data } = this.state;
return (
<div>
{data ?
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData={data}
/> : "Loading..."
}
</div>
);
}
and only if data is populated the component will be rendered.
...
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
data : null
}
}
It seems like an error on the state declaration?
1.- Import your json in App component on top like this: import jsonData from './data/sankey.json'
2.- Set jsonData in state jsonData in App component.
constructor(props) {
super(props)
this.state = {
jsonData : {}
}
}
componentWillMount() {
this.setState({ jsonData })
}
You do not need to fetch as you have your json locally.
Once you have your json in your state, you can display it in render like this for example:
this.state.jsonData.data.links.map(x=>
<div>
<div>x.links.source</div>
<div>x.links.target</div>
</div>
)
I've been testing and you need to replace the getData() method to this:
getData = (uri) => {
fetch(uri, {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => {
return response.json();
})
.then((data) => {
// successful got the data
console.log(data);
this.setState({ data });
});
}
This is because you need to declare 'Content-Type' and 'Accept' headers on your fetch options in order to make your request.

Render a simple list in React with promises

I'm trying to render some content from a service which returns a promise, but I can't figure out how I should return the data in my "return" block.
It's no problem to return some static data, as shown in this example. But how can I return the data from the commented code in the same way?
The image shows the console, where you can see the promise.
I know the answer is probaly very simple, but I just can't wrap my head around it.
render() {
const data2 = [
{title:'item one', id:'000'},
{title:'item two', id:'001'}
];
console.log(data2);
const data = firebaseCon.content.get('text', { fields: ['id', 'title'] });
console.log(data);
var itemList = data2.map(function(item) {
return <li className="item" key={item.id}>{item.title}</li>;
});
return (
<ul>
{itemList}
</ul>
)
}
First of all, I would say that you are doing in wrong way.
You should never make a server request in render function.
You should make a server request in componentWillMount() or componentDidMount().
If you know async/await concept then it would be helpful for you.
You can check this link..
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
You can try like this one...
import React, { Component } from 'react';
export default class ListData extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
async componentDidMount() {
const data = await firebaseCon.content.get('text', { fields: ['id', 'title'] });
this.setState({ data });
}
render() {
const { data } = this.state;
const itemList = data.map(function(item) {
return <li className="item" key={item.id}>{item.title}</li>;
});
return (
<div>
{data.length > 0 &&
<ul>
{ itemList }
</ul>
}
</div>
);
}
}
Hope it will be help
This code will handle the object that is returned by api and also moves the fetching to componentDidMount.
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
firebaseCon.content.get('text', { fields: ['id', 'title'] })
.then((response) => {
let data = [];
for (item in response) {
data.push(response[item]);
}
this.setState({ data });
});
}
render() {
let itemList = this.state.data.map(function(item) {
return <li className="item" key={item.id}>{item.title}</li>;
});
return (
<ul>
{itemList}
</ul>
)
}
A closer look at Promises' methods then and catch should make it clearer: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Have you seen this?
https://github.com/axios/axios
Promise based HTTP client for the browser and node.js.
You can see it in action in this tutorial, used with React:
https://codeburst.io/building-appointment-scheduler-app-in-react-and-nodejs-f163c4eaab6b
Hope it helps.

React setState of array of objects

I have an array of 10 objects (Lets call them "Blogs") which contain title, description and image-URL properties. I need to wrap each of the properties in HTML tags and export them all so they all load on a webpage together.
With my current code, I am only getting 1 of the objects in the current state loading on the page. How do I get all the objects in the same state?
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
title: [],
description: [],
image: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
const blogs = data.data;
var component = this;
for(var i in blogs) {
component.setState({
title: blogs[i].title,
description: blogs[i].description,
image: blogs[i].image,
loading: false
});
}
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<h2>New Blogs:</h2>
<h3>{this.state.title}</h3>
<em>{this.state.description}</em>
<img src={this.state.image}></img>
</div>
);
}
}
export default NewBlogs
I haven't run/test this but try something like this
The API call appears to return a list of objects. If so just set state once the xhr completes and set loading false once.
In the react render() is where you could iterate over your list. The easiest way to do that is with '.map()'. You then simply return react elements for each object in your list.
Also let's rename 'component' to 'list'
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
list: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
// const blogs = data.data;
// var component = this;
this.setState({list: data.data, loading: false })
// for(var i in blogs) {
// this.setState({
// title: blogs[i].title,
// description: blogs[i].description,
// image: blogs[i].image,
// loading: false
// });
// }
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
{this.state.list.map(e => (
<h2>New Blogs:</h2>
<h3>{e.title}</h3>
<em>{e.description}</em>
<img src={e.image}></img>
))}
</div>
);
}
}
export default NewBlogs

Resources