Using async/await inside a React functional component
You will have to make sure two things
useEffect
is similar tocomponentDidMount
andcomponentDidUpdate
, so if you usesetState
here then you need to restrict the code execution at some point when used ascomponentDidUpdate
as shown below:
function Dashboard() {
const [token, setToken] = useState('');
useEffect(() => {
// React advises to declare the async function directly inside useEffect
async function getToken() {
const headers = {
Authorization: authProps.idToken // using Cognito authorizer
};
const response = await axios.post(
"https://MY_ENDPOINT.execute-api.us-east-1.amazonaws.com/v1/",
API_GATEWAY_POST_PAYLOAD_TEMPLATE,
{ headers }
);
const data = await response.json();
setToken(data.access_token);
};
// You need to restrict it at some point
// This is just dummy code and should be replaced by actual
if (!token) {
getToken();
}
}, []);
return (
... rest of the functional component's code
);
}
async/await in react functional component
Your dynamic component is using hooks and if you call importedComponent.default
React will throw and error error of using hooks inside of useEffect
.
Instead, load the component and call .default
when you render it.
I've also added state.step
to useEffect
dependencies array to make sure to re-run the effect if when state.step
changes as if on initial render state.step is not title
the component will never render as you provided an empty dependencies array
useEffect(() => {
// same as before
// ...
if (DynamicComponent) {
setComponent(DynamicComponent);
}
}, [state.step])
if (!component) {
return null;
}
return (
<View>
<component.default />
</View>
)
How to test react functional component async call
Splitting your component into custom hooks and component make your life easier to test and more readable by splitting UI and logic.
The custom hooks will look like this
useSpecialistsList.js
import { useState, useEffect } from "react";
const useSpecialistsList = (specialistsListService) => {
const [specialistsList, setSpecialistsList] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
getSpecialistsList();
}, []);
async function getSpecialistsList() {
const specialistsListData = await specialistsListService.getSpecialistsList();
setSpecialistsList(specialistsListData);
setIsLoading(false);
}
return {
isLoading: isLoading,
specialistsList: specialistsList
}
}
export default useSpecialistsList;
The component look like this
import React from "react";
import SpecialistsListService from "../../../services/specialists";
import SpecialistsPageView from "./SpecialistsPageView";
import useSpecialistsList from "./useSpecialistsList";
import "./index.scss";
export default function SpecialistsPage() {
const {isLoading, specialistsList} = useSpecialistsList(new SpecialistsListService());
return (
<SpecialistsPageView isLoading={isLoading} specialists={specialistsList} />
);
}
Now you can test your hooks using "@testing-library/react-hooks"
Test will look like this
import {renderHook} from "@testing-library/react-hooks";
import useSpecialistsList from "./useSpecialistsList";
import SpecialistsListService from "../../../services/specialists";
describe("useSpecialistsList", ()=>{
it('Should return userDetails loading as false', async ()=> {
const {result, waitForNextUpdate} = renderHook(()=> useSpecialistsList(new SpecialistsListService()));
expect(result.current.isLoading).toEqual(true);
await waitForNextUpdate();
expect(result.current.isLoading).toEqual(false);
});
})
Here waitForNextUpdate call the useEffect (Generally update the component)
To read more about testing custom hooks use this like
await function returns a promise and returns true even if the condition is false | React Functional Component
You can do:
const [isPresentResult, setIsPresentResult] = useState(false);
useEffect(() => {
this.isPresent().then(res => setIsPresentResult(res))
}, [])
// ...
{ isPresentResult && <div> Content </div> }
Using Class Component:
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isPresentResult: false };
}
componentWillMount = () => {
this.isPresent().then((isPresentResult) =>
this.setState({ isPresentResult }));
};
async isPresent() {
const res = await AsyncFunction();
return !!res;
}
render() {
return {this.state.isPresentResult && <div> Content </div>};
}
}
Using async function, await and resolve in React component
As @Yousaf pointed out:
const [resultsFromDb, setResultsFromDb] = useState([]);
const _dbCall = () => {
const foo = [];
const fooDb = SQLite.openDatabase(db);
fooDb.transaction(tx => {
tx.executeSql(`SOME SQL`, [], (tx, results) => {
// do something the results
for (let i = 0; i < results.rows.length; i++) {
foo.push(results.rows.item(i));
}
setResultsFromDb(foo)
}, null);
});
}
const _renderSomething = () => {
const results = _dbCall();
return <FlatList
data={resultsFromDb}
renderItem={_renderFunc}
keyExtractor={item => item} />
}
Using Async await in react component
I think you need more about async/await
in JS.
An async function
always return a promise.
So x
in Wrapper
is a promise. Because you don't use await/async
.
It should be like this.
async Wrapper = (body) => {
try{
let x = await this.Send(body); // <-- missing await here
return x;
}catch(e){console.log(e)}
}
But then, the code in render
doesn't work. because this.Wrapper()
now returns a promise. -> returnData is a promise
. And the render
method can't be async function :)
render(props) {
//...
const returnData = this.Wrapper(jsonBody) // <-- returnData here is a promise.
//...
So to make things work.
You have to use state
. Call the this.Wrapper
in componentDidMount
or componentDidUpdate
. For example:
constructor() {
// ...
this.state = { returnData: null }
}
async componentDidMount() {
const returnData = await this.Post(...); // Using await to get the result of async func
this.setState({ returnData });
}
async Post(body) {
try{
const options = {
method: 'POST',
uri: 'XXXXXXXXXXXXXXXXXXXX',
body: body
}
return rp(options); // define await then return is unnecessary
}catch(e){console.warn(e)}
}
render() {
const { returnData } = this.state;
// ... Do the rest
}
Related Topics
How to Access Component Methods from "Outside" in Reactjs
Referenceerror: Fetch Is Not Defined
Canvas Image Crossplatform Insecure Error
Returning a Value from Callback Function in Node.Js
Javascript: Clear All Timeouts
How to Declare a Global Variable in JavaScript
Is It Safe to Assume Strict Comparison in a JavaScript Switch Statement
Facebook How to Check If User Has Liked Page and Show Content
Sorting Objects by Property Values
Dc.Js - How to Create a Row Chart from Multiple Columns
How to Remove Spaces from a String Using JavaScript
Function to Convert Timestamp to Human Date in JavaScript
Three.Js:2Xmeshes Using Same Vector as Position
How to Check If an Object Has a Key in JavaScript
What Is the 'Constructor' Property Really Used For