I'm trying to understand why this is throwing an error of name is undefined, but if I do it a different way, I can get data to display... Been stuck on this for quite awhile now and can't pass data to other components. This is the closest I've come, but don't know why one way works but the other says undefined. Trying to select and pass data from JobsTableApi.js to Title.js
JobsTableApi.js:
import React, { Component } from 'react'
//import Title from './components/header/Title.js'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXX_XXXX_XXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"where": "{40.CT. 'In Progress'}","sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response.json())
.then( data => this.setState({ data })
);
}
render() {
const { data } = this.state;
if (data === null) return 'Loading Job Data... ';
return (
<div>
{Object.keys(data["data"]).map(item => (
<div key = {item}>
{data["data"][item][6].value}
</div>
))}
</div>
)
}
}
export default JobsTableApi;
Title.js:
import { React } from 'react';
import PropTypes from 'prop-types';
import JobsTableApi from '../../JobsTableApi';
export default function Title() {
return(
<div>
<h3>
<JobsTableApi />
</h3>
</div>
)
}
Title.propTypes = {
name: PropTypes.string.isRequired
}
The above methods display all data coming over from my api call that has the field ID of 6 accurately, but since i'm attempting to pull different fields over to different components, I need to set it as props, but when I do, I get errors and says undefined. Example below.
JobsTableApi.js:
import React, { Component } from 'react'
import Title from './components/header/Title.js'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXXX_XXXX_XXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"where": "{40.CT. 'In Progress'}","sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response.json())
.then( data => this.setState({ data })
);
}
render() {
const { data } = this.state;
if (data === null) return 'Loading Job Data... ';
return (
<div>
{Object.keys(data["data"]).map(item => (
<div key = {item}>
<Title name = {data["data"][item][6].value} />
</div>
))}
</div>
)
}
}
export default JobsTableApi;
Title.js:
import { React } from 'react';
import PropTypes from 'prop-types';
//import JobsTableApi from '../../JobsTableApi';
export default function Title({ name }) {
return(
<div>
<h3>
{name}
</h3>
</div>
)
}
Title.propTypes = {
name: PropTypes.string.isRequired
}
As you can see from my api call in the 'body' I'm pulling over many fields with values. From here I need to send these throughout my app into different components to be used.
Right now I have 2 api call files in my src folder, then src>components>charts>MultipleLineCharts.js files. As well as src>components>header>Title.js
I'm wondering if I need to change my structure and make the Api Calls on a parent component? Instead of this as siblings? App.js in src is rendering all. Any advice or guidance on this would be appreciated as well.
Thanks!
If the components you need to send the data to are all direct children of this parent API fetching component, then in the interests of starting simple, you don't need to use anything like React Context or Redux - keep it simple and get it working, then iterate as required.
The moment you build out a complex component hierarchy and unrelated or deeply-nested components need access to the data, then you probably want to look at something like Context / Redux.
`Right now I have 2 api call files in my src folder, then src>components>charts>MultipleLineCharts.js files. As well as src>components>header>Title.js
I'm wondering if I need to change my structure and make the Api Calls on a parent component? Instead of this as siblings?
`
You can use React Context or Redux for state management, So you can send or use data to any component.
I understand how to pass data from component to component using this.props.
However, I can't seem to figure out a way to pass values or gather values asynchronously from say a database to a variable that will be used throughout the component,
I can't change this variable after load it has to be loaded before the render method since it is not allowed to be changed once loaded.
What I mean is this attempt 1
import React from "react";
import ReactDOM from "react-dom";
let my_variable = "";
fetch("url", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(data => data.json())
.then(result => {
return (my_variable = result);
});
--------------------new way-------------------------
const my_variable = fetch("url", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(data => data.json())
.then(result => {
return (result);
});
-------------------------------------------------
class MyClass extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {};
}
render() {
return (
<>
<NewElement variableNeeded={my_variable} />
</>
);
}
}
export default MyClass;
It, of course, does not work since the empty variable hits the component and the cannot be updated.
Option B:
import React from "react";
import ReactDOM from "react-dom";
class MyClass extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {};
}
render() {
return (
<>
<NewElement variableNeeded={this.props.my_variable} />
</>
);
}
}
export default MyClass;
Again does not work, this class is a child of another class and the variable value was gathered inside the onComponentDidMount() the render method was hit first so I don't know how to gather the data then pass it.
If anyone could help me with this I would greatly appreciate it
To fetch the data:
componentDidMount() {
fetch('url', /* your config here */).then(data => {
return data.json();
}).then(json => {
this.setState({ variable: json });
});
}
... Then in the render method ...
render() {
const { variable } = this.state;
return (
<>
{ variable && <NewElement variableNeeded={variable} /> }
</>
);
}
for handling gathering asynchronously data from somewhere you need to use React Life Cycles like so :
class MyClass extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
variable: ""
};
}
componentDidMount() {
fetch("url", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(data => data.json())
.then(result => {
return this.setState({ variable: result });
});
}
render() {
return <>{variable && <NewElement variableNeeded={variable} />}</>;
}
}
export default MyClass;
I'm not sure if this is ok also but now I don't get the message from the component that it is being re-rendered, using method A and change the top part to the following
--------------------new way-------------------------
const my_variable = fetch("url", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(data => data.json())
.then(result => {
return (result);
});
-------------------------------------------------
In the Items component I set the toke in localstorage. The Details component tries to access this token. It gets null
class Items extends Component {
constructor (props) {
super(props);
}
getT = () => {
axios({
method: 'post',
url: '/oauth2/token',
data,
config
})
.then(res => {
if (res.status === 200) {
console.log(res.data)
this.setState({
token: res.data
})
localStorage.setItem('token', JSON.stringify(res.data['access_token']))
} else {
const error = new Error(res.error);
throw error;
}
}).catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render () {
return (
<div>
<ul className="instancesUl">
{ this.props.items.map((item, index) =>
<li
key={item.id}
item={item}
onClick = {this.getT}
>
</li>
)
}
</ul>
<Details
/>
</div>
)
}
}
class Details extends Component {
constructor (props) {
super(props);
this.state = {
}
axios.defaults.headers.common['Authorization'] = localStorage.getItem('token');
}
componentDidMount() {
axios({
url: `https://app`,
method: "GET"
})
.then(res => {
})
.catch(error => {
})
}
render () {
return (
<div >
</div>
)
}
}
The problem is that Details constructor is invoked earlier at the time when the localStorage is empty, after that your logic to set the token works but Details constructor does not execute again to get the updated value.
Here is an example how you could tweak your code
In short you should not rely on the state everywhere, init your state in a parent component and setState there, pass it properties to the children components via React props, so when you change the state your props will get updated also and force the component which they belong to render the update and call all needed logic.
You are reading before the write.
Root of Problem:
getT method of class Items is responsible for writing the
localstorage and which has async call, It will take some time to get
data from server so you don't know when it will return.
Details component is rendered inside the Items component so it will
be called immediately when it reaches the render method and as discussed in
above point we are not sure token is written is localstorage or not.
Solution:
You can set some value in state saying token_is_saved
You can render Details component only when token_is_saved is true
Initialize state in Items constructor,
this.state = {
token: null
}
Modify your Items's render method as,
{
this.state.token && <Details />
}
Constructor of Details component works once in the very beginning while instantiating the component class and in that case there would be not token available in the localStorage. After if you click on the button, which later makes the call to the server and after promise is being resolved, you store the token. After refreshing your page, it will become available for you.
Is this the correct way of implementation?
import Vue from 'vue';
import axios from 'axios';
const baseURL = "http://127.0.0.1:8000/api/v1";
const token = localStorage.getItem('token');
export default axios.create({
baseURL,
headers: {
'Content-type' : 'application/json',
'Authorization' : 'Bearer ${token}'
}
});
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}
I have an application which needs to fetch invoice data from Stripe API (payment processor). When the invoice data has been returned, I'm trying to update my state using this.setState({invoiceData: invoices}) where invoices is a string of HTML that I build out from the data returned from the Stripe API.
The issue is that the HTML isn't being rendered and is showing as plain text. I am pretty new to React and have only just got my head around rendering states, but now I'm pretty stuck on working this one out. What do I need to do to render the HTML? Please see my code below.
import React from 'react';
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = {
invoiceData: false
}
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch('/stripe-invoices', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
customerId: '128973982'
})
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
console.log('Error with Stripe response');
}
})
.then((stripeData) => {
var invoiceCount = stripeData['result']['data'].length;
var i;
var invoices = '';
for (i = 0; i < invoiceCount; i++) {
invoices += '<div><a href="' + stripeData['result']['data'][i]['invoice_pdf'] + '" download>' + stripeData['result']['data'][i]['number'] + '</a></div>';
}
this.setState({
invoiceData: invoices
})
})
.catch((error) => {
console.log('Error: ', error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoiceData ? this.state.invoiceData : null}
</div>
);
}
}
export default BillingInvoices;
Thank you for any insight.
I've stripped out some of your code for my example to make it easier to read:
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = { invoiceData: [] }
}
componentDidMount() {
fetch('/stripe-invoices')
.then((response) => response.ok && response.json())
// Here I'm assigning the nested array to `invoiceData` immediately
// so that you don't need to map over it later
.then((data) => this.setState({ invoiceData: data.result.data }));
}
render() {
// Here we can check if the data exists. If it doesn't
// show a loading icon (or something) until it is
if (!this.state.invoiceData) <Loader />
// ...otherwise show the data
return (
<div id="billing-invoices">
// we map over the invoice data and for each invoice
// return JSX (your div with an anchor populated with that invoice data)
{this.state.invoiceData.map((invoice) => {
return (
<div>
<a href={invoice.invoice_pdf} download>{invoice.number}</a>
</div>
)
})}
);
</div>
)
}
}
You can populate invoiceData with react components using JSX like so:
let invoices = (<div>{stripeData['result']['data'].map(data => (<div><a href={data['invoice_pdf']} download>{data['number']}</a></div>))}</div>);
this.setState({invoiceData: invoices});
You can replace the content of the second then clause with the above and leave the rest of the code unchanged.
Putting the resulted json in the component state is a good idea.
But then, you should deal with this json directly in your render method, using the power of JSX.
Check the official documentation about how to use JSX.
This is a dummy example of what your component could look like with the usage of JSX:
import React from "react";
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
}
state = {
invoices: []
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch("/stripe-invoices", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
customerId: "128973982"
})
})
.then(response => {
if (response.ok) {
this.setState(invoices: response.json());
} else {
console.log("Error with Stripe response");
}
})
.catch(error => {
console.log("Error: ", error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoices.map((invoice, index) => {
return (
<div key={index}>{invoice.name}</div>
)
})}
</div>
);
}
}
export default BillingInvoices;