React-native Invoke API from one common handler class - javascript

I have a common function which uses FETCH to get data from external web service. this function will be invoked and parsed in multiple screens under componentDidMount(). instead of repeating the same code at multiple places, I put below under a common class, but unfortunately, data is not returned to those screens.
Common Function
export function convertValue(fromVal, toVal) {
var requestObj = {};
let apiEndpoint = '<target endpoint>'
return fetch(apiEndpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
.then((response) => response.json())
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
console.log('Error: ', error);
});}
Sample call below, and no pop-up when screen loaded.
componentDidMount () {
AsyncStorage.getItem('user_default').then((value) => {
this.setState({userDefault: value});
}).then((value) => {
var sample = convertValue('A', 'B');
Alert.alert(
'Success',
JSON.stringify(sample),
[
{text: 'OK',
onPress: () => {
console.log('.');
}}
]
)
});}

componentDidMount () {
AsyncStorage.getItem('user_default').then((value) => {
this.setState({userDefault: value});
convertValue('A', 'B').then((json)=>{
alert(json)
})
})}
This might work for you. The problem was improper chaining of asynchronous calls.

Nick is right, found the right way -
convertValue('A', 'B')
.then((responseJson) => {
this.setState({returnedValue: responseJson.convertedValue});
});

Related

Access this from another class React Native

I am currently factoring my code so as not to repeat the same lines x times, so I created a Functions.js file which I use to call functions from other classes. The problem is, that I cannot execute the function while keeping the properties of this to carry out setState, redirects etc. Here is an example, it will be more telling:
Class in functions.js :
export class KoHttpRequest extends React.Component {
constructor(props) {
super(props);
this.postRequest = this.postRequest.bind(this);
}
postRequest = async(url, json, accessToken) => {
this.setState({loaded: false, validatingAction: false});
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization' : 'Bearer '.concat(accessToken)
},
body: json
}).then((response) => {
if (response.ok === true) {
this.fetchData().then(() => {
this.setState({loaded: true}, () => {
this.userIsValidatingAnAction();
setTimeout(() => {this.setState({validatingAction: false})}, 1000);
});
})
} else {
let error = JSON.stringify(response.headers.map);
this.props.navigation.navigate('Accueil', {failedAction: true, errorReason: error.split('"error-reason":').pop().split('}},')[0].concat(' URL : '.concat(response.url))});
}
})
.catch((error) =>{
console.error(error);
});
}
render() {
return null;
}
}
And in the file where I want to call it :
import { KoHttpRequest } from '../Components&Functions/Koust.js';
createNewInvoice = () => {
new KoHttpRequest().postRequest('https://koupp.com/apex/rest/mobile/facture', JSON.stringify({
type:'C',
numero:this.state.invoiceNumber,
date_liv:this.state.pickedDate,
provider_id:this.state.selectedProvider
}), this.state.accessToken);
};
So, to explain clearly, in the class, the .then() and .error() are same for all request I do in my app, that's why I need the code here and not in the class that is calling it.
Unfortunely, I don't understand how I can tell the function that the 'this' referenced to use is in the other component. Cause actually the function is using themselve props..
Thanks for help.
I'm just trying to access the setState of class that is calling it.
In Functions.js, when it's using setState, I want it set the state of the other class actually
EDIT :
I think I found a solution using a callback.
createNewInvoice = () => {
this.setState({loaded: false, validatingAction: false}, () => {
new KoHttpRequest().koPostRequest('https://koupp.com/apex/rest/mobile/facture', JSON.stringify({
type:'C',
numero:this.state.invoiceNumber,
date_liv:this.state.pickedDate,
provider_id:this.state.selectedProvider
}), this.state.accessToken, this.props, function(result) {
if (result.status === 200) {
this.fetchData().then(() => {
this.setState({loaded: true}, () => {
this.userIsValidatingAnAction();
setTimeout(() => {this.setState({validatingAction: false})}, 1000);
});
})
}
});
});
};
But now, that's the callback function that can't access "this".
EDIT_2:
Find it. Just need to replace function() {..} by () => {..}
Thanks!

how to mock multiple fetch calls being made in the same function

I have a function, as below, that performs 2 fetch calls within the same function
getNames() {
var qs = require("qs");
fetch(<URL>,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: qs.stringify({
firstName: this.state.firstName,
lastName: this.state.lastName
})
})
.then(response => response.json()).then((data) => {
console.log(data)
});
var url = new URL(<someURL>)
fetch(<someURL>).then(response => response.json()).then((data) => {
...do something...
}
})
.catch(error => {
alert("no response");
console.log(error);
});
}
I am testing this using Jest and Enzyme on React. The above belongs to the GetName component. Below is my test case:
describe('getName', () => {
const wrapper = shallow(<GetName />).instance();
beforeEach(() => {
global.fetch.resetMocks();
});
it('positive flow', () => {
global.fetch.mockResolvedValue(
new Response(JSON.stringify({data: "mockData"}))
);
const state = {
firstName: "don",
lastName: "Lee"
};
wrapper.setState(state);
const actualValue = wrapper.getNames();
expect(actualValue).toBeUndefined();
});
});
Once I do this, I get an error that TypeError: body used already for: undefined
I understand that the fetch here is being used for the POST call, but how do I make sure that I can mock both the fetch calls within the function?
I have also tried fetch.mockResponse and fetch.mockResponses and also fetch.mockResponseOnce. None of them seem to help me mock them more than once and I get this error with all functions mentioned.
Is there any other way to mock both the fetch calls?

Problem with nested fetch request in React

New to React, I'm currently trying to create a data table with data from an API.
I want to have a first fetch, and then run another with response from the first (id) in order to complete my table.
Here is my code :
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {},
data: []
};
}
componentDidMount() {
this.setState({
user: JSON.parse(localStorage.getItem('user'))
}, function () {
this.loadAllObjectsInfo()
});
}
// Fetch all object info in order to fill the table
loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.userId,
'bbtoken': this.state.user.secret
},
};
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((data) => {
this.setState({ data: data })
})
}
With this code, I have the data I want to render my table but I need to run another fetch to get other info with the id coming from the first request.
How can I do that nested fetch request ?
Thanks a lot,
Matthieu
You can easily manage this with async/await:
async loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.user
'bbtoken': this.state.user.secret
},
};
let response = await fetch('https://xxxxx/api/objects', requestOptions);
let data = await response.json();
// here is another fetch - change to fit your request parameters (this is just example)
let info = await fetch('https://xxxxx/api/objects/' + data.id);
this.setState({ data });
}
You can read more about async function.
#JourdanM, you should return a new fetch request from one of the then handlers. I've made a simple snippet for you. There are no data validators and spinners. This is a simple showcase. =)
A fetch request returns a promise, and you can chain promises by simply returning them from the then handlers. Here is a good article about it, it has great examples: https://javascript.info/promise-chaining
function fetchUser (user) {
return fetch(`https://api.github.com/users/${user.login}`)
}
class User extends React.Component {
state = {
user: null
}
componentDidMount () {
fetch("https://api.github.com/users")
.then(response => response.json())
.then(users => fetchUser(users[0]))
.then(response => response.json())
.then(user => {
this.setState({user})
})
}
render () {
return (
<div>
<pre>{JSON.stringify(this.state.user, null, 2)}</pre>
</div>
)
}
}
ReactDOM.render(<User />, document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can write the code as below.
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res1) => {
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res2) => {
this.setState({ data: res2 });
});
});
Hope this will work for you!
You can also use axios like below
axios.post(url, data, header).then(res => {
if(res.status === 200){
console.log('1st data')
axios.post(url, data, header)
.then(response => {
if (response.status === 200) {
console.log('2nd data')
} else {
console.log('2nd error')
}
});
}else{
console.log('1st error')
}
});

Send surveyjs result to API

I am trying to send surveyjs results to my API.
In mounted(), I make a GET request with vue-resource, get questions from my dB and then set surveyjs. To send the results, I tried to use this.$http.post, in the surveyJS onComplete function, but I got Cannot read property 'post' of undefined. Also, I tried to put a watch on the result variable, but it did not work.
mounted() {
this.$http
.get("myAPI")
.then(res => res.json())
.then(questions => {
this.questions = questions;
this.survey = new SurveyVue.Model(this.questions.pesquisa);
this.survey.locale = "pt";
this.survey.onComplete.add(function(survey) {
this.result = survey.data;
this.$http
.post(
`myAPI`,
this.result,
{ headers: { "Content-Type": "application/json" } }
)
.then(response => {
console.log(response);
UIkit.notification({
message: "Success",
pos: "top-center",
status: "success"
});
})
.catch(error => {
console.log(error);
UIkit.notification({
message: "Erro",
pos: "top-center",
status: "danger"
});
});
});
})
.catch(error => {
console.log(error);
UIkit.notification({
message: "Error",
pos: "top-center",
status: "danger"
});
});
}
To get access to this inside the onComplete.add()'s parameter, you could replace your regular function with an arrow function:
this.survey.onComplete.add(survey => {
this.result = survey.data;
/* rest of your code... */
})
An alternative is to place this into a variable, which can be used to access the outer this:
const that = this;
this.survey.onComplete.add(function(survey) {
that.result = survey.data;
/* rest of your code... */
})
Read more about this.
The gist of it is that inside the the function, the function's this overrides the component's this, unless it's an arrow function, which purposefully doesn't have a this so the outside one is available.

ReactJs : How to prevent componentWillUpdate from re-rendering multiple times

I am creating a simple Todo App, I am using componentDidMount to display the data from the database. But the problem is, Once I add a new data the data gets stored but it doesn't display on to the page unless I refresh it.
Then I came across componentDidUpdate. It works perfectly, But it re-renders multiple times, What I mean is it keeps requesting the server to check for new data.
I am using Express for backend
So could anyone tell me how to prevent this ? or if there is any better solution?
Here is the current code:
class Navbar extends Component {
state = {
userArray: [],
username: "",
email: ""
};
//Storing the Data
addBtn = e => {
e.preventDefault();
var data = {
username: this.state.username,
email: this.state.email
};
fetch("/user", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
})
.then(response => {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
return response.json();
})
.then(data => {
console.log(data);
if (data === "success") {
console.log("Yay");
}
})
.catch(err => {
console.log(err);
});
console.log(this.state.userArray);
};
componentDidMount() {
this.displayData();
}
componentWillUpdate() {
this.displayData();
}
//Displaying the Data
displayData() {
fetch("/user")
.then(data => data.json())
.then(data => {
this.setState({
userArray: data
});
});
}
//Handling the input values
logChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
So, let's try and understand why there was a lot of calls to the server.
When componentDidMount is created, you called displayData, which then setState. As soon as setstate is called, it calls componentDidUpdate which calls displayData again, which then calls setState. And the loop goes on (probably till you run out of memory).
You could try this class:
import React from 'react';
export default class Navbar extends React.Component {
state = {
userArray: [],
username: '',
email: ''
};
componentDidMount() {
this.displayData();
}
addBtn = e => {
e.preventDefault();
var data = {
username: this.state.username,
email: this.state.email
};
fetch('/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
console.log(data);
if (data === 'success') {
this.displayData();
}
})
.catch(err => {
console.log(err);
});
};
displayData() {
fetch('/user')
.then(data => data.json())
.then(data => {
this.setState({
userArray: data
});
});
}
}
Basically, what I did was I removed the call to displayData in componentDidUpdate and then called the displayData when the ApI call was successful
componentDidMount is the right place to load the first time, and then, after creating new Todo, you need to refresh the list right after POST request complete
.then(data => {
console.log(data);
if (data === "success") {
console.log("Yay");
this.displayData();
}
})
To impove performace, you should return new Todo record after POST, so you only push it to the list userArray in state, no need to fetch whole list again
For this, you need to first understand how componentDidMount and componentWillUpdate works in React.
They are lifecycle methods of react.
componentDidMount gets called after the component is mounted. It gets called only once and never gets called again if not unmounted and mounted again.
componentWillUpdate gets called every time state changes and re-rendering is going to happen.
As commented by #trixn:
You need to call this.setState() in addBtn when you have the data instead of repeatedly calling this.displayData()
Everyone gave the right answer , But there is a tiny mistake.
You should call the displayData() outside of the if condition
.then(data => {
console.log(data);
if (data === "success") {
console.log("Yay");
}
this.displayData();
})

Categories

Resources