How to pass parameter in api url based on selected option? - reactjs

I would like to fetch data based on selected job id.
Job id should be selected from drop down list.
Once the job id will be selected, api url should be adjusted with the property job id.
I added the select option and fetch statement. However I cannot pass the parameter in the url.
const jsonify = res => res.json();
var chart_request = new Request(
`https://xxxx.com/prod/job-id?job_id_number=${this.state.selectVal}`,
{
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json'
})
}
);
const dataFetch = fetch(chart_request).then(jsonify);
export default class ZYZ extends Component {
constructor(props) {
super(props);
this.state = {
selectVal : "650"
}
}
setSelectValue = (event) => {
this.setState({
selectVal: event.target.value
});
}
render() {
return
<React.Fragment>
<select value={this.state.selectVal} onChange={this.setSelectValue}>
<option value = "650">650</option>
<option value = "1052">1052</option>
</select>
<p>{this.state.selectVal}</p>
</React.Fragment>
}
}

You can't use your state outside your class.
But if you insist, you can use componentDidMount & componentDidUpdate lifecycle methods on initial load and every select respectively, and pass the id as an argument to fetchData & chart_request as follow:
componentDidMount() {
// calling fetch, resolving the promise, and storing the data in state
fetchData(this.state.selectVal).then(data => this.setState({ data }));
}
componentDidUpdate() {
// same as above
fetchData(this.state.selectVal).then(data => this.setState({ data }));
}
The modifications for chart_request & fetchData:
const chart_request = id =>
fetch(
`https://xxxx.com/prod/job-id?job_id_number=${id}`
)
.then(response => response.json()) // instead of "jsonify"
.then(data => JSON.stringify(data))
.catch(error => error);
const fetchData = id => chart_request(id);
I have modified the SandBox so you can test your output.

Related

accessing react state variable outside of a function in a component is printing null

I have a AutoHospitals component and I am trying to get the value of a state variable outside the .then function but it is printing null.
Here is the code snippet where this.state.retrievedmrnNumber is printing.
.then(response => {
console.log("Extracting mrnNumber from Hospitals API results")
console.log(response.data.mrnNumber);
let retrievedMrnNo = response.data.mrnNumber;
this.setState({ retrievedmrnNumber: retrievedMrnNo});
console.log("Printing Retrieved mrn number from state");
console.log(this.state.retrievedmrnNumber);
})
Here is the console log statements outside the above .then function, where it is printing null:
console.log("Outside of then function: Printing Retrieved mrn number from state");
console.log(this.state.retrievedmrnNumber);
How do I access it outside of .then function?My ultimate goal is to use the value on this line:
selectedHospitals = [{label: this.props.value[0] && this.state.retrievedmrnNumber || 'Select'}]
Full component code is below:
export class AutoHospitals extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
selectedHospitalValues: null,
selectedHospitals: [],
retrievedmrnNumber:null,
loading: false
};
this.onChange = this.onChange.bind(this);
}
onChange = (val) => {
this.setState({
value: val,
selectedHospitalValues: val
});
this.props.onChange(val)
};
fetchRecords() {
let url = 'myurl'
this.setState({
loading: true
});
return axios
.get(url)
.then(response => {
let selectedHospitals;
if(this.props.value[0]){
console.log('this.props.value is DEFINED - Request has been EDITED!!!!')
// START: Logic to get MRN Number
let hospitalIdtoRetrieveMRNNumber = this.props.value[0].hospitalId;
axios
.get('api/Hospitalses/'+hospitalIdtoRetrieveMRNNumber)
.then(response => {
console.log("Extracting mrnNumber from Hospitals API results")
console.log(response.data.mrnNumber);
let retrievedMrnNo = response.data.mrnNumber;
this.setState({ retrievedmrnNumber: retrievedMrnNo});
console.log("Printing Retrieved mrn number from state");
console.log(this.state.retrievedmrnNumber);
})
// END: Logic to get mrn Number
console.log("Outside response block: Printing Retrieved mrn number from state");
console.log(this.state.retrievedmrnNumber);
selectedHospitals = [{label: this.props.value[0] && this.state.retrievedmrnNumber || 'Select'}]
//let selectedHospitals = [{label: this.props.value[0] && 'mrn # 1234' || 'Select'}]
}else {
console.log('this.props.value is UNDEFINED - it is a NEW REQUEST');
}
this.setState({
loading: false
});
if (this.props.value) {
this.props.value.forEach(e => {
selectedHospitals.push(response.data._embedded.Hospitalses.filter(hospitalSet => {
return hospitalSet.hospitalId === e.hospitalId
})[0])
})
}
this.setState({
selectedHospitals: response.data._embedded.Hospitalses.map(item => ({
label: (item.mrnNumber.toString()),
projectTitle: item.projectTitle,
hospitalId: item.hospitalId,
})),
selectedHospitalsValues: selectedHospitals
});
}).catch(err => console.log(err));
}
componentDidMount() {
this.fetchRecords(0)
}
render() {
return (
<div>
<Hospitalselect value={this.state.selectedHospitalsValues} options={this.state.selectedHospitals} onChange={this.onChange } optionHeight={60} />
<div className="sweet-loading" style={{ marginTop: '-35px' }}>
<ClockLoader
css={override}
size={30}
color={"#123abc"}
loading={this.state.loading}
/>
</div>
</div>
);
}
}
It's all about sync\async. Consider following two examples:
With then which is fully async (and do not allowing any waits) :
export const download = (url, filename) => {
fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
}).then((transfer) => {
return transfer.blob(); // RETURN DATA TRANSFERED AS BLOB
}).then((bytes) => {
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(bytes); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}).catch((error) => {
console.log(error); // OUTPUT ERRORS, SUCH AS CORS WHEN TESTING NON LOCALLY
})
}
With await, where the response becomes sync:
export const download = async (url, filename) => {
let response = await fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
});
try {
let data = await response.blob();
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(data); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}
catch(err) {
console.log(err);
}
}
The await example can be called as anonymous function (hope the normal call also possible):
(async () => {
await download('/api/hrreportbyhours',"Report "+getDDMMYYY(new Date())+".xlsx");
await setBtnLoad1(false);
})();
I believe the Promise from the async axios.get function hasn't resolved by the time you call the state value in selectedHospitals
Try passing a callback function to the return of the then statement:
.then(response => {
console.log("Extracting mrnNumber from Hospitals API results")
console.log(response.data.mrnNumber);
handleRequest(response.data.mrnNumber);
console.log("Printing Retrieved mrn number from state");
console.log(this.state.retrievedmrnNumber);
})
And here is the callback which can use setState:
handleRequest(data) {
this.setState({ retrievedmrnNumber: data});
}
EDIT To bind handle request to this properly try making it an arrow function:
handleRequest = (data) => this.setState({retrievedmrnNumber:data});

React - Trying to update Fetch call in Parent Component based off of User Input in Child Component

So I am still very much a beginner when it comes to React. I am trying to build an application where the user inputs their location, which would then dynamically update the URL within my fetch call. Based off the results of the fist fetch call, I would then dynamically update a second fetch call, to a different API, providing me with the information needed.
As it stands right now, both Fetch calls are properly working, when provided the right information. The problem I am currently running into is, I don't believe my parent component is re-rendering with the update information. I am trying to console log one of my states but it keeps coming back as blank. The weird part is, the other state that is being created within the child component, is coming back with the right information. Any help would be greatly appreciated!
APP.JS
import Home from './Home/Home';
import Userinput from './UserInput/Userinput';
const url =
'https://api.openuv.io/api/v1/uv?lat=-33.34&lng=115.342&dt=2018-01-24T10:50:52.283Z';
const url1 = `http://www.mapquestapi.com/geocoding/v1/address?key=${process.env.REACT_APP_MAP_API_KEY}`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
uvIndex: '',
lat: '',
long: '',
inputValue: '',
};
this.handleInputValue = this.handleInputValue.bind(this);
}
handleInputValue(val) {
this.setState({ inputValue: val });
}
componentDidMount() {
let newVal = this.state.inputValue;
fetch(`${url1}&location=${newVal}`, {
method: 'GET',
headers: {
'content-type': 'application/json',
},
})
.then((res) => res.json())
.then((res) => {
this.setState({
lat: res.results[0].locations[0].latLng.lat,
long: res.results[0].locations[0].latLng.lng,
});
// this.setState({ uvIndex: res });
})
.catch((err) => {
console.error(err);
});
fetch(url, {
method: 'GET',
headers: {
'content-type': 'application/json',
'x-access-token': `${process.env.REACT_APP_UV_API_KEY}`,
},
})
.then((res) => res.json())
.then((res) => {
console.log(res);
this.setState({ uvIndex: res });
})
.catch((err) => {
console.error(err);
});
}
render() {
console.log(this.state.lat); #this state comes back as blank
console.log(`${url1}&location=${this.state.inputValue}`); # this state comes back with the update userinput
return (
<div>
<header>
<Home />
</header>
<div>
<Userinput handleInput={this.handleInputValue} />
</div>
</div>
);
}
}
export default App;
Userinput.js
class Userinput extends Component {
constructor(props) {
super(props);
this.state = {
inputVal: '',
};
this.onInputChange = this.onInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// handle input change event
onInputChange(e) {
this.setState({ inputVal: e.target.value });
}
// handle button click event and pass data in parent
handleSubmit() {
this.props.handleInput(this.state.inputVal);
}
render() {
return (
<div>
<input value={this.state.inputVal} onChange={this.onInputChange} />
<input type='button' value='Submit' onClick={this.handleSubmit} />
</div>
);
}
}
export default Userinput;
Consider this.setState in the App Class. The first fetch() is writing your state like so:
this.setState({
lat: res.results[0].locations[0].latLng.lat,
long: res.results[0].locations[0].latLng.lng,
});
The second fetch() call sets a new state object, which removes the result from the first fetch() call, or whatever fetch() resolves faster:
.then((res) => {
console.log(res);
this.setState({ uvIndex: res });
})
You can fix this issue with object spread operator:
this.setState({...this.state, uvIndex: res });
This will keep a copy of your state, and only overwrite uvIndex (if it was set before)
Also consider the handleInputVal Method in App.js. I think same problem here. the state is overwritten with a new Object (setState doesn't update the state, it creates a new State), which means that lat and long are being set to undefined 🤔
handleInputValue(val) {
//this.setState({ inputValue: val }); sets lat and long to undefined
this.setState({...this.state, inputValue: val }); //keep lat and long values, and update inputValue with val
}

Using this.setstate with Fetch API

I'm trying to use a fetch call to retrieve some API data. Unfortunately I cannot use this to set the state.
consructor() {
this.state = {
weather: ""
};
this.search = this.search.bind(this);
}
search(postalCode) {
const url = `https://api.weatherbit.io/v2.0/forecast/daily?&postal_code=${postalCode}&key=${API_KEY}&days=7`;
fetch(url)
.then(response => response.json())
.then(data => console.log(data));
}'
This code works fine, and prints an object to the console containing the correct data.
consructor() {
this.state = {
weather: ""
};
this.search = this.search.bind(this);
}
search(postalCode) {
const url = `https://api.weatherbit.io/v2.0/forecast/daily?&postal_code=${postalCode}&key=${API_KEY}&days=7`;
fetch(url)
.then(response => response.json())
.then(data => this.setState({weather: data}))
.catch(error => console.log(error));
}
As soon as I try to use the data to update my state, I get the "this.setState is not a function" error. I've bound the search function in the constructor.
I've even tried to bind it like this:
fetch(url)
.then(response => response.json())
.then(data => this.setState({ weather: data })).bind(this);
This doesn't work either. Anyone have any ideas?
I can't see your class definition, but I believe that using the state requires you to extend Component
The issue you are getting is that this component doesn't have the state functions in it.
Here's some code from a working project of mine using similar fetches, returned by the services:
type HomeProps = {}
type State = {
projects: ProjectModel[],
skills: SkillModel[],
resume: JobModel[]
}
export default class Home extends Component<HomeProps, State> {
private projectService: ProjectService;
constructor(props: HomeProps) {
super(props);
this.projectService = new ProjectService();
this.state = {
projects: []
}
}
componentDidMount() {
this.getProjects();
}
private getProjects() {
this.projectService.retrieveItems().then(projects => {
this.setState({projects});
})
}
Here is the service:
export default class ProjectService {
url = "http://api.flynndev.us";
async retrieveItems() {
return fetch(`${this.url}/projects/all`)
.then(response => response.json());
}
async getItem(itemLink: string) {
return fetch(`${this.url}/projects/${itemLink}`)
.then(response => response.json());
}
}
Note: I'm using Typescript. Feel free to ignore the typing stuff for regular React

map axios response then use it for another response

I want to map an array of images, but because Directus is identifying an image or a file as an id in the collection's item.
right now I need to map the array of id from the response in the first request then using that id for making another get request in another endpoint which is right here is files.
the database that I use is DirectusCms, and the front end is react.js
class ComponentToPrint extends React.Component {
constructor(props) {
super(props);
this.handleLoad = this.handleLoad.bind(this);
this.state = {
nama: [],
kk:[],
ijazah:[]
};
}
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
componentWillUnmount() {
window.removeEventListener('load', this.handleLoad)
}
handleLoad() {
let nama_1;
let id_kk;
const url = `https://sensor/public/gemaclc/items/pendaftar?access_token=sensor`
const url2 = `https://sensor/public/gemaclc/files/${id_kk}?access_token=sensor`
axios(url, {
method: 'get',
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
const nama = res.data.data;
const nama = res.data.data;
console.log( nama)
this.setState( {nama} );
})
.catch(error => console.error('Error:', error))
// .then(res => {console.log('Success:', nama)})
// this.setState( nama );
// console.log(nama)
axios(url2,{
method:`get`,
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
id_kk = res.data.data;
console.log( id_kk)
// this.setState( {id_kk} );
})
}
render() {
return (
<>
{this.state.nama.map(node =>
<div key={node.id}>
<h2>Nama :{node.nama}</h2>
<h2>Tanggal lahir : {node.tanggal_lahir}</h2>
<h2>Tempat lahir : {node.tempat_lahir}</h2>
<h2>Email : {node.email}</h2>
<h2>Email : {node.telepon}</h2>
<h2>kk : {node.kartu_keluarga}</h2>
</div>
)}
</>
);
}
}
When requesting from the items endpoint in Directus one can supply a query parameter called fields, with this you can get nested information from relations, including the hash etc, see: https://docs.directus.io/api/items.html#fields-optional
If you have the private hash, you can use the assets endpoint to get the actual image, see: https://docs.directus.io/api/assets.html#get-an-asset
PS: You haven't mentioned what your problem is, so I guessed it based on my overall knowledge of the topic - next time also mention your exact problem, what you've tried and what didn't work etc

componentDidUpdate keeps getting called

componentDidUpdate () {
this.showPosts();
}
showPosts = async () => {
var userID = await AsyncStorage.getItem('userID');
fetch(strings.baseUri+"getPostWithUserID", {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"user_id": userID
})
})
.then((response) => response.json())
.then((responseJson) => {
let jsonObj = JSON.parse(JSON.stringify(responseJson));
if (jsonObj.status=="true") {
this.setState({
data: responseJson.data,
imageSrc: responseJson.data.imgSrc,
});
}
else {
this.setState({show: false});
}
})
}
I'm calling showPosts function from componentDidUpdate to show my updated Flatlist. But componentDidUpdate keeps getting called. Should I use shouldComponentUpdate ?
========================== UPDATED CODE ============================
This is from Home Screen
async componentDidMount () {
this._isMounted = true;
await this.showPosts();
}
componentDidUpdate () {
this.showPosts();
}
showPosts = async () => {
try {
var userID = await AsyncStorage.getItem('userID');
fetch(strings.baseUri+"getPostWithUserID", {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"user_id": userID
})
})
.then((response) => response.json())
.then((responseJson) => {
let jsonObj = JSON.parse(JSON.stringify(responseJson));
if (jsonObj.status=="true") {
this.setState({
data: responseJson.data,
imageSrc: responseJson.data.imgSrc,
});
}
else {
if (this._isMounted) {
this.setState({show: false});
}
}
})
.catch((error) => {
console.error(error);
});
}
catch (err) {
console.warn(err);
}
}
componentWillUnmount () {
this._isMounted = false;
}
This is Image Descrpiption screen from where I'll navigate back to Home Screen
postData = async () => {
this.setState({loading: true});
var location = await AsyncStorage.getItem('location');
var path = await AsyncStorage.getItem('path');
var post_type = await AsyncStorage.getItem('post_type');
var userId = await AsyncStorage.getItem('userID');
var newPath = path.split("/");
var imageName = newPath[newPath.length-1];
const formData = new FormData();
var media = {
uri: path,
name: imageName,
type: 'image/jpg',
};
formData.append('image', media);
formData.append('user', userId);
formData.append('description',this.state.description);
formData.append('location',"usa");
formData.append('post_type',post_type);
formData.append('userprofile_picture',imageName);
fetch(strings.baseUri+"addPosts",{
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
body: formData,
})
.then((response) => response.json())
.then((responseJson) => {
let jsonObj = JSON.parse(JSON.stringify(responseJson));
if (jsonObj.status=="true") {
this.props.navigation.popToTop()
&& this.props.navigation.navigate('Home'); // This navigates me to the HomeScreen
}
else {
}
})
.catch((error) => {
console.error(error);
});
}
ComponentDidUpdate is an update LifeCycle Hook, this will get triggered when there is something is changed in the component State or Props.
Coming to your code:
You are calling a handler showPosts to setState, that will again trigger the update lifecycle.
This will lead to an infinite loop.
Solution
If you want to load the posts only in the first time, then move to Creational Life Cycle hook ( componentDidMount ).
componentDidMount() { // This just gets called once in creational lifecycle //
this.showPosts(); }
if you want this to always have the latest data, then there are two ways
Updating component is in the same component tree branch:, In this case , it's easy to achieve this you can pass the state from the updating component down to child component has props, your job is done OR if they are siblings then do a level up you can move the state one level up and have it coming in has props.
Updating component is in the different component tree branch: I recommend using REDUX, this is the main use of redux.
shouldComponentUpdate Yes definitely you can use this to verify the data and the load if needed, but be careful by using this your components update depends on the code in this.
Please check https://reactjs.org/docs/react-component.html#shouldcomponentupdate
You just need to call this in that way if you do in ComponentDidUpdate and update state in the method call by ComponentDidUpdate then a infinite loop start.
componentDidMount () {
this.showPosts();
}
================EDITED=======================
If you want to use only ComponentDidUpdate then you can use it like.
componentDidUpdate(prevProps, prevState) {
// only update if not match I don't know what's your data is so add a
// simple check like we use for strings.
if (prevState.data !== this.state.data) {
this.showPosts();
}
}
Just use prevState to match.
You can do this too
Common parent component
Create a new component say Posts.
import React, { Component } from 'react';
import HomeScreen from '../../HomeScreen';
import ImageDescription from '../../ImageDescription';
class Posts extends Component {
constructor(){
super();
this.state = {
dataEditted: false;
}
}
newDataHandler = () =>{
this.setState({dataEditted:true}); // this is flag to identify that there is change in data //
}
resetnewDataHandler = () =>{
this.setState({dataEditted:false}); // this handler is used to reset the falg back to initial //
}
render () {
const homeScreen = <HomeScreen editted={this.state.editted} resetHandler={this.resetnewDataHandler}/>;
const imageDescription = <ImageDescription newdataHandler={this.resetnewDataHandler}/>
return (
<div>
{homeScreen}
{imageDescription}
</div>
)
}
}
export default Posts;
This component is going to serve as a bridge to move data between.
Whenever there is fresh data in ImageDescription Component use the newDataHandler passed has props to update the common parent, then the dataEditted will be updated and passed has props to homeScreen Component, now in this componentDidUpdate of homeScreen check whether its true, then call this.showPosts() and also call resetnewDataHandler.
componentDidUpdate() is called when the state is changed (calling setState()) and if you do it inside the showPosts that is also inside the componentDidUpdate() you are creating an infinite state updating.

Resources