I have a react application that I am trying to render some basic JSON to the screen. I will place a small snippet of the JSON object below. I am trying to represent the data in the researchPage. I will be placing the entire component class below. Please let me know if I can provide any further information to clarify.
db.json - This is the JSON file that I am trying to pull data from.
{
"hits": [
{
"_index": "issflightplan",
"_type": "issflightplan",
"_key": "IDP-ISSFLIGHTPLAN-0000000000000447",
"_version": 1,
"_score": null,
"ContentType": {
"__deferred": {
"uri": "https://bi.sp.iss.nasa.gov/Sites/FP/_api/Web/Lists(guid'a9421c5b-186a-4b14-b7b2-4b88ee8fab95')/Items(252)/ContentType"
}
researchPage - This is the component page that I am trying to render the JSON data too. I have looked at the console and do not seem to be getting any errors. The page is showing, and the H1 Record MetaData is also rendering to the screen, however, there is no H3 or Paragraph below it.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import data from "../../data/db.json";
console.log(data);
class ResearchPage extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
error: null,
dataSet: [],
data: [],
}
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(data)
.then((res) => {
debugger;
if (res.ok) {
return res.json();
} else {
throw Error("Error Fetching Data");
}
})
.then((data) => {
console.log(data);
this.setState({ data: data, isLoading: false });
})
.catch((error) => {
console.log((error) => this.setState({ error }));
});
}
render() {
const { error, isLoading, data } = this.state;
const dataItems = data.map((dataSet) => (
<div key={dataSet.id}>
<h3>{dataSet._index}</h3>
<p>{dataSet.uri}</p>
</div>
));
if (error) {
return <p style={{ color: "red" }}>{error.message}</p>;
}
if (!isLoading) {
return <p>Loading Data...</p>;
} else
return (
<div>
<h1>Record MetaData</h1>
{dataItems}
</div>
);
}
}
export default ResearchPage;
Update:
I got it working.
The problem I was facing was when trying to display the data was that the data in the render method wasn't initialized but, if you do the mapping inside the didmount and also it looks like your trying to access the list so you need to specify that.
const hits = data.hits.map((dataSet) => (
<div key={dataSet.id}>
<h3>{dataSet._index}</h3>
<p>{dataSet.uri}</p>
</div>
));
this.setState({ data: hits, isLoading: false });
render() {
const { error, isLoading, data } = this.state;
if (error) {
return <p style={{ color: "red" }}>{error.message}</p>;
}
if (isLoading) {
return <p>Loading Data...</p>;
} else {
return (
<div>
<h1>Record MetaData</h1>
{data}
</div>
);
}
}
Oh also, I put the db.json in the public directory and accessed it as follows because I was getting module not found.
fetch("db.json", {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}})
.then (...)
How to troubleshooting
To feed some example test data to see if it's and IO issue you can convert the json object manually into a javascript object like so,
this.setState({ data: [
{ _index: 'ia', uri: 'ub' },
{ _index: 'ib', uri: 'ub' },
], isLoading: false });
More Explanation
Issues you faced
Component Lifecycle - The ComponentDidMount will fire after or at the same time that render fires. This raised a concurrency issue where the data you were trying to map was not use was not yet available. By doing the map inside the DidMount ensures that it's performed only once the data is ready. In the meantime the {data} in the render will be null and nothing will be displayed. If you wanted to test this you could put a sleep/block inside your didmount and see that the data won't display until the block is complete.
Correctly navigating the json object - Using data.map instead of data.hits.map was targeting the entire json object and not the specific list "hits" that actually needed to be accessed (data.hits). Next the .map function iterates through that list providing 'dataSet' on each iteration which allow the use that value.
Potential - I don't normally get data from a json file but, instead from web API's so, I have little experience but, normally to access the file you need a special library or have to put the file inside your public folder which is available throughout the project. You can test this by doing http://localhost:3000/db.json
if (!isLoading) {
return <p>Loading Data...</p>;
This is backwards. It should be:
if (isLoading) {
return <p>Loading Data...</p>;
Related
I'm using the Query component of React-Admin version 2.9 and I'm having trouble accessing its returned value. I've looked through github for usage examples, but with no luck thus far. It's an old version of the framework and the Query component appears to now be deprecated.
But I believe my issue may be a more generic one relating to my not fully grasping how to deal with js promises.
My function uses Query to make an api call and it looks like this:
const myfunction = (avalue) =>
<Query type="GET_LIST" resource="student" payload={{ filter: { id: avalue }}}>
{({ data, loading, error }) => {
if (loading) { return "loading"; }
if (error) { return "error"; }
return data.filter( i => i.studentType === 1 ).length
}}
</Query>
The result of the api call is an Integer value and I believe the above is obtaining it correctly.
Yet when I try to access the return value in this way ...:
console.log(myfunction(123));
... I don't get the actual value, but instead I get this:
{$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …}
Any idea why the above is getting returned instead of the actual value and how to resolve this?
Your function is a React component. React components, when executed, return a React element. They are designed to be rendered in JSX, not to be called directly. See https://reactjs.org/docs/components-and-props.html#function-and-class-components for details.
If you want to execute a query in the JS (not JSX) part of a component, in react-admin 2.9 you must use the withDataProvider decorator.
Here is an example usage:
import {
showNotification,
UPDATE,
withDataProvider,
} from 'react-admin';
class ApproveButton extends Component {
handleClick = () => {
const { dataProvider, dispatch, record } = this.props;
const updatedRecord = { ...record, is_approved: true };
dataProvider(UPDATE, 'comments', { id: record.id, data: updatedRecord })
.then(() => {
dispatch(showNotification('Comment approved'));
dispatch(push('/comments'));
})
.catch((e) => {
dispatch(showNotification('Error: comment not approved', 'warning'))
});
}
render() {
return <Button label="Approve" onClick={this.handleClick} />;
}
}
ApproveButton.propTypes = {
dataProvider: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
record: PropTypes.object,
};
export default withDataProvider(ApproveButton)
I have a React project using Graphql and Mongo. When doing a query, I'm trying to log the returned data in the console through dot notation and it return "undefined".
I can't fathom Why??
My Component:
import { useQuery } from "#apollo/client";
import React, { useEffect } from "react";
import { GET_USER_SETTINGS } from "../graphql/getUserTimeSetting";
const UserAuthSettings = ({ isAuthenticated, toggle, user = { name: "" } }) => {
const { data, loading, error } = useQuery(GET_USER_SETTINGS, {
variables: { userName: user.name },
options: {
awaitRefetchQueries: true,
},
});
console.log(data.userTimeSetting);
if (loading) return <div>Submitting...</div>;
if (error) return <div>{error.message}</div>;
return null;
};
export default UserAuthSettings;
Console
If I only "console.log(data)
Ok I found the answer.
To use or "consume" the data with Apollo/client hooks, you need to do it once you make sure the data exist.
In other words, the code block
if (loading) return <div>Submitting...</div>;
if (error) return <div>{error.message}</div>;
must come before my console.log(data) - otherwise the query is still in flight and react could not verify the existence of the data. Hence the error.
Fantastic, moving one.
I am trying to render this data from the API onto my page.
I am aware that what I am trying to render isn't in the typical array its a javascript object which is throwing me off.
With my code, as is, the API data is there its just a matter of being able to access because .map isn't working because the data I have is not an array.
Do I need to create another function?
I am not sure
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
apiData: []
}
render() {
console.log('api datat is')
return (
<div>
<center>
<h1>hello something</h1></center>
{this.state.apiData.map(title => title.rendered)}
</div>
)
}
componentDidMount() {
fetch('http://www.mocky.io/v2/5dece3d333000052002b9037')
.then(response => response.json())
.then(data => {
this.setState({
apiData: data.data
})
})
console.log("component fetched data")
}
}
export default App
Any help would be great.
I am still learning how to code so be nice
Look at the API response:
{
"id": 233383,
"date": "2019-10-28T10:50:53",
"date_gmt": "2019-10-28T10:50:53",
"modified": "2019-10-28T10:55:14",
"modified_gmt": "2019-10-28T10:55:14",
"link": "https:\/\/www.stylist.co.uk\/long-reads\/friendship-friends-whatsapp-facebook-messenger-social-media-group-chat-best-friends-psychology-advice\/233383",
"title": {
"rendered": "Whatsapp and Facebook Messenger: how group chat is changing the dynamic of our friendships"
},
"slug": "whatsapp-and-facebook-messenger-how-group-chat-is-changing-the-dynamic-of-our-friendships",
etc.
It does not have a .data property, so
.then(data => {
this.setState({
apiData: data.data
})
})
sets undefined to this.state.apiData.
The only rendered exists in the one top-level object in the response, so you should remove the .map entirely, something like:
.then(data => {
this.setState({
apiData: data
})
})
<h1>hello something</h1></center>
{this.state.apiData.title.rendered}
I want to load book list (book files) from /list endpoint and list them in <ul>. I created a list component and then import it on index.jsx. But it doesn't work. How can I render component with json data fetched from server in body?
list.component.jsx:
import React from 'react'
class List extends React.Component {
constructor() {
super()
this.state = { files: [] }
}
componentDidMount() {
let files = []
fetch('/books', {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ dir: '/' })
})
.then(response => response.json())
.then(data => {
this.setState({ files: data.books })
})
}
render() {
return (
<div>
<h1>Book List</h1>
<ul>
{this.state.files.map(book => {
return <li key={`book-${book.id}`}>{book.name}</li>
})}
</ul>
</div>
)
}
}
export default List
index.jsx:
import List from '../components/list'
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<List/>,
document.body.appendChild(document.createElement('div')),
)
})
I see that '/list' fetched in Network tab but no data on the browser.
Errors given:
list.jsx:31 Uncaught TypeError: this.state.files.map is not a function
at List.render (list.jsx:31)
list.jsx:31 Uncaught (in promise) TypeError: this.state.files.map is not a function
at List.render (list.jsx:31)
Have you tried console.log statements directly before the setState command? Might be worth inspecting data and then data.books. Your data might actually be in data and not data.books. data.books might be a string and you may need to JSON.parse it to convert to an array.
Please follow the link https://codesandbox.io/s/2p5p0n4lpn
Just check using ternary operator this.state.files is data available or not. If data is available then render.
{this.state.files
? this.state.files.map(book => {
return {book.userId};
})
: null}
As shown below I'm getting my data in my nextJS application in the pages/article.js using a graphQL query.
This data is passed down to another react component, which gives me a list of checkboxes.
Selecting a checkbox is calling a mutation to store the ID of the selected checkboxes in the DB.
To get the content updated, I'm using refetchQueries to call the main query again, which will pass the data down to the current component.
So far everything is working. Now I would like to get this stuff realtime using optimistic UI - which makes me some problems...
Replacing the refetchQueries with
update: (store, { data: { getArticle } }) => {
const data = store.readQuery({
query: getArticle,
variables: {
id: mainID
}
})
console.log(data)
}
runs me to the error TypeError: Cannot read property 'kind' of undefined which comes from readQuery.
I don't see what I'm doing wrong. And this is just the first part to get optimisic UI..
pages/article.js
import Article from '../components/Article'
class ArticlePage extends Component {
static async getInitialProps (context, apolloClient) {
const { query: { id }, req } = context
const initProps = { }
// ...
return { id, ...initProps }
}
render () {
const { id, data } = this.props
const { list } = data
return (
<Article
mainID={id}
list={list}
/>
)
}
}
export default compose(
withData,
graphql(getArticle, {
options: props => ({
variables: {
id: props.id
}
})
})
)(ExtendedArticlePage)
components/Article.js
import { getArticle } from '../graphql/article'
import { selectMutation } from '../graphql/selection'
export class Article extends Component {
checkboxToggle (id) {
const { mainID, checkboxSelect } = this.props
checkboxSelect({
variables: {
id
},
refetchQueries: [{
query: getArticle,
variables: {
id: mainID
}
}],
})
}
render () {
const { list } = this.props
return (
list.map(l => {
return (<Checkbox onClick={this.checkboxToggle.bind(this, l.id)} label={l.content} />)
}
)
}
}
export default compose(
graphql(selectMutation, { name: 'checkboxSelect' })
)(Article)
You have a variable shadowing issue in your update code, it seems that you're using the same name getArticle for both your query and the mutation result nested in data.
This is why your call to readQuery fails, the query params you need to provide resolves to the mutation result and not the actual query, hence the TypeError: Cannot read property 'kind' of undefined.
You just need to name your query with another identifier like getQueryArticle.