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>
Related
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
Based on the React Forms documentation, I am wondering why the following example is valid ?
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
As this.setState is asynchronous, you could have a change and a submit event that follow each other, so you would obtain the following senario : :
change event emitted
submit event emitted
handleChange is called, so this.setState is called, as it's asynchronous the state is not updated immediately
handleSubmit is called, this.state.name doesn't refer to the last value typed into the input field as the state update hasn't been applied yet.
The state is updated
So if my understanding is correct, in this example, the handleSubmit is not guaranteed to revrieve the last value typed into the input field ?
OK so I have an input field that whose value is pre-loaded when the component mounts (this.state.color) and I'm attempting to add a handleColorPicker() function either onClick or onFocus that will dropdown a color picker from https://casesandberg.github.io/react-color/. I'll paste the relevant code below:
import React, { Component } from 'react';
import { ChromePicker } from 'react-color';
class App extends Component {
constructor(props) {
super(props);
this.state = {
color: 'FFFFFF',
};
this.handleChange = this.handleChange.bind(this);
this.handleColorPicker = this.handleColorPicker.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleColorPicker(){
console.log('open');
return(
<ChromePicker />
)
}
render() {
return(
<input
className="App-textForm"
type="text"
name="color"
value={this.state.color}
onChange={this.handleChange}
onClick={this.handleColorPicker}
/>
);
}
}
As constructed, it console.logs 'open' every time I click on it.
Is there anything obvious I'm missing as to why that onClick wouldn't trigger the ChromePicker? I've tried changing onClick to onFocus and have tried wrapping my input in a div with an onClick={this.handleColorPicker}. Any help would be appreciated, thanks!
onClick event listener doesn't do anything with the returned component. You need to set a state the renders the component conditionally like
import React, { Component } from 'react';
import { ChromePicker } from 'react-color';
class App extends Component {
constructor(props) {
super(props);
this.state = {
color: 'FFFFFF',
isColorPickerOpen: false
};
this.handleChange = this.handleChange.bind(this);
this.handleColorPicker = this.handleColorPicker.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleColorPicker(){
console.log('open');
this.setState({ isColorPickerOpen: true });
}
render() {
return(
<React.Fragment>
<input
className="App-textForm"
type="text"
name="color"
value={this.state.color}
onChange={this.handleChange}
onClick={this.handleColorPicker}
/>
{isColorPickerOpen? <ChromePicker /> : null}
</React.Fragment>
);
}
}
The problem is that you are not rendering the picker, you are just returning it and then doing nothing with it.
What you need to do is render the picker, you can do that by using the state and hide/show the picker based on the user selection. For example:
state = {
showPicker: false,
};
handleColorPicker(){
console.log('open');
this.setState(current => ({
showPicker: !current.showPicker, // <-- Toggle the state
}));
}
render() {
const { showPicker } = this.state;
return (
<div>
<input
className="App-textForm"
type="text"
name="color"
value={this.state.color}
onChange={this.handleChange}
onFocus={this.handleColorPicker} // <--- Open the picker on focus
onBlur={this.handleColorPicker} // <--- Hide it on blur? You need to define when to hide it
/>
{ showPicker && <ChromePicker /> }
</div>
);
}
using onFocus and onBlur the state change and the picker gets renderer or not, based on the value of showPicker.
You need to define when to hide it, onBlur might not be what you want.
You are attaching your handleColorPicker to run correctly, but you shouldn't be returning the ChromePicker in that function.
Take a look at this CodeSandbox Example.
Notice how I have state in my component (isColorPickerOpen) that keeps track of whether the color picker is showing or not. My click handler toggles this state between true and false.
Then in my render method, I only render the ChromePicker component when isColorPickerOpen is true.
OK, so you are not rendering the <ChromePicker />
try this
class App extends Component {
constructor(props) {
super(props);
this.state = {
color: 'FFFFFF',
isPickerActive: false
};
this.handleChange = this.handleChange.bind(this);
this.handleColorPicker = this.handleColorPicker.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleColorPicker(){
this.setState({isPickerActive: true});
}
render() {
return(
<div>
<input
className="App-textForm"
type="text"
name="color"
value={this.state.color}
onChange={this.handleChange}
onClick={this.handleColorPicker}
/>
{this.state.isPickerActive && <ChromePicker />}
<div>
);
}
}
But it will just open the picker. Hope it will help.
With the onClick you are not render ChromePicker when it return.
Instead you can have an state to handle the ChromePicker render. Every time that you click or focus on you input, you can change the state, and then the component will be re render again.
Example:
import React, { Component } from 'react';
import { ChromePicker } from 'react-color';
class App extends Component {
constructor(props) {
super(props);
this.state = {
color: 'FFFFFF',
showChromePicker: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleColorPicker = this.handleColorPicker.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleColorPicker(){
console.log('open');
this.setState({
showChromePicker: true,
});
}
render() {
const { showChromePicker } = this.state;
return(
<div>
<input
className="App-textForm"
type="text"
name="color"
value={this.state.color}
onChange={this.handleChange}
onClick={this.handleColorPicker}
/>
{showChromePicker ? <ChromePicker /> : null}
</div>
);
}
}
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')} />
I have a form in react with many input components. I do not like that I have to write a new onChange handler method for every input component that I build. So I want to know how can I stop repeated code.
<Input
label={"Blog Name"}
hint={"e.g. 'The Blog'"}
type={"text"}
value={this.state.name}
onChange={this.handleInputChange.bind(this, "name")}
/>
<Input
label={"Blog Description"}
hint={"e.g. 'The Blog Description'"}
type={"text"}
value={this.state.desc}
onChange={this.handleInputChange.bind(this, "desc")}
/>
So instead of writing a new function I am reusing the same function and passing an extra value. Is this the right way to do it? How do other experienced people solve this problem.
If you want your parent component to maintain the state with the value of each input field present in 'Input' child components, then you can achieve this with a single change handler in the following way:
handleChange(id, value) {
this.setState({
[id]: value
});
}
where the id and value are obtained from the Input component.
Here is a demo: http://codepen.io/PiotrBerebecki/pen/rrJXjK and the full code:
class App extends React.Component {
constructor() {
super();
this.state = {
input1: null,
input2: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id, value) {
this.setState({
[id]: value
});
}
render() {
return (
<div>
<Input id="input1"
changeHandler={this.handleChange} />
<Input id="input2"
changeHandler={this.handleChange} />
<p>See input1 in parent: {this.state.input1}</p>
<p>See input2 in parent: {this.state.input2}</p>
</div>
);
}
}
class Input extends React.Component {
constructor() {
super();
this.state = {
userInput: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const enteredText = event.target.valuel
this.setState({
userInput: enteredText
}, this.props.changeHandler(this.props.id, enteredText));
}
render() {
return (
<input type="text"
placeholder="input1 here..."
value={this.state.userInput}
onChange={this.handleChange} />
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can try event delegation, just like the traditional ways.
That is, just bind a function to the parent form element, and listens to all the events bubbling up from the children input elments.