Context:
I am creating a simple web app where the user can login, select the amount they wish to pay, and then press a Pay button which redirects them to a payment gateway in order for them to pay.
Problem:
When they press the Pay button, I set a mobX observable variable to loading so as to show a loading spinner and disable the button. This works fine. However, if the user proceeds to the external payment gateway, and then press the browser Back button, on certain browsers (Firefox and Safari), my state is still loading, despite setting it to not loading on componentDidMount. What component lifecycle method is called when navigating to the component from the back button on all browsers?
code:
#observer
class DuesForm extends React.Component<Props, {}> {
private form;
private loadingStore: LoadingStore = new LoadingStore();
constructor(props: Props) {
super(props);
this.form = React.createRef();
}
public componentDidMount() {
this.loadingStore.setLoadingToComplete();
}
public componentWillUnmount() {
this.loadingStore.setLoadingToComplete();
}
public render() {
return (
<form
name="dues-form"
ref={(f) => (this.form = f)}
method="POST"
action="www.external-payment-gateway.com"
>
<div>
<h4 style={{ marginBottom: '0.5em' }}>Currency</h4>
<Radio.Group
options={currencyOptions}
optionType="button"
buttonStyle="solid"
/>
</div>
<div style={{ marginTop: '1em' }}>
<h4 style={{ marginBottom: '0.5em' }}>Amount</h4>
<InputNumber
type="number"
style={{ minWidth: '8em', justifyContent: 'center' }}
min={0}
step={0.01}
precision={2}
/>
</div>
{this.loadingStore.isLoading ? (
<Space size="middle" style={{ marginBottom: '1em', marginTop: '1em' }}>
<Spin indicator={<LoadingOutlined />} />
</Space>
) : null}
<div style={{ marginTop: '1.5em' }}>
<Button
className="primary-button"
style={{ width: '100%' }}
type="primary"
onClick={this.handleButtonClick.bind(this)}
disabled={this.loadingStore.isLoading}
>
Pay
</Button>
</div>
<PaymentForm /> // This is a hidden form given to me by the payment gateway to send the data
</form>
);
}
private handleSubmit(e: any): void {
e.preventDefault();
this.loadingStore.setLoadingToLoading();
this.form.submit(); // the reason I am doing it this way is I have to fetch some data from the API before submitting
}
}
Related
Here is my code
import React from 'react';
import './style.scss';
const CalcButton = (props) => {
return (
<button id="calcBtn" style={props.style} onClick={props.onClick}>
{props.btnText}
</button>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentNum: '0',
log: ' ',
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
console.log('a', e.target.value);
this.setState((state) => ({
currentNum: state.currentNum + e.target.getAttribute(this.props.btnText),
}));
}
render() {
return (
<div id="container">
<div id="display">
<h3 id="log">{this.state.log}</h3>
<h3 id="currentNum">{this.state.currentNum}</h3>
</div>
<CalcButton
style={{
width: 150,
backgroundColor: 'rgb(173, 0, 0)',
}}
btnText="AC"
onClick={() => {
return this.setState({
currentNum: '',
log: '',
});
}}
/>
<CalcButton
btnText="/"
style={{
backgroundColor: 'gray',
}}
/>
<CalcButton
btnText="X"
style={{
backgroundColor: 'gray',
}}
/>
<CalcButton btnText="7" onClick={this.handleClick}/>
<CalcButton btnText="8" />
<CalcButton btnText="9" />
<CalcButton
btnText="-"
style={{
backgroundColor: 'gray',
}}
/>
<CalcButton btnText="4" />
<CalcButton btnText="5" />
<CalcButton btnText="6" />
<CalcButton
btnText="+"
style={{
backgroundColor: 'gray',
}}
/>
<CalcButton btnText="1" />
<CalcButton btnText="2" />
<CalcButton btnText="3" />
<CalcButton
btnText="="
style={{
float: 'right',
height: 150,
backgroundColor: 'rgb(34, 86, 134)',
}}
/>
<CalcButton
btnText="0"
style={{
width: 150,
}}
/>
<CalcButton btnText="." />
</div>
);
}
}
export default App;
As you can see I am trying to build a calculator, but the problem I am facing is that I want to use btnText prop value of CalcButton in handleClick event handler, but I am unable to figure out how to access it in the said event handler. I know it is a very basic problem but trust me I have searched and unable to find any reference regarding my problem, kindly help.
add data-btntext={props.btnText} to the button element in the CalcButton function. Then access it with e.target.dataset.btntext. –
Emil Karlsson
The above mentioned comment solves the problem,although I do agree with #maxagno3's answer, however, since I am still learning, I really wanted to learn that concept.
Thanks for all the answers.
I do not think you need to create CalcButton component. Since your button text is going to be the same, you can do it the following way -
Create a new state which stores your button texts in an array.
Map over that state and create buttons for the calculator.
When using the handle click function, pass in the button text value.
In that function check if the button text is AC then execute another function or set the state value. Better if you create a separate function for the AC button and call it in the if-else condition you'd be doing inside the handleClick function.
If you really need to create the component then you'd need to add the handle click function to the multiple components you are reusing and pass in the value of button text into it as an argument.
I hope this helps.
in child component :
const CalcButton = (props) => {
const handleClick = () => {
if (props.onClick) {
props.onClick(props.btnText);
}
};
return (
<button id="calcBtn" style={props.style} onClick={handleClick}>
{props.btnText}
</button>
);
};
In Parent component :
class App extends React.Component { ...
handleClick(val) {
console.log('a', val);
this.setState((state) => ({
currentNum: state.currentNum + val,
}));
}
......
<CalcButton btnText="7" onClick={this.handleClick}/>
I'm a newbie to react and I'm trying to route on a different set of components after a form (in my case a search bar) has been submitted (using onSubmit event). I have my search component wrapped in withRouter and have the history deconstructed. I've tried using onClick and the push does trigger, but does not seem to do anything with onSubmit. Hoping for your advise.
Thank you
Search component
const SearchAppBar = (props) => {
const {
history,
onPlatformDrop,
onPlatformChange,
onPriceRangeDrop,
clearPriceRange,
searchQuery,
setSearchQuery,
clearGenre,
onDropDownChange,
} = props;
return (
<Box sx={{ flexGrow: 1 }}>
<Search
sx={{
width: { xs: "85vw", md: "50vw", lg: "30vw" },
margin: "auto",
marginBottom: "20px",
}}
>
<form action="/" method="get" style={{ display: "flex" }}>
<StyledInputBase
defaultValue={searchQuery}
autoComplete="off"
placeholder="Search All Games…"
inputProps={{ "aria-label": "search" }}
type="search"
name="s"
id="site-search"
/>
<SearchIconWrapper>
<IconButton
type="submit"
onSubmit={(e) => {
history.push("/find");
clearGenre();
clearPriceRange();
onPlatformChange("");
onPlatformDrop("All Platforms");
onPriceRangeDrop("All Price Range");
onDropDownChange("All Genres");
setSearchQuery(e.target.value);
}}
>
<SearchIcon style={{ color: "#55597d" }} />
</IconButton>
</SearchIconWrapper>
</form>
</Search>
</Box>
);
};
export default withRouter(SearchAppBar);
Then on my App, i have something like this:
<Route
path="/find"
exact
render={(props) => (
<div>
<Search
onPlatformChange={onPlatformChange}
onPlatformDrop={onPlatformDrop}
clearPriceRange={clearPriceRange}
onPriceRangeDrop={onPriceRangeDrop}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
clearGenre={clearGenre}
onDropDownChange={onDropDownChange}
/>
</div>
)}
/>;
The onSubmit handler should be on the form element, not the button submitting the form. Remove the form action and method. You will also need to prevent the default form action from occurring, if you don't then the form is submitted and the page reloads.
<form
style={{ display: "flex" }}
onSubmit={(e) => { // handles form submission
e.preventDefault(); // prevents default form action
history.push("/find"); // navigate
...
setSearchQuery(e.target.s.value); // <-- access field value
}}
>
<StyledInputBase
...
name="s" // <-- access in submit handler
...
/>
<SearchIconWrapper>
<IconButton type="submit">
<SearchIcon style={{ color: "#55597d" }} />
</IconButton>
</SearchIconWrapper>
</form>
Sorry if the title is confusing.
Basically I have a form that contains a single input with the type file that is disguised as an icon
I want the form to submit when a file is selected, but I have no clue how to invoke the form submission with the input onChange event (tbh I'm not even sure that would be the adequate way of doing it).
Here is the code:
<Form onSubmit={handleSubmit((data) => updateProfilePicture(data))}>
<IconButton
component='label'
htmlFor='image-input'
style={{
position: "absolute",
bottom: "0",
right: "0",
backgroundColor: "#eee",
}}
>
<Input
type='file'
id='image-input'
name='file'
style={{ display: "none" }}
inputRef={register}
/>
<PhotoCameraIcon style={{ fontSize: "25px" }} />
</IconButton>
</Form>
Here what it looks like:
note: there is two forms, one for the image, one for the data, I don't want to handle the through the "update" button, otherwise it would be one form only and I wouldn't be having this question
You don't need a form to submit after image is added.
you can just use the input field and add onChange event handler get the Image details. Check this out if interested
https://stackblitz.com/edit/react-qjqt3f
import React from "react";
import "./style.css";
export default class App extends React.Component {
constructor() {
super();
this.state = {
url: "https://www.w3schools.com/howto/img_avatar.png"
};
}
onSelectFile = e => {
if (e.target.files && e.target.files[0]) {
var reader = new FileReader();
reader.readAsDataURL(e.target.files[0]); // read file as data url
reader.onload = event => {
// called once readAsDataURL is completed
console.log(event.target.result);
this.setState({ url: event.target.result });
//you can do the HTTP post call here
};
}
};
render() {
return (
<div>
<label className="hoverable" htmlFor="fileInput">
<img src={this.state.url} />
<div className="hover-text">Choose file</div>
<div className="background" />
</label>
<br />
<input id="fileInput" type="file" onChange={this.onSelectFile} />
</div>
);
}
}
You can give the form a id and submit the form per javascript:
document.getElementById("imageForm").submit();
or handle the data without submit:
onChange={() => { handleSubmit(this.files) }}
<Form id="imageForm" onSubmit={handleSubmit((data) => updateProfilePicture(data))}>
<IconButton
component='label'
htmlFor='image-input'
style={{
position: "absolute",
bottom: "0",
right: "0",
backgroundColor: "#eee",
}}
>
<Input
type='file'
id='image-input'
name='file'
style={{ display: "none" }}
inputRef={register}
onChange={() => { document.getElementById("imageForm").submit(); }}
// or:
onChange={() => { handleSubmit(this.files) }}
/>
<PhotoCameraIcon style={{ fontSize: "25px" }} />
</IconButton>
</Form>
Submitting forms programmatically is not the way to go specially within react. But for the sake of it you could achieve something like this by dispatching a submit event.
<Form ref={formRef} onSubmit={handleSubmit((data) => updateProfilePicture(data))}>
...
<Input
type='file'
id='image-input'
name='file'
style={{ display: "none" }}
onChange={(e) => formRef.dispatchEvent(new Event('submit'))}
inputRef={register}
/>
</Form>
better yet, if you do not want to fire any sort of html form validation, putting this file input inside a <form/> is not mandatory. You could call your updateProfilePicture(data) function directly inside of <Input/> s onChange handler.
I am using galleryswiper library to display an array of images inside a modal but when i navigate to the modal component and onPress the modal opens up i don't see any images. Can anyone help me how to pass the image sources inside the modal? Also i am unable to close the modal on Press of the icon.
export default class imagenav extends Component{
constructor(props){
super(props)
state = {
modalVisible: true,
};
}
closeModal() {
this.setState({modalVisible: false});
}
render() {
return (
<Modal visible={this.modalVisible} onRequestClose={() => {} }>
<GallerySwiper
style={{ flex: 1, backgroundColor: "black" }}
images={[
{source: {uri:
dimensions: {width: 1080, height: 1920}}
}
]}
/>
<Header
style={{
backgroundColor: 'black',
borderBottomWidth: 0,
}}
>
<Right>
<Icon
name='close'
color='white'
onPress={() => {
this.setState({
modalVisible: false
})
//this.closeModal()
}}
/>
</Right>
</Header>
</Modal>
);
}
}
I'm trying to make a simple login page with React Native, React Navigation Native Base and Expo on Android/Windows, but when I preview the application, I get this error :
How can this error be remedied?
My application includes, in App.js, React Navigation which redirects to the AuthScreen page if the user is not authenticate. When I click Dismiss, I can see my login page, without the text on the buttons, like this :
Here is the AuthScreen code :
import React from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import { Container, Form, Item, Label, Input, Text, Button } from 'native-base';
import * as auth from '../engine/AuthScreenModel';
export default class AuthScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
render() {
const { navigate } = this.props.navigation;
return (
<Container style={styles.container}>
<Form>
<Text style={{ textAlign: 'center', padding: 20 }}>
Welcome! Create an account or login to continue.
</Text>
<Item floatingLabel>
<Label>Username</Label>
<Input
onChangeText={email => this.setState({ email })}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
onChangeText={password => this.setState({ password })}
returnKeyType="go"
secureTextEntry
/>
</Item>
<Button
full
style={{ marginTop: 20 }}
onPress={() => auth.login(this.state.email, this.state.password, navigate)}>
>
<Text>Login</Text>
</Button>
<Button
full
success
style={{ marginTop: 10 }}
onPress={() => auth.register(this.state.email, this.state.password, navigate)}>
>
<Text>Create an account</Text>
</Button>
</Form>
</Container>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 10,
paddingTop: StatusBar.currentHeight,
},
});