How to Async Await in React Render Function

How to async await in react render function?

You should always separate concerns like fetching data from concerns like displaying it. Here there's a parent component that fetches the data via AJAX and then conditionally renders a pure functional child component when the data comes in.

class ParentThatFetches extends React.Component {
constructor () {
this.state = {};
}

componentDidMount () {
fetch('/some/async/data')
.then(resp => resp.json())
.then(data => this.setState({data}));
}

render () {
{this.state.data && (
<Child data={this.state.data} />
)}
}
}

const Child = ({data}) => (
<tr>
{data.map((x, i) => (<td key={i}>{x}</td>))}
</tr>
);

I didn't actually run it so their may be some minor errors, and if your data records have unique ids you should use those for the key attribute instead of the array index, but you get the jist.

UPDATE

Same thing but simpler and shorter using hooks:

const ParentThatFetches = () => {
const [data, updateData] = useState();
useEffect(() => {
const getData = async () => {
const resp = await fetch('some/url');
const json = await resp.json()
updateData(json);
}
getData();
}, []);

return data && <Child data={data} />
}

How to render something that is async in React?

I don't fully understand what you are trying to output but how you would usually handle this is with both the useState hook and the useEffect hook see example below.

  //function that stores the data in the result array, 
//but result array will only be available after the
//server response, and after the page is rendered
const pin = () => {
const [result, setResults] = useState([]);
var url = "http://warm-hamlet-63390.herokuapp.com/pin/list"

useEffect(() => {
//Attempt to retreive data
try {
const res = transformData();

if (res) {
// Add any data transformation
setResults(transformData(res))
}
else {
throw (error)
}
}
catch (error) {
//Handle error
}
}, [])

// Handle data transformation
const transformData = async () => {
const res = await axios.get(url)

const txt = JSON.stringify(res.data.data)
const result = JSON.parse(txt)

return result
}

if (!result) {
// Return something until the data is loaded (usually a loader)
return null
}

// Return whatever you would like to return after response succeeded
return <></>;
}

This is all assuming that Pin is a component like you have shown in your code, alternatively, the call can be moved up to the parent component and you can add an inline check like below to render the pin and pass some data to it.

{result && <Pin property={someData} />}

Just a bit of background the useEffect hook has an empty dependency array shown at the end "[]" this means it will only run once, then once the data has updated the state this will cause a rerender and the change should be visible in your component

Render 'async' function return value in render method (React)

Issue

React components and lifecycle are 100% synchronous, especially the render method. The render method is also considered a pure function, meaning it should have zero side effects (like fetching data!!).

Solution

You should refactor your code to fetch data in one or both of componentDidMount and componentDidUpdate and save the result to local component state for rendering.

Here's an example refactorization.

class TitleCards extends Component {
constructor(props){
super(props);

state = {
portfolioTotal: '',
};

this.totalPortfolio = this.totalPortfolio.bind(this);
this.getIntradayPrice = this.getIntradayPrice.bind(this);
}

async getIntradayPrice(tick) {
const resp = await fetch(`${IEX.base_url}/stock/${tick}/intraday-prices?chartLast=1&token=${IEX.api_token}`);
return resp.json();
}

async totalPortfolio() {
const { info } = this.props;
const respPromises = info.map(({ tick }) => this.getIntradayPrice(tick));
const respArrays = await Promise.all(respPromises);
const result = respArrays.reduce((acc, val, index) => acc + val[0].close * info[index].amtPurch, 0)
return result;
}

// When the component mounts, call totalPortfolio
componentDidMount() {
this.totalPortfolio()
.then(portfolioTotal => {
this.setState({
portfolioTotal
});
})
.catch(error => {
// add any required error handling/messaging here
});
}

render() {
const { portfolioTotal } = this.state;
return(
return(
<div className="positioning">
<div className="StockCardTitle">
<img src={Folder} className="StockCardTitle-image" />
{portfolioTotal} // <-- render state value
</div>
</div>
);
}
}

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

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} />
}

React - calling async function from render()

First of all, I would like to say that componentDidMount function is a static function.

But you can have an async function like loadQRCode where you can call the function and iterate the API calls and push the result into and array and then you can render the div's with the required data.

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

}

Async function doesn't return in React app

Inside Header.js

import {useEffect,useState} from "react";
import someFunction from "./someFile.js";

const Header = () => {
const [text, setText] = useState("");

useEffect(() => {
someFunction()
.then((data) => setText(data))
.catch((err) => console.log(err));
}, []);

return (<h1>{text}</h1>)
}

export default Header;

React Native Async Call in Render Function

You must use promise instead async/await function, and it is better to use functional components instead of class component.

First, you must use useEffect function for setState after AsyncStorage setItem:

const [asyncStorageItemSet, setAsyncStorageItemSet] = React.useState(false);
const [storageKey, setStorageKey] = React.useState('')
React.useEffect(() => {
AsyncStorage.getItem('@storage_Key').then(res => {
setStorageKey(res);
})
}, [asyncStorageItemSet])

and your return likes this:

<Button
onPress={storeData}
title="Store"
color="#841584"/>
<Text>{storageKey}</Text>

and storeData function looks like this:

const storeData = () => {
const value= "hello"
AsyncStorage.setItem('@storage_Key', value).then(res => {
setAsyncStorageItemSet(true)
})}


Related Topics



Leave a reply



Submit