Reputation: 1583
I have created a small react App that presents documents from a SharePoint site.
To make the App work correctly I am having to add setTimeouts, but I know there must be a better way using callbacks or promises or something >.<
My knowledge is lacking, so could somebody please point me in the right direction?
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
// Checks security permissions - MAKES A GET REQUEST
setTimeout(() => {
this.getSecurityGroupUsers();
}, 1000);
// Ghecks if user can access docs
setTimeout(() => {
this.checkForDocAccess();
}, 2000);
// Gets documents - MAKES A GET REQUEST
setTimeout(() => {
this.getDocuments();
}, 4000);
// Delete Mark as Reviewed property THEN displays docs
setTimeout(() => {
this.hideMarkAsReviewed();
}, 8000);
}
One of the functions:
// Grabs the documents
public getDocuments() {
axios
.get("https://bpk.sharepoint.com/_api/search/query?querytext='owstaxIdDocumentx0020Owner:" + this.state.selectedOption + "'&trimduplicates=true&rowsperpage=100&rowlimit=1000&selectproperties='LastReviewDateOWSDATE,ScheduledReviewDateOWSDATE,Path'",
{ params:{},
headers: { 'accept': 'application/json;odata=verbose' }
})
.then(response =>
response.data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results.map(document => ({
Type: this.checkFile(document),
Name: document.Cells.results[6].Value.split("/").pop(),
'Scheduled Review Date': document.Cells.results[8].Value.slice(0,-11),
Path: document.Cells.results[6].Value.replace('https://bpkintelogydev.sharepoint.com', ''),
Site: document.Cells.results[6].Value.split('/').slice(4).slice(0,1),
'Last Review Date': document.Cells.results[7].Value.slice(0,-11),
View: <a href="#" onClick={()=>window.open(document.Cells.results[6].Value + '?web=1&action=edit')}>View</a>,
'Mark as Reviewed': <a href='#'>Mark</a>
}))
)
.then(documents => {
this.setState({documents, isLoading: true});
})
.catch(error => {
//console.log(error);
});
}
Upvotes: 0
Views: 849
Reputation: 1074178
Yes, callbacks or promises are what you do here. Promises would be the more modern way (not least so you can use async
/await
with them). setState
provides a callback for when the state is set if you want that (it's an optional second argument to setState
). Then your various functions (getSecurityGroupUsers
, etc.) would return promises that they fulfill or reject.
In your getDocuments
, for instance, you want to:
return
the result of calling then
, andhandleDropdownChange
...and similar in the others. For any that don't already have a promise to chain on (getDocuments
has the one from axios
) because they use a callback-style API you can create a promise and fulfill/reject it yourself (see this question's answers for details there).
Doing that, handleDropdownChange
might look something like:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
this.getSecurityGroupUsers()
.then(() => this.checkForDocAccess())
.then(() => this.getDocuments())
.then(() => this.hideMarkAsReviewed())
.catch(error => {
// ...handle/report error...
});
}
or if you didn't want to start those until the state had been changed initially:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState(
{ selectedOption: e.target.value, isLoading: true },
() => {
this.getSecurityGroupUsers()
.then(() => this.checkForDocAccess())
.then(() => this.getDocuments())
.then(() => this.hideMarkAsReviewed())
.catch(error => {
// ...handle/report error...
});
}
);
}
In this particular case, async
/await
doesn't help much, but perhaps some. I assume handleDropdownChange
shouldn't be async
since it's an event handler and so nothing would handle the promise it would return, so:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
(async () => {
try {
await this.getSecurityGroupUsers();
await this.checkForDocAccess();
await this.getDocuments();
await this.hideMarkAsReviewed();
} catch (error) {
// ...handle/report error...
}
})();
}
Notice how the entire body of the async
function is in a try
/catch
, because nothing hooks its promise, so it's important we handle errors directly. (Ensure that "handle/report error" doesn't throw an error. :-) )
Upvotes: 2
Reputation: 22885
Use async await and a simple sleep function to wait between them if it is required
const sleep = time => new Promise(res => {
setTimeout(res, time);
});
async handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
// Checks security permissions - MAKES A GET REQUEST
await sleep(1000);
await this.getSecurityGroupUsers();
await sleep(1000);
// Ghecks if user can access docs
await this.checkForDocAccess();
await sleep(1000);
// Gets documents - MAKES A GET REQUEST
await this.getDocuments();
await sleep(1000);
// Delete Mark as Reviewed property THEN displays docs
await this.hideMarkAsReviewed();
}
// But you methods should return a promise like this
getDocuments = () => {
return fetch('some url').then(res => res.json());
}
// Or something like
getSecurityGroupUsers = async () => {
const data = await axios('some url');
return data.message;
}
Upvotes: -1
Reputation: 187
You can use Async await for waiting promise to resolve
public handleDropdownChange = async e => {
this.setState({ selectedOption: e.target.value, isLoading: true });
await this.getSecurityGroupUsers();
await this.checkForDocAccess();
await this.getDocuments();
await this.hideMarkAsReviewed();
}
Upvotes: 1
Reputation: 3853
You need a function call back!
function functionOne(x) { return x; };
function functionTwo(var1) {
// some code
}
functionTwo(functionOne);
Upvotes: 0