Using Async/Await Inside a React Functional Component

Using async/await inside a React functional component

You will have to make sure two things

  • useEffect is similar to componentDidMount and componentDidUpdate, so if you use setState here then you need to restrict the code execution at some point when used as componentDidUpdate 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



Leave a reply



Submit