ReactJS On submit change classes of button and input field - reactjs

I have a form and when I "Submit" the form I want to add an attribute and some extra classes to the "submit" button and the input field
This is my handleSubmit function
handleSubmit = event => {
event.preventDefault();
const formData = new FormData(event.target);
axios.post(`MyPostUrl`,formData)
.then(res => {
})
}
This is my form
<form onSubmit={this.handleSubmit} method="POST">
<div className="form-row">
<input required min="1" max="10" name="grade" className="form-control col-md-5" type="number" />
<button className="btn btn-outline-primary col-md-6">
Grade
</button>
</div>
</form>
So in let's say jQuery i could just go $(this).find("someClass") and do what ever i need to do with it. How can i achieve this with React?
What I'm trying to do is change the input class to col-md-12 and add an disabled attribute and I want to remove the button on submit
And I have a lot of forms since I've mapped over an object

Consider an example like this: https://codesandbox.io/s/throbbing-bird-ob89o
The idea is to use your component-state to control what classes, styles and attributes to use for your markup.
In this case, we define a submitted state and depending on its Boolean-value, we can use ternary operators to toggle the code we want to render.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
grade: "",
submitted: false
};
handleSubmit = e => {
e.preventDefault();
this.setState({
submitted: true
});
};
handleOnChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
const { submitted, grade } = this.state;
return (
<form onSubmit={this.handleSubmit} method="POST">
<div className="form-row">
<input
required
onChange={this.handleOnChange}
min="1"
max="10"
name="grade"
className={`form-control ${submitted ? "col-md-12" : "col-md-5"}`}
value={grade}
type="number"
disabled={submitted}
/>
{!submitted ? (
<button className="btn btn-outline-primary col-md-6">Grade</button>
) : (
""
)}
</div>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When you submit the form, we toggle the submitted state to true. Our component re-renders and that recalculates all the ternary operators in our mark-up like ${submitted ? "col-md-12" : "col-md-5"} and etc.

You would have to use react states for managing classes too.
e.g:
<button className={this.state.buttonClass}>
Grade
</button>
Better yet, create a wrapper component around it so that these actions can be controlled via props *e.g disabled={true} would add class

Related

Form submission canceled because the form is not connected in react js. Where am I going wrong?

**This is a redeem Voucher form which I have created , based on the API response data. I have to make the states either true for success or false on failure, untill we get the api response, Loading snippet will be displayed. **
Onclick of the avail subscription button, I'm not able to display the loading snippet. In the console it shows "Form submission canceled because the form is not connected ".
import React, { Component } from 'react';
import styles from './RedeemVoucher.module.scss'
import close from '../../../Assets/Images/close_grey.svg'
import verified from '../../../Assets/Images/verified.svg'
class RedeemVoucher extends Component {
constructor(props){
super(props);
this.state = {
voucherNumber:'',
pin:'',
redeemVoucherForm:true,
isLoading:false,
error:false,
verified:false
}
}
handleUserInput =(e)=> {
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value.replace(/[-/+/?/./`/~/,/{/}/[!/|/#/#/$/%/^/&/*/(/)/_/>/</:/;/'/"/=A-Za-z]/g,"")});
}
handleSubscription=()=>{
this.setState({
isLoading:true,
redeemVoucherForm:false,
error:false,
verified:false
})
}
render() {
return (
<>
{this.state.redeemVoucherForm ?
<form className={styles.redeem_form}>
<img src={close} className={styles.close} onClick={this.props.close_RedeemVoucher_Prompt}/>
<p className={styles.head}>Redeem Voucher</p>
<p className={styles.description}>Enter your NCPA Membership Voucher details to avail your free subscription plan</p>
<label className={styles.voucher_number}>Voucher Number <span className={styles.star}>*</span></label>
<input
type="text"
placeholder='Enter Voucher Number'
className={styles.voucher_number_box}
value={this.state.voucherNumber}
name='voucherNumber'
autoComplete="off"
onChange={(event) => this.handleUserInput(event)}
/>
<label className={styles.pin}>Pin <span className={styles.star}>*</span></label>
<input
type="text"
placeholder="Enter Pin"
className={styles.pin_box}
value={this.state.pin}
autoComplete="off"
name='pin'
onChange={(event) => this.handleUserInput(event)}
/>
<button className={styles.avail_subs} onClick={this.handleSubscription} disabled= {!this.state.voucherNumber || !this.state.pin} >Avail Subscription</button>
</form>
: this.state.isloading ?
<div className={styles.loading}>
<p className={styles.verifying_info}>Verifying Information</p>
<p className={styles.please_wait}>Please Wait</p>
{/* loader */}
</div>
: this.state.error ?
<div className={styles.error}>
{/* API error response */}
<button className={styles.exit}>exit</button>
</div>
: this.state.verified ?
<div className={styles.verified}>
<p className={styles.verifying_info}>Verifying Information</p>
<p className={styles.please_wait}>Please Wait</p>
<img src={verified} className={styles.verified_img} />
</div>
: null}
</> )}
}
export default RedeemVoucher
I suspect your code is submitting the form and some default form actions are occurring. This is because button elements are of type="submit" by default when no type attribute is provided, and the form element has no onSubmit handler to prevent the default form actions from occurring. When the default form action occurs, generally the page will reload, this is likely wiping out your state update.
Explicitly declare the button to be type="submit" and move the handleSubscription callback to the form element's onSubmit handler. In handleSubscription prevent the default submit action.
handleSubscription = (event) => {
event.preventDefault();
this.setState({
isLoading: true,
redeemVoucherForm: false,
error: false,
verified: false
});
};
...
<form
className={styles.redeem_form}
onSubmit={handleSubscription}
>
...
<button
className={styles.avail_subs}
disabled={!this.state.voucherNumber || !this.state.pin}
>
Avail Subscription
</button>
</form>

state value is undefined on Redirect

I need to pass data to another page using react redirect method, but I'm getting undefined value of state.
Form Component:
onSubmit I'm getting values of state through console.log but these values are not passing to Show component
class RegisterForm extends React.Component{
//constructor here
// handleInputChange function here
submit(){
console.log(this.state);
const data = this.state;
return <Redirect to={{
pathname: '/show',
state: {data: data},
}}
/>
}
render(){
return(
<div>
<div class="row">
<div class="col-md-6 offset-md-3">
<br /><br />
<h3>Register Form</h3><br />
<form
action="show"
>
<div class="form-row">
<div class="form-group col-md-6">
<label>First Name :</label>
<input type="text" class="form-control" name="first_name" onChange={this.handleInputChange} />
</div>
<div class="form-group col-md-6">
<label>Last Name :</label>
<input type="text" class="form-control" name="last_name" onChange={this.handleInputChange} />
</div>
</div>
<div class="form-row">
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-primary" onClick={()=>this.submit()}>Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
)
}
}
export default RegisterForm;
props.location.state // undefined
can you help me?
Form action - Attribute for form submission
The URL that processes the form submission.
Question
It redirects to show page because I've used action="show" can you
tell me why submit function is not calling
The submit function isn't linked to anything in the UI. The form has a button of type "submit", so when clicked the default form actions are taken, i.e. the form is submitted and tries to go to the page specified by the action. React doesn't quite work in this way.
Since the button type is already "submit" you can simply replace the action prop with the onSubmit callback.
<form onSubmit={submit}> ...
Now that the submit callback handler is being invoked you'll also want to not take the default submit action on the form (likely a page reload). Returning JSX won't work here either, it won't ever get rendered. In order to do the redirect you'll need to do this using the history object from the Router/Route. This assumes RegisterForm is rendered directly as component or render prop of a Route or has been decorated with the withRouter Higher Order Component.
submit(event) {
event.preventDefault();
console.log(this.state);
const data = this.state;
const { history } = this.props;
history.replace({
pathname: '/show',
state: { data },
});
}
I would recommend you to use Function Components insted of class components,
Class components are too old, Life becomes too much easier with function components, and react hooks. I suggest you to go though react fucntion components and react hooks.
here I converted your class component into the function component with some hooks,
in submit method, just use history.push('/show',state).
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
const RegisterForm = () => {
const [state, setState] = useState({
first_name: "",
last_name: "",
});
const history = useHistory();
const handleInputChange = (key) => (e) => {
let value = e.target.value;
setState((s) => ({ ...s, [key]: value }));
};
const handleSubmit = (e) => {
history.push("/show", state);
};
return (
<div>
<div className="row">
<div className="col-md-6 offset-md-3">
<br />
<br />
<h3>Register Form</h3>
<br />
<div className="form-row">
<div className="form-group col-md-6">
<label>First Name :</label>
<input
type="text"
className="form-control"
name="first_name"
value={state.first_name}
onChange={handleInputChange("first_name")}
/>
</div>
<div className="form-group col-md-6">
<label>Last Name :</label>
<input
type="text"
className="form-control"
name="last_name"
value={state.last_name}
onChange={handleInputChange("last_name")}
/>
</div>
</div>
<div className="form-row">
<div className="col-md-12 text-center">
<button
className="btn btn-primary"
onClick={handleSubmit}
>
Submit
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default RegisterForm;
Now, In the component at /show route,
you can use,
import { useLocation } from "react-router-dom";
....
....
// inside the Component,
let location = useLocation();
console.log(location);
console.log(location.state); // you can see, your state is available here...
that's it!
let me know if you found some difficulties here.
good luck ;)

Setting the default value of an input field after data is retrieved causes the content to overlap and the "onChange" event not to be triggered

I have an "edit category" component in my React application.
The ID is passed through the URL.
When the component is mounted, the action "fetchCategory" is called, which updates the props on the component with the current category.
I have a form which I want to be pre-populated, which I'm currently doing using the defaultValue on the input.
However, this isn't reflected on the state and the label for the text field overlaps the input field.
Any help would be greatly appreciated. I'll leave snippets of my code below which could help with understanding what I'm trying to do.
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchCategory } from "../../store/actions/categoryActions";
class AddOrEditCategory extends Component {
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id);
if (this.props.match.params.id) {
this.setState({
_id: this.props.match.params.id
});
}
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
render() {
const addingNew = this.props.match.params.id === undefined;
return (
<div className="container">
<h4>{addingNew ? "Add category" : "Edit category"}</h4>
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
defaultValue={this.props.category.name}
onChange={this.handleChange}
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
defaultValue={this.props.category.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
category: state.categoryReducer.category
};
};
export default connect(
mapStateToProps,
{ fetchCategory }
)(AddOrEditCategory);
EDIT: Included whole component as requested
You need to replace the 'defaultValue' attribute with 'value' in the inputs.
You are using a controlled vs uncontrolled component. You dont need to use defaultValue.
You can set the initial values on the promise success for fetchCategory
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id).then(response => {
// Set the initial state here
}
}
OR in
componentWillReceiveProps(nextProps) {
// Compare current props with next props to see if there is a change
// in category data received from action fetchCategory and set the initial state
}
React docs
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
onChange={this.handleChange}
value={this.state.name} //<---
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
value={this.state.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>

How should I pass input value with Redux without using this.state and setState

As I am learning Redux, I managed to use Provider, use child component, connect them etc.
But I have form in child component, I want to pass data from child to parent but on Submit. I don't want to use 'value onChange setState value' method. I want to use Redux with this.
I can also think of doing onChange dispatch but I want to pass values once onSubmit happens only.
import React from "react";
class InputComponent extends React.Component {
addNewTodo = (e) => {
e.preventDefault()
console.log(e.target);
};
render() {
return (
<form onSubmit={this.addNewTodo}>
<input
type="text"
class="form-control form-control-lg addTodoform"
id='addTodoform'
placeholder="Add new task or note"
defaultValue=''
/>
<button type="submit" class='btn btn-primary' >
Add
</button>
</form>
);
}
}
export default InputComponent;
One way to achieve what you want would be to use the document API to access the value of the form input addTodoform like so:
const inputValue = document.getElementById('addTodoform').value;
So, in your addNewTodo() handler, you might do the following:
class InputComponent extends React.Component {
addNewTodo = (e) => {
e.preventDefault()
console.log(e.target);
// Get value from input directly, without the use of setState, etc
const inputValue = document.getElementById('addTodoform').value;
// Dispatch inputValue to your redux action
// dispatch(submitAction(inputValue))
};
render() {
return (
<form onSubmit={this.addNewTodo}>
<input
type="text"
class="form-control form-control-lg addTodoform"
id='addTodoform'
placeholder="Add new task or note"
defaultValue=''
/>
<button type="submit" class='btn btn-primary' >
Add
</button>
</form>
);
}
}
Hope this helps

Highly reusable react component without any code change and use only properties

I would like to create a component which I can reuse also for other projects without any code change (only by passing different properties).
A small example:
I have a login form which has a username input and a password input which can look for example like this:
class Login extends Component {
static propTypes = {
login: PropTypes.func.isRequired,
defaultEmailValue: PropTypes.string,
defaultPasswordValue: PropTypes.string,
};
handleSubmit = (event) => {
if (event) {
event.preventDefault();
}
const username = this.refs.username;
const password = this.refs.password;
this.props.login(username.value, password.value);
};
render() {
const {
defaultEmailValue,
defaultPasswordValue,
} = this.props;
return (
<form method="post">
<input defaultValue={defaultEmailValue} ref="username" type="email" />
<input defaultValue={defaultPasswordValue} ref="password" type="password" />
<button onClick={this.handleSubmit} type="submit">Submit</button>
</form>
);
}
}
This is the minimal version of a login form, but what to do when I want to extend the render function to add container components like this (added column tags):
class Login extends Component {
/*....*/
render() {
const {
defaultEmailValue,
defaultPasswordValue,
} = this.props;
return (
<form method="post">
<div class="row">
<div class="col-md-6">
<input defaultValue={defaultEmailValue} ref="username" type="email" />
</div>
<div class="col-md-6">
<input defaultValue={defaultPasswordValue} ref="password" type="password" />
</div>
</div>
<button onClick={this.handleSubmit} type="submit">Submit</button>
</form>
);
}
}
Therefore I have always to modify the render function. Is there a good possibility to do this only with properties?
I estimated to do this with a Wrapper-Component over the Login component but then I have always rewrite the complete render function.
The next possible solution I thought about is to pass wrapper component classes through properties which can be rendered. But is this so a good solution or is this bad practice?
Unfortunately, I found no real solution for this tutorial in the internet and therefore I try it here. Thanks in advance :-)
Is there a good possibility to do this only with properties?
Representational Components are exactly for this kind of things.
export default const LoginForm = (/**props**/ {username, email, submitHandler, errors}) => {
return (
<form>
{/* as usual */}
</form>
)
}
use in your ContainerComponent:
...
render() {
return (
<div className="loginWrapper">
<LoginForm ...this.props ...this.state ...whatever username="pete" />
</div>
)
}
...

Resources