handleSubmit function appears to be refreshing page without firing any internal logic - javascript

The handleSubmit function seems to refresh the page without firing any of the internal logic. I've set up a few console.log's along the way to test out if the internally declared const that's set to the venue property in the state would log, but nothing appears.
I've commented on various parts of the function stepwise starting with setting the scheduling variable to my Firebase schedule table.
After that, I changed the handleSubmit function from an arrow function to just handleSubmit(e) (sorry, I'm new to this so I'm not familiar with the terminology)
import React, {Component} from 'react';
import FrontNav from './nav.js';
import firebase from '../Firebase';
class FrontSchedule extends Component {
constructor () {
super();
this.state = {
venue:'',
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
e.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
return(
<div>
<FrontNav/>
<h1>Schedule</h1>
<form>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<button onSubmit={this.handleSubmit}> Enter Event </button>
</form>
</div>
);
}
}
export default FrontSchedule;
Herein lies the crux of the problem. The page refreshes and the input bar clears, but no error message is provided. At this point, I'm really confused about what is going on here. Any feedback is appreciated!

Let us consider the following example:-
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
Now, when we press on submit button the default behavior to browse to a new page. If you want this behavior it works out of the box in ReactJS. But in cases where you need more sophisticated behavior like form validations, custom logic after the form is submitted you can use controlled components.
We can do so by using following:-
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
Now coming to your solution it can be implemented as follows:-
class FrontSchedule extends React.Component {
constructor () {
super();
this.state = {
venue:'',
}
/* this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); */
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
event.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
console.log(this.state);
return(
<div>
<h1>Schedule</h1>
<form onSubmit={this.handleSubmit}>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
ReactDOM.render(<FrontSchedule />, document.querySelector("#app"))
Hope it helps :)
You can read more at react documentationhere

Related

trouble getting data from react input form

I am just making a simple app to learn react with redux.
I just want to get data input in the react input form on the server-side.
The problem is that the params on the server-side is like this.
{"item"=>{"name"=>"undefined","price"=>"undefined"...}...}
Here is part of my code:
import React from "react";
class ItemForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.update = this.update.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const ItemData = new FormData();
ItemData.append("item[name]", this.props.item.name);
ItemData.append("item[price]", this.props.item.price);
};
update(field) {
return (e) => {
this.setState({ [field]: e.target.value });
};
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<label>
<div>Item</div>
<input
type="text"
value={this.props.item.name}
onChange={this.update("name")}
/>
</label>
<label>
<div>Price</div>
<input
type="number"
value={this.props.item.price}
onChange={this.update("price")}
/>
</label>
<div>
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
);
}
}
Shoule I use store function in redux or is there more easy way?
Thanks.
Assumption
I'm assuming that the problem is that the data you can access in your handleSubmit function is not being updated, therefore you always receive the values that you initialized the component with.
Solution
Initialize the state based on the name and price props passed in
Set the value of your input tags to the state values
Access the state in your handleSubmit function
import React from "react";
import { withRouter } from "react-router-dom";
class ItemForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.update = this.update.bind(this);
this.state = {
name: this.props.item.name,
price: this.props.item.price
}
}
handleSubmit(e) {
e.preventDefault();
const ItemData = new FormData();
ItemData.append("item[name]", this.state.name);
ItemData.append("item[price]", this.state.price);
};
update(field) {
return (e) => {
this.setState({ [field]: e.target.value });
};
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<label>
<div>Item</div>
<input
type="text"
value={this.state.name}
onChange={this.update("name")}
/>
</label>
<label>
<div>Price</div>
<input
type="number"
value={this.state.price}
onChange={this.update("price")}
/>
</label>
<div>
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
);
}
}
export default withRouter(ItemForm);
Other suggestions
Your redux wrapper component doesn't have any big effects on this, so you can remove it from this question for clarity 😀
This is actually one of the benefits of redux, the connected component (ItemForm) is a regular React component and does not have any knowledge that it will be acted on by redux
Hope that helps! 👍

Saving state of inputs added by user in React

I working on a small personal project using React on Rails. I am very new to both of these things.
I have a react component that is a form. I also have another component that has some inputs that the user can add as many as needed. Using the Add Properties button. I am trying to save the state of each input that is added. I could have the component itself save the state but how then would I send it with my fetch post request that happens onClick?
I have looked at react's context API but cant figure out if this would help me. Also I have never used redux so it is possible I should look into that as well.
https://reactjs.org/docs/context.html
https://redux.js.org/basics/usagewithreact
I understand that I don't want to reach into state. So I was trying to figure out how to create an array of objects that will hold the input values of each input pair. But I cannot seem to wrap my mind around how to implement.
class ProductsCreate extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
upc: '',
availableOn: '',
inputs: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.name === 'name') {
this.setState({ name: e.target.value });
}
if (e.target.name === 'upc') {
this.setState({ upc: e.target.value });
}
if (e.target.name === 'date') {
this.setState({ availableOn: e.target.value });
}
}
submitData = () => {
fetch(`/send_data`, {
method: 'POST',
body: JSON.stringify({
name: this.state.name,
upc: this.state.upc,
availableOn: this.state.availableOn
}),
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
})
.then(response => {
return response.json;
})
.then(data => {
console.log(data);
});
};
clickHandler = e => {
e.preventDefault();
this.submitData();
};
appendInput = e => {
e.preventDefault();
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
};
render() {
return (
<div className="form_container">
<h1>Products</h1>
<form>
<label>Name</label>
<input type="text" name="name" onChange={this.handleChange} />
<label>UPC</label>
<input type="text" name="upc" onChange={this.handleChange} />
<label>Availiable On</label>
<input
type="text"
name="date"
placeholder="mm/dd/yyyy"
onChange={this.handleChange}
/>
<h1>Properties</h1>
{this.state.inputs.map(input => (
<Properties key={input} />
))}
<button onClick={this.appendInput}>Add Properties</button>
<button onClick={this.clickHandler}>Save</button>
</form>
</div>
);
}
}
export default ProductsCreate;
This is the component that will be added on click
import React from 'react';
class Properties extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="property_container">
<label>Property Name</label>
<input type="text" name="propertyName" />
<label>Property Value</label>
<input type="text" name="propertyValue" />
</div>
);
}
}
export default Properties;
Pass handle change as prop to Properties component and then use that prop in input onChange within your Properties component. Make the following edits. I also suggest if you don't want to continously update the state on every character typed use debounce.
ProductsCreate
{this.state.inputs.map(input => (
<Properties key={input} onChange={this.handleChange} name={input}/>
))}
Properties
<input type="text" name={this.props.name} onChange={this.props.onChange}/>

React: Render component based on click

I currently have an input field set up so the inputs value will be console logged when a button is pressed, I am trying to render a component instead of this console.log.
I can get it to sorta work but it re-renders every time I type a single character because I'm not sure how to check for the click within the render method, I read through some of the docs but couldn't figure out how do to it. How could I go about achieving this?
here is the code
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.input) {
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
let me know if you need anymore information. Thanks!
Edit: By the way, I want to be able to submit the form many times, sorry, I should have been more descriptive of my problem. I want the Form component to stay rendered and I want the Fetch component to get rendered on clicked
A solution depends on your needs and there might be a lot of them. The simplest way, just to demonstrate:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.submitted) { // show the component by the flag
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
Obviously, in real application it's too simple logic. You have to define first, what are you trying to reach.
UPDATE:
If you want to keep the input field and the component simultaneously, you can do it like this:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
{this.state.submitted && (
<Fetch username={this.state.input} />
)}
</form>
);
}
}
Actually what you are doing is every time the user type a character, you are displaying Fetch component with the input's value passed in props.
With this and with the handleChange method
if (this.state.input) {
return <Fetch username={this.state.input} />
}
The condition here return true if input is different from "". So when a character is type it becomes true and displays it.
In your case what you want to do is to display the Fetch component when you are clicking on the submit button.
What you can do is create a state to manage it.
For example you could have this in your state :
this.state = {
input: '',
fetchIsVisibile: false,
};
Then in your handleSubmit just do this:
handleSubmit(e) {
this.setState({ fetchIsVisible: true }),;
e.preventDefault();
}
And the condition for display your fetch component would be using this new state instead of input state
if (this.state.fetchIsVisible) {
return <Fetch username={this.state.input} />
}
You can add state whether the form is submitted in the state and to check for it when rendering the component
if you want to check whether the form is submitted and the input is not empty you can use (this.state.submitted && this.state.input) instead
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
this.setState({
submitted: true
});
}
render() {
//Not sure how to check for this
if (this.state.submitted) {
return <div>
<div>FETCH COMPONENT</div>
<div>input: {this.state.input}</div>
</div>
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<div>input: {this.state.input}</div>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
<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="app"></div>

How not to refresh the page after onClick

I have small class in react, i want to display the result on the screen after i click on the button, but before the display happens, the page reload.
how do i do it?
what am I missing?
import React, {Component} from 'react';
class InputFieldWithButton extends Component{
constructor(props){
super();
this.state = {
message: ''
};
}
handleChange(e){
this.setState({
message: e.target.value
});
}
doSomething(e){
return(
<h1>{this.state.message}</h1>
)
}
render(){
return (
<div>
<form >
<input type="text" placeholder="enter some text!" value=
{this.state.message}
onChange={this.handleChange.bind(this)}/>
<button onClick={this.doSomething.bind(this)}>Click me</button>
</form>
</div>
)
}
}
export default InputFieldWithButton;
Your button is inside a form and triggering a submit.
You can use the preventDefault() method to stop it from doing so:
doSomething(e) {
e.preventDefault();
return (
<h1>{this.state.message}</h1>
)
}
By the way, your return statement of this click handler makes no sense at the moment.
Edit
As a followup to your comment:
Can you explain me what is my mistake in the return?
Not really a mistake, but it is useless in this context as your are not doing anything with the returned object.
Where and how do you expect to use the <h1>{this.state.message}</h1> that you are returning?
If you intend to show / hide the input message in your screen you could do it with conditional rendering.
Just store a bool like showMessage in your state and render the message only if it's set to true.
Here is a small example:
class InputFieldWithButton extends React.Component {
constructor(props) {
super(props);
this.state = {
message: '',
showMessage: false
};
}
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
toggleMessage = (e) => {
e.preventDefault();
this.setState({ showMessage: !this.state.showMessage })
}
render() {
const { showMessage, message } = this.state;
return (
<div>
<form >
<input
type="text"
placeholder="enter some text!"
value={message}
onChange={this.handleChange}
/>
<button onClick={this.toggleMessage}>Toggle Show Message</button>
{showMessage && <div>{message}</div>}
</form>
</div>
)
}
}
ReactDOM.render(<InputFieldWithButton />, document.getElementById('root'));
<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>
By the way, it is considered as bad practice to bind the functions inside the render method, because you are creating a new instance of a function on each render call. instead do it inside the constructor which will run only once:
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
Or you can use arrow functions which will reference this in a lexical context:
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
This is what i've used in my example.
you're not specifying the buttons'type
<button type="button">
Set the type attribute on the button to be button. The default is submit since it is wrapped in a form. So your new button html should look like this:
<button type="button" onClick={this.doSomething.bind(this)}>Click me</button>

How to handle multiple controlled inputs with react es6?

Here's my fiddle
https://codepen.io/seunlanlege/pen/XjvgPJ?editors=0011
I have two inputs and I'm trying to use one method to handle the onChange event for any input field.
I've torn the internet apart looking for a solution but came up with nothing.
I'm using es6 please how do I go about this?
class Form extends React.Component {
`constructor(props) {
super(props);
this.state = {text:{
e:'hi',
c:''
}};
this.handleSubmit = this.handleSubmit.bind(this);
}`
`handleChange(event,property) {
const text = this.state.text;
text[property] = event.target.value;
this.setState({text});
}`
`handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}`
`render() {
return (
<div>
<div>{this.state.text.e}</div>
<input type="text"
placeholder="Hello!"
value={this.state.text.e}
onChange={this.handleChange.bind(this)} />
<input type="text"
placeholder="Hello!"
value={this.state.text.c}
onChange={this.handleChange.bind(this)} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}`
ReactDOM.render(
`<Form />`,
document.getElementById('root')
);
You have not passed the propert to the handeChange function. pass it like this.handleChange.bind(this, 'e') and also the order of receiving props is wrong, property will be the first argument and then the event and not the reverse.
Code:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {text:{
e:'hi',
c:''
}};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(property, event) {
console.log(event.target.value);
const text = {...this.state.text};
text[property] = event.target.value;
this.setState({ text }); //or you can use the shorthand here. ES6 is awesome <3
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}
render() {
return (
<div>
<div>{this.state.text.e}</div>
<div>{this.state.text.c}</div>
<input type="text"
placeholder="Hello!"
value={this.state.text.e}
onChange={this.handleChange.bind(this, 'e')} />
<input type="text"
placeholder="Hello!"
value={this.state.text.c}
onChange={this.handleChange.bind(this, 'c')} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
CodePen
One way to do this would be to give each of your inputs a name attribute and set the state based on that:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = { text: {
e: 'hi',
c: ''
} };
this.onChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
var oldState = this.state.text;
var newState = { [e.target.name]: e.target.value };
// I have to assign/join because you've put the text state in a parent object.
this.setState({ text: Object.assign(oldState, newState) });
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}
render() {
console.log(this.state);
return (
<div>
<div>{this.state.text.e}</div>
<input type="text"
placeholder="Hello!"
name="e"
value={this.state.text.e}
onChange={this.onChange} />
<input type="text"
placeholder="Hello!"
name="c"
value={this.state.text.c}
onChange={this.onChange} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('View')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react-dom.min.js"></script>
<div id="View"></div>
Also, there are so-called two-way binding helpers. As I understand they still show mixins in React's documentation, so you are probably better off with third party libraries like react-link-state:
this.state = {
username: '',
password: '',
toggle: false
};
<input type="text" valueLink={linkState(this, 'username')} />
<input type="password" valueLink={linkState(this, 'password')} />
<input type="checkbox" checkedLink={linkState(this, 'toggle')} />

Categories