hi I have an API and from React which represents my frontend I recover the data from the API to display it but from there I encounter this problem.
Thank you in advance for guiding me, explain to me clearly what the problem is
constructor(props) {
super(props);
this.state = {
api: api.api,
annonces: []
}
}
componentDidMount = () => {
console.log(this.state.annonces_id)
this.onLoaodPreloader();
this.getAnnonce();
}
getAnnonce = () => {
var baseApiUrl = this.state.api
axios.get(baseApiUrl + 'annonces' ,
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.usertoken}`
}
}).then((response) => {
console.log(response)
this.setState({
annonces: response.data,
})
}).catch((erreur) => {
console.log(erreur)
})
}
render() {
console.log(this);
const { annonces } = this.state
const renderAnnonces = annonces.map((annonces, index) =>{
return (
<div className="col-md-6 col-sm-6 mb-3" key={index}>
{annonces.meeting_type}
</div>
)
});
return <div className="component-annonces">{renderAnnonces}</div>;
}
pretty sure your response.data is not an array. since .map only works if it is an array.
if you console.log('response data type :', typeof response.data) you will see what type the data is.
your data needs to be in an array for the .map to work
I'm calling an action in componentDidMount as follows
componentDidMount() {
const { allowedEvcCards} = this.props;
allowedEvcCards(id);
}
With these actions i'm doing API calls and receiving some data as the response. I have set the data to a state with my reducer. I want to do some logic in the componentDidMount it self with the data received in the response.
For example in my reducer i'm doing this
case ALLOWED_EVC_SUCCESS:
return {
...state,
allowedEvc: action.data
}
And in componentDidMount i want to use allowedEvc . But it returns undefined as the action call is not complete at the time.
My action
// Get allowed Evc cards
export const ALLOWED_EVC_LOADING = 'ALLOWED_EVC_LOADING';
export const ALLOWED_EVC_SUCCESS = 'ALLOWED_EVC_SUCCESS';
export function allowedEvcCardsLoading() {
return {
type: ALLOWED_EVC_LOADING
}
}
export function allowedEvcCardsSuccess(data) {
return {
type: ALLOWED_EVC_SUCCESS,
data
}
}
export function allowedEvcCards(id) {
return dispatch => {
dispatch(allowedEvcCardsLoading());
axios.get(`${API_URL}/****/****/${id}/*****`, {
headers: {
// 'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
})
.then(res => {
console.log("Allowed EVC response ", res.data);
if (res.data.success === true) {
dispatch(allowedEvcCardsSuccess(res.data));
} else {
console.log("error");
// alert("error");
}
})
}
}
Unfortunately, componentDidMount is only called when a component is mounted. Unless, you unmount it you can't use that property. However, you could use componentDidUpdate since it is called as soon as it receives props.
Read more on this lifecycle method.
Edit: maybe you could try returning the axios promise along with the data and use it.
// Component
async componentDidMount() {
const { allowedEvcCards} = this.props;
const data = await allowedEvcCards(id);
// ... do something with data
}
// Action
export function allowedEvcCards(id) {
return dispatch => {
dispatch(allowedEvcCardsLoading());
return axios.get(`${API_URL}/****/****/${id}/*****`, {
headers: {
// 'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
})
.then(res => {
console.log("Allowed EVC response ", res.data);
if (res.data.success === true) {
dispatch(allowedEvcCardsSuccess(res.data));
return res.data;
} else {
console.log("error");
// alert("error");
}
})
}
}
Expected effect:
In componentDidMount () I download s and saves in the variabletimeId. If timeId is true, passthis.state.timeId to the loadTime () function to https://app/load-id/${id} and call this function. The data returned by this function is saved in the variable checkId. this.state.checkId transfers to theDetails component.
Problem: how to call the function loadId (), after receiving data in componentDidMount ()?
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
checkId: '',
timeId: ''
}
}
componentDidMount() {
axios({
url: `https://app`,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
this.setState({
timeId: res.data.id,
});
})
.catch(error => {
console.log(error);
})
}
loadId = (id) => { //id ---> this.state.timeId
axios({
url: `https://app/load-id/${id}`,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => {
console.log(response);
this.setState({
checkId: response.data
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div>
<Item
/>
<Details
checkId = {this.state.checkId}
/>
</div>
)
}
}
Details
class Details extends React.Component {
constructor(props) {
super(props);
this.state = {
task: ''
};
}
componentDidUpdate(previousProps, previousState) {
if (previousProps.checkId !== this.props.checkId) {
this.setState({
task: this.props.checkId
})
}
render() {
return (
<div >
</div>
);
}
}
You need to call loadId inside the then function.
axios({
url: `https://app`,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
this.setState({
timeId: res.data.id,
});
this.loadId(res.data.id);
})
.catch(error => {
console.log(error);
})
You need to bind loadId() to set state and call it when request in componentDidMount() returns response:
class App extends Component {
constructor (props) {
super(props);
this.state = {
checkId: '',
timeId: ''
}
this.loadId = this.loadId.bind(this); // Bind here
}
componentDidMount() {
axios({
url: `https://app`,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
this.setState({
timeId: res.data.id,
});
this.loadId(res.data.id); // Call loadId
})
.catch(error => {
console.log(error);
})
}
//...
}
I'm able to loop through JSON data to create an array filled with numbers, but when I go to create the list items it doesn't work. The component just renders an empty list.
When I console.log(ticketNumbers) right before the map function, it shows as a collapsed Array [] until I expand it (it then shows all the values)
function apiCall() {
var ticketNumbers = [];
var memId = 'xxx';
var myInit = {
method: 'GET',
mode: 'cors',
headers: {
'authorization': "xxx",
'Access-Control-Allow-Origin':'*',
'content-type': "application/json",
'cache-control': "no-cache"
},
params: {
'orderBy': 'status/name asc',
'pageSize': 300,
'conditions': "resources contains '" + memId + "' AND status/id not in (17,165,36,163,164,42,73,46,78,148,34,132,45,159,60,168,106,51,72,95)"
}
};
axios.get('Url', myInit)
.then((response) => {
console.log(response.data)
for (var ticket in response.data) {
ticketNumbers.push(response.data[ticket].id)
};
})
return ticketNumbers
}
class TicketContainer extends Component {
constructor(props) {
super(props)
this.state = {
data: [],
loading: true,
};
}
componentWillMount() {
this.setState({
data: {
numbers: apiCall()
},
loading: false
})
};
render() {
return(
<div>
{this.state.loading ? 'Loading' : <Tickets data={this.state.data} />}
</div>
)
}
}
class Tickets extends Component {
render() {
const stuff = this.props;
var ticketList = stuff.data.numbers;
console.log(ticketList);
return(
<div>
<ul>Ticket Number
{ticketList.map((ticket, index) => {
return <li key={index}>sweet</li>;
})}
</ul>
</div>
);
}
}
You should correctly use Promise to solve this. First, let's change apiCall so that it will return a Promise:
function apiCall() {
var ticketNumbers = [];
var memId = 'xxx';
var myInit = {
method: 'GET',
mode: 'cors',
headers: {
'authorization': "xxx",
'Access-Control-Allow-Origin':'*',
'content-type': "application/json",
'cache-control': "no-cache"
},
params: {
'orderBy': 'status/name asc',
'pageSize': 300,
'conditions': "resources contains '" + memId + "' AND status/id not in (17,165,36,163,164,42,73,46,78,148,34,132,45,159,60,168,106,51,72,95)"
}
};
return axios.get('Url', myInit)
.then((response) => {
console.log(response.data)
for (var ticket in response.data) {
ticketNumbers.push(response.data[ticket].id)
}
return ticketNumbers;
});
}
You know have a Promise based api that can be used like this:
apiCall().then(ticketNumbers => console.log(ticketNumbers);
We just need to modify componentWillMount know:
componentWillMount() {
apiCall().then(numbers => this.setState({ loading: false, data: numbers });
}
The apiCall function calls an API which is asynchronous process and returning ticketNumbers from function request won't return the result as the return statement will be executed before the API response is ready and ticketNumbers array is populated.
The easiest way for you to do this is to define this function in the React class and directly setState in the callback of axios request
class TicketContainer extends Component {
constructor(props) {
super(props)
this.state = {
data: [],
loading: true,
};
}
componentWillMount() {
this.apiCall()
};
apiCall =() => {
var memId = 'xxx';
var myInit = {
method: 'GET',
mode: 'cors',
headers: {
'authorization': "xxx",
'Access-Control-Allow-Origin':'*',
'content-type': "application/json",
'cache-control': "no-cache"
},
params: {
'orderBy': 'status/name asc',
'pageSize': 300,
'conditions': "resources contains '" + memId + "' AND status/id not in (17,165,36,163,164,42,73,46,78,148,34,132,45,159,60,168,106,51,72,95)"
}
};
axios.get('Url', myInit)
.then((response) => {
var ticketNumbers = [];
for (var ticket in response.data) {
ticketNumbers.push(response.data[ticket].id)
};
this.setState({data: ticketNumbers, loading: false})
})
}
render() {
return(
<div>
{this.state.loading ? 'Loading' : <Tickets data={this.state.data} />}
</div>
)
}
}
class Tickets extends Component {
render() {
const stuff = this.props;
var ticketList = stuff.data.numbers;
console.log(ticketList);
return(
<div>
<ul>Ticket Number
{ticketList.map((ticket, index) => {
return <li key={index}>sweet</li>;
})}
</ul>
</div>
);
}
}
In case you are wondering why the console.log() statment logs the array, check this answer Value below was evaluated just now in JavaScript object
***This component makes a REST API call and parses promise value and renders the data in the form of a table.
load(function) makes the API call and takes orderType as input. OrderType is passed as a query parameter from the navigation component which is not included here.
class SampleController extends React.Component {
constructor(props){
super(props);
let orderType = this.props.location.query.orderType;
this.load = this.load.bind(this);
this.load(orderType);
this.state = {orderType: orderType, data: null}
}
load(orderType) {
let self = this;
console.log("order type is" + orderType);
let baseURL = base_urls.orderMetricsBaseURL;
console.log("base URL is", baseURL);
let url = baseURL + "/getData";
let response_data = fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_type: orderType
})
})
.then((response) => response.json())
.then((responseJson) => {
let order_data = responseJson;
console.log("responseeeeee is: ", order_data);
self.setState({data: order_data});
self.forceUpdate();
})
.catch((error) => {
console.error(error);
});
}
render() {
var content = <DataTable dataList = {this.state.data} />;
return (
content
);
}
}
export { SampleController as default };
remove this line this.load(orderType); from constructor and put it inside componentDidMount:
constructor(){
//....
}
componentDidMount() {
const orderType = this.props.location.query.orderType;
this.load(orderType);
}
You need to do setState to trigger a rerun/rerender on your component.
From the documentation:
setState() does not update the state immediately, so there's a chance you are losing that update because of the forceUpdate(), which shouldn't be needed as setState() will trigger a re-render by itself. Try removing the forceUpdate() call.
Update:
In case you want to call your load() method each time your orderType you'd need to subscribe to the componentWillReceiveProps method.
Example:
class SampleController extends React.Component {
constructor(props){
super(props);
let orderType = this.props.location.query.orderType;
this.load = this.load.bind(this);
this.load(orderType);
this.state = {orderType: orderType, data: null}
}
componentWillReceiveProps(nextProps) {
const orderType = nextProps.location.query.orderType;
const prevOrderType = this.props.location.query.orderType;
if (prevOrderType !== orderType) {
this.load(orderType);
}
}
load(orderType) {
let self = this;
console.log("order type is" + orderType);
let baseURL = base_urls.orderMetricsBaseURL;
console.log("base URL is", baseURL);
let url = baseURL + "/getData";
let response_data = fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_type: orderType
})
})
.then((response) => response.json())
.then((responseJson) => {
let order_data = responseJson;
console.log("responseeeeee is: ", order_data);
self.setState({data: order_data});
})
.catch((error) => {
console.error(error);
});
}
render() {
var content = <DataTable dataList = {this.state.data} />;
return (
content
);
}
}
export { SampleController as default };