Call Function(that render component) in async Function - javascript

Good day everyone.
I have problem with this piece of code:
It's 2 function:
1.renderModal() - it's responsible for rendering ModalSuccess at the moment where data sucesfully will be added to databbase (to inform user about correctly fill form.
Component ModalSuccess when call it's render modal.
2.submitToServer - it's sending all data from redux-form to API.
In end of try, i trying call function renderModal.
How can i make it correctly?
function renderModal() {
return (
<div>
<ModalSuccess/>
</div>
);
}
//async function send to server
export async function submitToServer(values) {
//FUND
try {
let response = await fetch('endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...authHeader()
},
body: JSON.stringify(values),
});
let responseJson = await response.json();
return responseJson;
renderModal();
} catch (error) {
console.error(error);
}
I call submitTo server in 2 places:
1.
export var submit =(values) =>{
let isError =false;
if (isError) {
// throw new SumissionError(error);
} else{
return submitToServer(values)
.then(data =>{
if (data.errors) {
console.log(data.errors);
throw new SubmissionError(data.errors);
} else{
console.log(values)
console.log('server added data to database');
}
});
}
}
2.
<form onSubmit={handleSubmit(submitToServer)}>

I think you can restructure your code a bit better. Instead of returning the modal you can just mount the modal once and control its visibility leveraging the state.
Take a look at how I think your component should be structured.
class Comp extends React.Component {
state = {
isOpen: false
};
submitToServer = async values => {
try {
let response = await fetch("endpoint", {
method: "POST",
headers: {
"Content-Type": "application/json",
...authHeader()
},
body: JSON.stringify(values)
});
let responseJson = await response.json();
this.setState({ isOpen: true });
return responseJson;
} catch (error) {
console.error(error);
}
};
render() {
/* your component */
<ModalSuccess isOpen />;
}
}

As it stands your renderModal() invocation will never register since you are returning once the response it has been returned.
What you'd need to is something like this:
let responseJson = await response.json();
if (responseJson) {
renderModal();
}

Related

I don't know how to get data from fetch correctly

I have a Users class where I want to get data from the server for later writing it to state and passing data from state to the child component
export default class Users extends Component {
constructor(props) {
super(props);
this.state = {
users: this.getUsers(),
};
}
getUsers = async () => {
await return fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(res => {
if(res.ok) {
res.json();
}
})
}
}
this is what the console shows me when I output data about this.state.users
I tried to look for similar situations, but I didn't find anything worthwhile, so I ask for help here. I would be grateful for any advice or help. I'm only learning asynchrony in js
if you use async await, you don't have to pass callback function, just await the promises and update the state incase of successful response.
getUsers = async () => {
try {
const response = await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
});
const users = await response.json();
this.setState({ users });
} catch (error) {
console.log(error);
}
}
and instead of calling getUsers function from the constructor, use componentDidMount
componentDidMount() {
this.getUsers();
}
and your state should be initially null or an empty array
this.state = {
users: []
};
Add componentDidMount and call getUsers and set state.
this.state = {
users: [],
};
getUsers = async () => {
return await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(response => response.json())
.then(res => { this.seState({ users: res })})
.catch(e => { console.log(e)})
}
componentDidMount = () => {
this.getUsers()
.catch(e => console.log(e)
}

Searchbar in react to call table of data

I'm trying to create a searchbar in react that creates a table of data from an API inside the current view.
async function handleSearch() {
console.log("searching...", searchRef.current?.value);
setMessage("Searching...");
var headers = {
"Content-Type": "application/x-www-form-urlencoded",
"auth-token": token,
};
fetch(
"http:"..
{
method: "GET",
headers: headers,
}
)
.then((response) => {
setMessage("");
if (response.status !== 200) {
console.log("erROR", response);
return null;
} else {
console.log("success", response);
this.searched = true;
let productList = response.json()
return productList;
}
})
.then((responseData) => {
console.log("responseData", responseData);
// setting resonseData to productList
setProductList(responseData);
});
}
For some reason this is ridiculously hard to get working.
I would imagine the code above would work and fill the table with the correct array, but is it not.
The JSON response is like this and works properly in the console with the search component currently.
Any idea on how to solve this issue?
It seems you need to add productList as state variable like below.
// We need to initialize with empty array. Otherwise while rendering component for the first time it will try to access map of undefined(productList).
const [productList,setProductList] = useState([]);
// And then in your fetch call you can store data inside productList
fetch(
"http://localhost:5000/adverts/mwap?searchTerm=" +
encodeURIComponent(searchRef.current.value),
{
method: "GET",
headers: headers,
}
)
.then((response) => {
setMessage("");
if (response.status !== 200) {
console.log("erROR", response);
return null;
} else {
console.log("success", response);
this.searched = true;
let productList = response.json()
return productList;
}
})
.then((responseData) => {
console.log("responseData", responseData);
// setting resonseData to productList
setProductList(responseData);
});

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();
})

How do i retrieve data from Async storage and use it in my component?

I have saved a user_id and token in Async storage and i can retrieve it in via console log. with the retrive function. So i know the set function is working perfectly, the functions in deviceStorage all Async.
The problem comes when trying to use the retrieved user_id & token in my component it returns undefined.
How can i get an item from storage and use it later in my code, i want to use the token and userid for a fetch request. Please help me and highlight the best way to do.
import deviceStorage from "../components/services/deviceStorage";
class Jobs extends Component {
constructor() {
super();
this.state = {
jobsData: [],
isLoading: true
};
}
componentDidMount() {
deviceStorage.retrieveToken().then(token => {
this.setState({
token: token
});
});
deviceStorage.retrieveUserId().then(user_id => {
this.setState({
user_id: user_id
});
});
const opts = {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Token " + this.state.token
}
};
fetch("http://example.com/job/" + this.user_id, opts)
.then(res => res.json())
.then(jobs => {
this.setState({
jobsData: jobs,
isLoading: false
});
console.log(jobsData);
})
.catch(error => {
console.error(error);
});
}
render {}
Code for the retrieve and set
import {AsyncStorage} from 'react-native';
const deviceStorage = {
async storeItem(key, item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value
var jsonOfItem = await AsyncStorage.setItem(key, JSON.stringify(item));
return jsonOfItem;
} catch (error) {
console.log(error.message);
}
},
async retrieveItem(key) {
try {
const retrievedItem = await AsyncStorage.getItem(key);
const item = JSON.parse(retrievedItem);
// console.log(item);
return item;
} catch (error) {
console.log(error.message);
}
return
}
};
export default deviceStorage;`
There are two ways to get the data stored in async storage:
(1) Promise method. Here your code does not wait for the block to finish and returns promise which is accompanied by .then clause if the promise resolves and .catch clause if there is error.
(2) Async and Await method. This is more preferred, here your code waits for the execution before proceeding one of the example to refer is below:
retrieveData() {
AsyncStorage.getItem("id").then(value => {
if(value == null){
//If value is not set or your async storage is empty
}
else{
//Process your data
}
})
.catch(err => {
// Add some error handling
});
Second Method example:
async retrieveData() {
try {
let value = await AsyncStorage.getItem("id");
if (value !== null) {
//you have your data in value variable
return value;
}
}
catch (error) {
// Error retrieving data
}
}
your retrieve data storage methods should look like this
retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
// We have data!!
return value;
}
} catch (error) {
// Error retrieving data
}
return null;
};
Adding to the previous solutions
//function to retrieve data
async function retrieveItem(key) {
try {
const retrievedItem = await AsyncStorage.getItem(key); //dataType String
const item = JSON.parse(retrievedItem);//dataType object
return item;
} catch (error) {
console.log(error.message);
}
return
}
//function call
retrieveItem(key).then((value) => {
//unlike normal function call, this waits for the promise to complete
return value;// actual value not the promise
})
.catch((error) => {
console.log('Error: ' + error);
});

Subscribe http.post that is placed inside a promise Angular 6

It gets complicated to me when I mix the promise with subscribe and another async task together.
This is my auth service:
getCurrentUserToken(){
return new Promise((resolve,reject)=>{
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
resolve(idToken)
}).catch(function(error) {
reject(error)
});
})
}
This is my HTTP service:
sendEmail(email) {
return this.authService.getCurrentUserToken().then(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions)
})
}
This is how I call the sendEmail(email) function at the component:
Observable.fromPromise(this.httpService.sendEmail(element)).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
I have to pass currentUserToken to the API to let the API authenticate the user session. Still, both of the the getCurrentUserToken() sendEmail() are running in async, so I have to use Promise to pass the Token to sendEmail() function, and let the sendEmail function to call the API to send the email.
Without the promise, I am able to subscribe to the http.post like this:
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
Unfortunately, I screwed it up when I added the promise into it, and the console.log is returning this:
Observable {_isScalar: false, source: Observable, operator: MapOperator}
Please advise on how to subscribe to the http.post that is placed inside the Promise.
There's seriously no need of Complicating things here.
I'll use async/await syntax here and for that, we'll have to work with Promises instead of Observables. Good thing is, we can leverage the toPromise() method on an Observable value to change it to a Promise
Focus on my comments in the code as well
Here's the implementation
For getCurrentUserToken
getCurrentUserToken() {
return firebase.auth().currentUser.getIdToken(true);
// This will already return a Promise<string>
// So no need to do a .then and then return from there.
}
For sendEmail
async sendEmail(email) {
// Since getCurrentUserToken returns a Promise<string> we can await it
const token = await this.authService.getCurrentUserToken();
// token will now have the Current User Token
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions).toPromise();
// Notice how we're calling the .toPromise() method here
// to change Observable into a Promise
}
How to use it?
This code will go in your Component Method where you were previously calling this.httpService.sendEmail. DO MAKE SURE TO MARK THAT FUNCTION AS async THOUGH.
// We can only await something in a function which is declared of type async
async sendEmail() {
try {
const data = await this.httpService.sendEmail(element);
// Since sendEmail again returns a Promise, I can await it.
console.log(data);
} catch (error) {
console.log(error);
}
}
Why don't we use Observable instead of Promises here.
getCurrentUserToken() {
return new Observable(obs => {
firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then(function(idToken) {
obs.next(idToken);
obs.complete();
})
.catch(function(error) {
obs.error(error);
});
});
}
sendEmail(email): Observable {
return new Observable(obs => {
this.authService.getCurrentUserToken().subscribe(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Basic server-Password'
})
};
let data = email;
data['idToken'] = token;
this.http
.post(this.apiServer + 'sendEmail', data, httpOptions)
.subscribe(
result => {
obs.next(result);
obs.complete();
},
error => {
obs.error();
}
);
});
});
}
// now call the service from Component like this.
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
));

Categories

Resources