Reputation: 1668
This is the first time I came across handling the promises inside the JSX in my React JS project.
Here is my component code.
import React from 'react';
import Sodexo from './Sodexo';
import { connect } from 'react-redux';
import {withCookies} from 'react-cookie';
import ticketImg from './../../images/web-images/ticketrest.png';
import sodexImg from './../../images/web-images/sodexo.png';
import {selectMealVoucher} from './../../actions/paymentActions';
import {getSavedCard} from './../../utils/PaymentGateway';
class MealVoucher extends React.Component {
checkCardSaved = async () => {
const {cookies} = this.props.cookies;
const card = await getSavedCard(cookies.id,cookies.token);
const {sodexo} = card.data;
return sodexo.length === 0 ? 0 : 1;
}
render() {
const {sodexo, ticketrestaurant} = this.props;
return (
<div>
<div class="row">
<div className="col-md-1 col-sm-1"></div>
<div class="col-md-5 col-sm-5">
<div class="form-group">
<input
type="radio"
name="mealvoucher"
{...sodexo.isActive ? {checked: true} : {}}
onChange={() => this.props.selectMealVoucher('sodexo')}
/>
<img src={sodexImg} height="30px" style={{marginLeft:'15px'}}/>
</div>
</div>
<div class="col-md-5 col-sm-5">
<div class="form-group">
<input
type="radio"
name="mealvoucher"
{...ticketrestaurant ? {checked: true} : {}}
onChange={() => this.props.selectMealVoucher('ticketrestaurant')}
/>
<img src={ticketImg} height="30px" style={{marginLeft:'15px'}} />
</div>
</div>
</div>
{
sodexo.isActive ? (
this.checkCardSaved().then(res => {
res ? <Sodexo /> : ''
})
): ''
}
</div>
);
}
}
const mapStateToProps = state => state.paymentpage.paymentoption.mealvouchers;
const mapDispatchToProps = {selectMealVoucher};
export default withCookies(connect(mapStateToProps,mapDispatchToProps)(MealVoucher));
In the above, I am trying to call checkSavedCard()
inside the JSX, but even if I am returning the 0
or 1
from checkSavedCard()
, I see that promise is getting returned instead of 0
or 1
.
So I used .then()
and tried to render another component depending on the value returned by the checkSavedCard()
.
But, this isn't working and instead, I am getting an error message.
Objects are not valid as a React child (found: [object Promise]).
So, I came up with a different approach.
I created one global variable and inside the checkSavedCard()
instead of returning the value I am saving that value to the global variable and then inside the JSX I am checking for the value of that global variable.
This approach works fine for me.
Here is the working component code.
import React from 'react';
import Sodexo from './Sodexo';
import { connect } from 'react-redux';
import {withCookies} from 'react-cookie';
import ticketImg from './../../images/web-images/ticketrest.png';
import sodexImg from './../../images/web-images/sodexo.png';
import {selectMealVoucher} from './../../actions/paymentActions';
import {getSavedCard} from './../../utils/PaymentGateway';
class MealVoucher extends React.Component {
cardStatus;
componentDidMount() {
this.checkCardSaved();
}
checkCardSaved = async () => {
const {cookies} = this.props.cookies;
const card = await getSavedCard(cookies.id,cookies.token);
const {sodexo} = card.data;
this.cardStatus = sodexo.length === 0 ? 0 : 1;
}
render() {
const {sodexo, ticketrestaurant} = this.props;
return (
<div>
<div class="row">
<div className="col-md-1 col-sm-1"></div>
<div class="col-md-5 col-sm-5">
<div class="form-group">
<input
type="radio"
name="mealvoucher"
{...sodexo.isActive ? {checked: true} : {}}
onChange={() => this.props.selectMealVoucher('sodexo')}
/>
<img src={sodexImg} height="30px" style={{marginLeft:'15px'}}/>
</div>
</div>
<div class="col-md-5 col-sm-5">
<div class="form-group">
<input
type="radio"
name="mealvoucher"
{...ticketrestaurant ? {checked: true} : {}}
onChange={() => this.props.selectMealVoucher('ticketrestaurant')}
/>
<img src={ticketImg} height="30px" style={{marginLeft:'15px'}} />
</div>
</div>
</div>
{
sodexo.isActive && this.cardStatus ? (
<Sodexo />
): ''
}
</div>
);
}
}
const mapStateToProps = state => state.paymentpage.paymentoption.mealvouchers;
const mapDispatchToProps = {selectMealVoucher};
export default withCookies(connect(mapStateToProps,mapDispatchToProps)(MealVoucher));
But I think this isn't a perfect solution, there might be something provided by React JS, to handle the promises inside the JSX.
I googled it but I didn't find any solution on this.
Upvotes: 2
Views: 6907
Reputation: 2390
React can't render from the result of a Promise. You should update a value in the component's state and render based on the state's value. See my example here: https://codesandbox.io/s/1vzon8r4k4. A button click sets the state to loading: true
(just to show the user something while they wait), then fires off an async call. When the async call finished, the state is updated to set loading: false
and set the result of the async call to a value in the state. When the state is updated, the render
function is automatically called and the UI is updated to reflect the state change.
const fakePromise = () =>
new Promise((resolve, reject) => {
const fakeResult = "Complete";
setTimeout(() => resolve(fakeResult), 1000);
});
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
loading: false,
result: null
};
this.startAsync = this.startAsync.bind(this);
}
startAsync() {
this.setState({
loading: true
});
fakePromise().then(result =>
this.setState({
loading: false,
result
})
);
}
render() {
const { loading, result } = this.state;
return (
<div className="App">
{!result &&
!loading && (
<div>
<h1>Result Not Fetched</h1>
<button onClick={this.startAsync} type="button">
Fetch Result Async
</button>
</div>
)}
{loading && <h1>Fetching Result</h1>}
{result && <h1>Result is: {result}</h1>}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Upvotes: 4