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.
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>
Related
I have created a Main Component from which I am making use of the rest of the components. For a component, I am passing in appropriate props through onClick. Even though the property is correctly defined and passed on, I seem to get this error.
Here are the snippets:
1.Main Component
import Menu from './MenuComponent';
import DishDetail from './DishDetailComponent';
import { DISHES } from '../shared/dishes';
onDishSelect(dishId) {
this.setState({
selectedDish: dishId
});
}
// In render:
<Menu dishes={this.state.dishes} onClick={(dishId) => { this.onDishSelect(dishId) }} />
<DishDetail dish={this.state.dishes.filter((dish) => dish.id === this.state.selectedDish)[0]}
In the menucomponent:
<div key={dish.id} className="col-12 col-md-5 mt-2">
<Card key={dish.id}
onClick={() => this.props.onClick(dish.id)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
</div>
In the dishdetail component:
render() {
const dish = this.props.dish;
const dishdetail = this.renderDish(dish);
const dishcomments = this.renderComments(dish.comments);
if (dish != null) {
return (
<div className="row">
{dishdetail}
{dishcomments}
</div>
);
}
else {
return (<div></div>);
}
}
}
First I thought, the selecteddish is set to null in state of maincomponent at the beginning and that's why the error is occuring. But then even if I add the null check as above, the error still persists. Where am I getting wrong?
In DishDetail component, the dish receives a value a bit eventually. You are making checks but at incorrect place.
So, in your render, make checks on this.props.dish in the beginning.
render() {
if (this.props.dish) {
const dish = this.props.dish;
const dishdetail = this.renderDish(dish);
const dishcomments = this.renderComments(dish.comments);
return (
<div className="row">
{dishdetail}
{dishcomments}
</div>
);
}
else {
return (<div></div>);
}
}
}
I have been unable to setState on props I keep getting
TypeError: props.setState is not a function
I'm trying to implement a search function
const HeroComp = (props) => {
let handleSearchSubmit = (e) => {
props.setState({searchValue: e.target.value});
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;
When I console.log(props) I get
{searchValue: ""}
searchValue: ""
__proto__: Object
This is the parent component
import images from '../data/images'; //the file from which i'm importing images data
class HomePage extends React.Component{
constructor(){
super();
this.state = {
images,
searchValue: ''
}
}
render(){
const {images , searchValue} = this.state;
const filteredImage = images.filter(image => image.cartigory.toLowerCase().includes(searchValue));
return(
<div >
<HeroComp searchValue={ searchValue } />
<GalleryComp filteredImage={filteredImage} />
</div>
)
}
}
export default HomePage;
I know this should be easy but I just can't see the solution .
How about this?
useEffect(() => {
// set the current state
setSearchValue(props.searchValue)
}, [props]);
Functional component dont have state, but you can use reactHooks:
import React, { useState } from 'react';
const HeroComp = (props) => {
let [searchValue, setSearchValue] = useState();
let handleSearchSubmit = (e) => {
setSearchValue(e.target.value);
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;
parent component:
import React from "react";
import InputRow from "./InputRow";
const Bid: React.FunctionComponent = () => {
const inpVal = (d:string) => {
}
return (
<div style={{ display: "none" }} className="bid-container animated zoomIn" id="bid-modal">
<p>Mortage</p>
<div className="bid-row-container">
<p>Enter your bid</p>
<div className="bid-row">
<InputRow bid="Bid" inpVal={(inpVal: string)=>{console.log(inpVal)}} />
</div>
</div>
</div>
);
};
export default Bid;
child component:
import React from "react";
interface Props{
bid: string,
inpVal: (inpVal:string) => void;
}
const InputRow: React.FunctionComponent<Props> = (bid, inpVal) => {
return (
<div className="input-row">
<div>
<input type="text" onChange={(e) => { inpVal(e.target.value) }} />
<p>Rate</p>
</div>
<button className="bid-btn">{bid.bid}</button>
</div>
);
};
export default InputRow;
I am trying to pass the input value from the child component to the parent component but it is throwing error.
TypeError: inpVal is not a function.
pass value using props function
How to pass data from child component to its parent in ReactJS?
Parent:
<div className="col-sm-9" >
<SelectLanguage onSelectLanguage={this.handleLanguage}/>
</div>
Child:
handleLangChange = () => {
var lang = this.dropdown.value;
this.props.onSelectLanguage(lang);
}
You need to wrap your parameters in {} which will extract bid and inpVal from the props object
const InputRow: React.FunctionComponent<Props> = ({ bid, inpVal }) => {...}
I have a weird problem when I console log my component on load to check if there is a state. I Get an array back with data. But when I try to loop through it. I get map undefined? I don't understand why it's driving me crazy.
What am i doing wrong? I used the same thing on other components without any problems.
Thanks!
My code:
import React, { Component } from 'react';
import ReactHtmlParser from 'react-html-parser';
// API settings
import { WP_DATA_URL } from 'constants/import';
// Axios fetching
import axios from 'axios';
// components
import Youtube from 'components/Youtube/Youtube';
import Slider from 'react-slick';
import SpinnerLoader from 'components/SpinnerLoader/SpinnerLoader';
class College extends Component {
state = {
page_college: [],
loading: true,
};
getCoffee() {
return new Promise(resolve => {
setTimeout(() => resolve('☕'), 1000); // it takes half of a second to make coffee
});
}
async showData() {
try {
const wpCollege = axios(`${WP_DATA_URL}/pages?slug=college`);
await this.getCoffee();
await Promise.all([wpCollege]).then(response => {
this.setState({
page_college: response[0].data[0].acf,
loading: false,
});
console.log(this.state.page_college);
});
} catch (e) {
console.error(e); // 💩
}
}
componentDidMount() {
this.showData();
}
render() {
const { loading } = this.state;
const { title, description, page_college: college } = this.state;
return (
<div className="pages--container">
<div className="pages">
<div className="row center-xs pages--wrapper">
<div className="page">
<div className="page--content">
{loading ? (
<SpinnerLoader />
) : (
<React.Fragment>
<div className="col-xs-12 col-md-5">
<h2>HOI</h2>
</div>
<div className="col-xs-12 col-md-6">
{college.map(data => {
console.log(data);
})}
</div>
</React.Fragment>
)}
</div>
</div>
</div>
</div>
</div>
);
}
}
export default College;
setState is asynchronous so your console.log after it may be reflecting the previous state. Pass setState a callback as the 2nd param and check the state there. response[0].data[0].acf might not be an array.
async componentDidMount() {
await this.showData();
}
Just make the componentDidMount wait for the showData to complete.
I have a simple form rendering with reactjs and I want to pass a param from the form to complete a route to a test endpoint.
Here is the endpoint: https://jsonplaceholder.typicode.com/comments?postId=1
Here is the component:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios'
import MenuCombo from './menucombo'
const heading = "Enter a price cap here for recommendations"
class App extends Component {
handleSubmit = (e) => {
e.preventDefault()
axios.get('https://jsonplaceholder.typicode.com/comments?postId=PriceCap')
.then(response =>{
console.log("FOUND", response)
})
.catch(err => {
console.log("NOT FOUND",err)
})
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">{heading}</h1>
</header>
<div>
<form onSubmit={this.handleSubmit}>
<label>Enter a price</label>
<input name = 'PriceCap'
type = 'number'
min = '1'
max ='20'/>
<button>Generate Suggestions</button>
</form>
</div>
</div>
);
}
}
export default App;
As you can see I cam passing the form element with the name PriceCap ideally the user would set this to 1 to log the data. And if it is set to any other value than 1 it logs an error. But I can't seem to get the parameter to pass properly.
I feel like this would be easier with POST but I also feel like POST is overkill given that I am only sending one param.
Set a reference to your input by
<input
name = 'PriceCap'
ref = {node => {this.input = node}}
type = 'number'
min = '1'
max ='20'
/>
Then you can access the value in your submit handler by
handleSubmit = event => {
let PriceCap = this.input.value;
axios.get(`https://jsonplaceholder.typicode.com/comments?postId=${PriceCap}`)
.then(...).catch(...)
}
You want something like this:
import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";
class App extends React.Component {
state = {
val: ""
};
handleSubmit = e => {
e.preventDefault();
axios
.get(
`https://jsonplaceholder.typicode.com/comments?postId=${this.state.val}`
)
.then(response => {
console.log("FOUND", response);
})
.catch(err => {
console.log("NOT FOUND", err);
});
};
render() {
return (
<div className="App">
<div>
<form onSubmit={this.handleSubmit}>
<label>Enter a price</label>
<input
name="PriceCap"
type="number"
min="1"
max="20"
onChange={e => this.setState({ val: e.target.value })}
/>
<button>Generate Suggestions</button>
</form>
</div>
</div>
);
}
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working example here.
Here, you store the input value in state, and then use that in your get() call.
Notice we added the state, and also an onChange in the input.