Issue mapping an array from api fetch call - json

I reviewed previous posts and did not see anything that addresses this issue. I'm using a functional component/hook to fetch data from an open source api. The code works as long as I am only displaying 1 field from data.map in the return render. If I try to display more than one, I get the following error: Objects are not valid as a React child (found: object with keys {name, catchPhrase,bs}). If you meant to render a collection of children, use an array instead. DevTools says the error is in the list component. When all but one list element is commented out, I can see that the entire array is returned in DevTools, and the one list element will display. Adding an additional fields(list items) results in the error. Not sure what I'm doing wrong. Any help is appreciated.
import React, { useEffect, useState } from 'react';
function PeopleData() {
const [data, setData] = useState([]);
function GetData(){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(response => setData(response))
}
console.log(data)
useEffect(() => {
GetData();
}, []);
return (
<div>
{data.map(item => <div key={item.id}>
<div>
<li>{item.name}</li>
{/* <li>{item.company}</li>
<li>{item.phone}</li>
<li>{item.email}</li> */}
</div>
</div>)}
</div>
)
}
export default PeopleData;

It's because you are trying to display company property from the response.
From the API:
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
This means that you have to display company.name, company.bs or company.catchPhrase.

Each object has the shape shown bellow, and the app is crashing because you are trying to render company which is an object, when it is expecting a string or node. So you should use item.company.name instead
{
id: 1,
name: "Leanne Graham",
...
company: {
name: "Romaguera-Crona",
catchPhrase: "Multi-layered client-server neural-net",
bs: "harness real-time e-markets",
},
};
You can also take advantage of the await/async syntax instead of chaining together then statements like bellow. The execution of the line is promised before the beginning of the next line by means of the await operator
async function GetData() {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const json = await response.json();
setData(json);
}
I have this working in a sandbox here https://codesandbox.io/s/throbbing-butterfly-dt2yy?file=/src/App.js

The company property is an object and an object can't be a child in React as it says in the error as well.
Objects are not valid as a React child (found: object with keys {name,
catchPhrase,bs})
The solution is to only show properties from the company object that you may be interested to show in the UI.
import React, { useEffect, useState } from 'react';
function PeopleData() {
const [data, setData] = useState([]);
function GetData(){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(response => setData(response))
}
console.log(data)
useEffect(() => {
GetData();
}, []);
return (
<div>
{data.map(item => <div key={item.id}>
<div>
<li>{`Name: ${item.name}`}</li>
<li>{`Company: ${item.company.name}`}</li>
<li>{`Phone: ${item.phone}`}</li>
<li>{`Email: ${item.email}`}</li>
</div>
</div>)}
</div>
)
}
export default PeopleData;

Item.company is an Object, that's why React throws an Error. You have to access the attributes of company. If you want to display all attributes of Company you could built a "For In" loop and and display them all too. I can show you an example if you need clarification.
As a little side note and tip, look into the useFetch hook. You will love it, especially if you have multiple components that fetch data like this.

Related

How do I export an JSON file correctlly react?

I have an API url where I will do a get request. But I dont want to use this directly from my component. I want to store the result into an json file and import it from my component.
example of psedocode:
// example.json
let data = fetch(url).then()
export data
// Buy.jsx
import data from './data/example.json
useEffect(
//... store it to the state
)
What is the correct way to implement this ?
instead of json u can do like this
const movies_data = {
"data": [
{
"id": 1,
"title": "Freed"
},
{
"id": 2,
"title": "Fifty"
}
]
}
export default movies_data;
and then can import where u want
I believe this code snippet will cover all the needs:
import React from 'react'
const App = () => {
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
const fetchData = async() => {
const result = await fetch('https://jsonplaceholder.typicode.com/posts');
//whatever you receive convert to the JSON format ('axios' does this by default)
const data = await result.json();
// console.log('data: ', data);
setPosts(data);
}
fetchData();
}, []);
return (
<div>
{posts.map(
post => (
<React.Fragment key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</React.Fragment>
)
)
}
</div>
)
}
export default App;
Explanation:
This example utilize Fetch API a built-in JavaScript solution for querying REST API's from any source. Once you receive the result from the API fetch will convert it to the JSON format. Following step is saving the outcome using setPosts(data) method defined at the beginning of the component.
References:
React Fragments
React Hooks FAQ
Feel free to ask any questions regarding this code.

I need help displaying data from the Steam API using a Flatlist in React Native

I'm trying to display game information from the Steam API in a React Native Flatlist. I'm new to React and JSX, so a lot of what I'm reading doesn't make sense.
I want the Flatlist to display a list of game titles owned by a particular account. The data returned from Steam's API call (via fetch) looks like this:
{
"response": {
"game_count": 69,
"games": [
{
"appid": 220,
"name": "Half-Life 2",
"playtime_forever": 24,
"img_icon_url": "fcfb366051782b8ebf2aa297f3b746395858cb62",
"img_logo_url": "e4ad9cf1b7dc8475c1118625daf9abd4bdcbcad0",
"has_community_visible_stats": true,
"playtime_windows_forever": 0,
"playtime_mac_forever": 0,
"playtime_linux_forever": 0
},
{
"appid": 320,
"name": "Half-Life 2: Deathmatch",
"playtime_forever": 0,
"img_icon_url": "795e85364189511f4990861b578084deef086cb1",
"img_logo_url": "6dd9f66771300f2252d411e50739a1ceae9e5b30",
"has_community_visible_stats": true,
"playtime_windows_forever": 0,
"playtime_mac_forever": 0,
"playtime_linux_forever": 0
},
and so on. Since I'm trying to display a list of games by name, the name attribute is the only one I need.
The data lists each game as an anonymous object, so I can't access the properties within each game using dot notation like I normally would. I tried using a for loop to iterate through them, but that doesn't work either. From my research, it seems like people normally use an Array.map for this kind of thing, but I'm unclear if that can be used with Objects.
Another problem I've encountered is the Flatlist keyExtractor property. I know it's supposed to be an anonymous function that returns some unique index or property about each Flatlist item, for the purpose of making the structure more efficient and to allow it to track updates to the list. However, I have no idea how to create this function myself. I think the appid field from the JSON data would be a good candidate, but I'm not sure how to get that into the keyExtractor function.
So, to put it as a question: How would I go about displaying data from a JSON object containing anonymous sub-objects in a Flatlist, and how would I populate the keyExtractor of that list with a different data entry (the appid from that list?
Below is my starting code:
import React, {Component} from 'react';
import {FlatList, Stylesheet, Text, View} from 'react-native';
export default class App extends Component {
state = {
dataset: []
};
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch("<API URL>");
const json = await response.json();
//const data = json.map((item) => item.games.name);
var key = 0;
const data = json[games][0][name];
this.setState({ dataset: data });
}
render() {
console.log(this.state.dataset);
return (
<View>
<FlatList
data={this.state.dataset}
keyExtractor={(x, i) => i} //I have no idea what this does, or if it makes sense here.
//Where do x and i come from? (I got this from a tutorial video
//and this was glossed over)
renderItem={({ item }) => //Where does item come from?
<Text>
{item}
</Text>
}
/>
</View>
);
}
}
Alright, it seems you're having a few minor problems with understanding how FlatList works. Let me break it down for you.
Let's start with the Steam API request. In your example, you're first declaring dataset as an empty array in your state, then trying to update it with the result of a network request which is the way to go. The problem is, when you do json['games'][0]['name'] you're accessing the first item (index 0) of the games array and getting its name property and then setting that name as your dataset. Although you forgot the quotes around property names, it won't work. What you need to do instead is something like this:
fetchAllGames = async () => {
const steamResponse = await fetch("<API URL>");
const json = await steamResponse.json();
// We get all the games back from Steam in the form of an array
this.setState({ games : json.games });
}
We're now correctly updating the array inside our state with the data from the games array.
Let's move on to the keyExtractor and renderItem functions. The keyExtractor function is used to tell React about a unique identifier for each of your list items. In this case, this would be the appid property of a game. React then uses this unique ID to differentiate between items and determine which ones need updating. This function provides you with two parameters, namely the actual item and its index. Using these, we can then do something like this:
keyExtractor = (item, index) => {
return item.appid.toString();
}
We're now returning the appid property as a string (which is the type React expects key to be).
The renderItem function is a bit different, React is providing you with a parameter which contains your item plus a lot of other properties. Since we're only interested in the actual item, we're destructuring it using brackets like so: { item }. This is a technique commonly used in JavaScript to "extract" properties from objects. It is normally used like this:
const testObj = {
name : "John",
surname : "Doe"
}
const { name, surname } = testObj;
This way, you can directly refer to name and surname as if they were independent variables. Another way of doing this would be:
const testObj = {
name : "John",
surname : "Doe"
}
const name = testObj.name;
const surname = testObj.surname;
I hope this cleared some of the questions you might've been asking yourself! Here's the complete working code below. You may notice I moved some inline functions to class members, this is just a performance optimization to prevent the functions from being recreated on every render, you can ignore that.
import React, { Component } from 'react';
import { FlatList, Text } from 'react-native';
export default class App extends Component {
state = {
games : []
};
componentDidMount() {
this.fetchAllGames();
}
fetchAllGames = async () => {
const steamResponse = await fetch("<API URL>");
const json = await steamResponse.json();
// We get all the games back from Steam in the form of an array
this.setState({ games : json.response.games });
}
keyExtractor = (item, index) => {
return item.appid.toString();
}
renderItem = ({item}) => {
return (
<Text>{item.name}</Text>
);
}
render() {
return (
<FlatList
data={this.state.games}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem} />
);
}
}
EDIT #1 - As pointed out by the OP, I made a typo and corrected it. I also changed the JSON object to reflect the response property.

React constant with parameters using square brackets

I'm new to React.
I have the code below with a function, but when I run it, it returns an error:
TypeError: renderJson[item.node] is not a function.
How can I fix the renderJson function?
export const readItem = item => {
printlog(item);
return renderJson[item.node](item);
};
const renderJson = {
"heading": item => <h1>{item.map(item => readItem(item))}</h1>
};
If you're trying to create a single React functional component that takes a JSON, and outputs the items in the JSON as a header, it would be more like this:
// If you're getting this JSON from an external source using something like a GET request, put the request inside a "useEffect()" hook
const myJson = {
"heading": ["My First Header", "My Second Header"]
};
export const Header = () => {
console.log(myJson);
return <h1>{myJson.heading.map(header => header}</h1>
};
I apologize if this is a misinterpretation of your question. If it is, any additional details would be helpful.

ReacJs and fetch json and update state prop

I'm a bit disappointed of my results about getting started with reactjs.
I'm now just trying to parse json data from a simple restful ws.
If I put on chrome the url of rest get query, it answers correctly a json array:
[{"label":"TestLabel!!","value":7,"rating":17.25},{"label":"TestLabel2 !!","value":8,"rating":18.25}]
this is my React component:
export default class ItemLister extends React.Component {
constructor() {
super();
this.state = { items: [{"label":"Test1!!","value":7,"rating":17.25}] };
}
componentDidMount() {
fetch('/rest/json/product/get')
.then(result=> {
this.state.items.push(result);
});
}
render() {
return(
<div>
<div>Items:</div>
{
this.state.items.map(function(item, i){
return <div key={i}>{item.label}</div>
}
)}
</div>
);
}
}
This, after lots of tweaking, shows no errors, but nothing happens to the list, which shows only the constructor element.
Pls point me to the right direction...
thanks
SOLVED:
the "setState()" as suggested was part of the problem.
The other part is the way i manage the json answer.
This way the project works:
componentDidMount() {
fetch('/rest/json/product/get')
.then(response => response.json())
.then(response => {
this.setState({items: response });
})
}
But I just tried another way to implement the call by chance since i was totally lost.
I feel a bit confused. What is the correct way to implement a fetch?
Anyways...this is ok.
Read the last line in the Docs:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
try this:
this.setState((state) => ({ items: state.items.concat(result) }))
or you can do this if result is array:
this.setState({ items: [...this.state.items , ...result] })
try this.setState('items',result)

display json data with react and redux

I am attempting to load some local json data with redux and display in react app. But i'm getting the pageId is undefined in the reducer.
Not sure what I am doing wrong here, I think it might be something wrong with how I'm passing the data but im very new to redux so i'm not sure.
Data
const page = [
{"title":"Mollis Condimentum Sem Ridiculus"},
{"title":"Pharetra Tellus Amet Commodo"}
]
export default page;
Action
const getPage = (pageId) => {
const page = { pageId: pageId }
return {
type: 'GET_PAGE_SUCCESS',
payload: page
}
}
export default getPage
Reducer
import getPage from '../actions/actionCreators'
import pageData from './../data/pageData';
const defaultState = pageData
const pageReducer = (state = defaultState, action) => {
if (action.type = 'GET_PAGE_SUCCESS') {
state.page[action.payload.pageId].title = action.payload
}
return state
}
export default PageReducer
Component
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import getpage from '../../actions/actionCreators'
const mapStateToProps = (state, props) => {
const page = state.page[props.pageId]
return { page }
}
class Page extends Component {
componentDidMount () {
this.props.getpage(this.props.pageId)
}
render() {
return (<div><PageContainer pageId={0} /></div>)
}
}
const PageContainer = connect(mapStateToProps, { getpage })(page)
export default Page
I've modified your code into a working JSFiddle for reference: https://jsfiddle.net/qodof048/11/
I tried to keep it as close to your example, but let me explain the changes I made to get it working (also note that JSFiddle does not use the ES6 import syntax).
1) Your PageContainer was not constructed correctly. The last parameter should have been a reference to the Page component (not 'page').
const PageContainer = connect(mapStateToProps, { getPageSimple, getPageAsync })(PageComponent)
2) You used PageContainer in the Page component, but PageContainer is the 'wrapper' around Page. You use PageContainer instead of Page in your render method, so it loads the data (maps state and actions).
ReactDOM.render(
<Provider store={store}>
<div>
<PageContainer pageId="0" async={false} />
<PageContainer pageId="1" async={true} />
</div>
</Provider>,
document.getElementById('root')
);
3) The store was mixed up a bit. If I understood your example correctly you want to load a page into the local store from the pageData array, which simulates a server call maybe. In that case you intialState can't be pageData, but rather is an empty object. Think of it like a local database you're going to fill. The call to your action getPage then gets the page (here from your array) and dispatches it into the store, which will save it there.
const getPageSimple = (pageId) => {
const page = pageDatabase[pageId]; // this call would be to the server
// then you dispatch the page you got into state
return {
type: 'GET_PAGE_SUCCESS',
payload: {
id: pageId,
page: page
}
}
}
4) I've added an async example to the JSFiddle to explain how you would actually fetch the page from the server (since the simple example would not be sufficient). This needs the thunk middleware for redux to work (since you need access to the dispatch method in order to async call it). The setTimeout simulates a long running call.
const getPageAsync = (pageId)=>{
return (dispatch, getState) => {
setTimeout(()=>{
const page = pageDatabase[pageId]; // this call would be to the server, simulating with a setTimeout
console.log("dispatching");
// then you dispatch the page you got into state
dispatch({
type: 'GET_PAGE_SUCCESS',
payload: {
id: pageId,
page: page
}
});
}, 2000);
}
}
The JSFiddle loads 2 containers, one with your simple getPage and one with the async version, which loads the title after 2 seconds.
Hope that helps you along on your react/redux journey.
Hey I see a small mistake in you component, I think. You are doing this.props.pageId, when you are setting page and not pageId on the component's props. So shouldn't it be this.props.getPage(this.props.page.pageId) instead? Could that be it?
Also a small side note, an important tip for using redux is to not mutate state. In you reducer where you are doing state.page[action.payload.pageId].title = action.payload you should probably not set state like that, but instead return a new object called newState which is identical to state, but with the title updated. It is important to treat objects as immutable in Redux. Cheers