I have a form:
<form onSubmit={this.onSubmit}>
<input ref="name" type="text" />
...
<select ref="pet">
<option>Dog</option>
<option>Cat</option>
</select>
</form>
In another place, I have a different form, with different inputs, but the same select. I could simply bindly copy the code from the first one, but I don't want to.
I want to make a component. In terms of UI, I know it would work. However, I have no idea how to access this.refs.pet.value in that case:
<form onSubmit={this.onSubmit}>
<input ref="name" type="text" />
...
<PetsSelect ??????? />
</form>
How to access the value of the select box from the component, in its parent (form)?
very quick example on composing
class PetsSelect extends React.Component {
get value(){
return this.state.value
}
handleChange(key, value){
this.setState({[key]: value})
this.props.onChange && this.props.onChange(key, value)
}
constructor(props){
super(props)
this.state = { value: props.value || '', name: '' }
}
render(){
// add the name etc and then you can handleChange('name', ...)
// or make it more DRY
return <div>
<select
ref={select => this.select = select}
value={this.state.value}
onChange={e => this.handleChange('value', e.target.value)}>
<option value=''>Please select</option>
<option value='dog'>Dog</option>
<option value='cat'>Cat</option>
</select>
</div>
}
}
class Form extends React.Component {
handleSubmit(e){
e.preventDefault()
console.log(this.pets.value)
}
render(){
// this.pets becomes the instance of the PetsSelect class.
return <form onSubmit={e => this.handleSubmit(e)}>
<PetsSelect ref={pets => this.pets = pets} />
<button type='submit'>try it</button>
</form>
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
see here: https://codepen.io/anon/pen/WExepp?editors=1010#0. basically, you can either: onChange and get the value in the parent, or read the value of the child when needed.
keep in mind you said 'controlled' - i am not doing anything to keep props.value with state.value - and in uncontrolled, you'd use defaultValue
Just add ref like this <PetsSelect ref="petSelect"/> and get value by this this.refs.petSelect.refs.pet.value .
class FormComponent extends React.Component{
constructor(props){
super(props)
this.state = {selected: null,
...
}
}
selectPet(e){
this.setState({selected: e.target.value})
}
render(){
return (<form onSubmit={this.onSubmit}>
<input ref="name" type="text" />
...
<PetsSelect onSelect={this.selectPet.bind(this)} />
</form>)
}
}
class PetsSelect extends React.Component{
constructor(props){
super(props)
}
render(){
return (<select onChange={this.props.onSelect}>
<option value='dog'>Dog</option>
<option value='cat'>Cat</option>
</select>)
}
}
Related
I'm trying for this component to show the specific form based upon the option select. I'm using the switch statement for conditional rendering. But at the moment it's not working, what am I missing?
class Form extends Component {
state = {
selectedValue: ''
};
handleChange(event) {
this.setState({selectedValue: event.target.value});
}
renderSelectedForm = (param) => {
const formStyle = {
display: 'none'
}
switch(param) {
case 'form_name1':
return <form name="form_name1" id="form_name1" style={formStyle}>
form 1
</form>;
case 'form_name2':
return <form name="form_name1" id="form_name2" style={formStyle}>
form 2
</form>;
case 'form_name3':
return <form name="form_name1" id="form_name3" style={formStyle}>
form 3
</form>;
default:
return null;
}
}
render() {
return (
<div>
<div className={styles.ContactUs}>
<form >
<select value={this.state.selectedValue} onChange={this.handleChange}>
<option value="form_name1">Form 1</option>
<option value="form_name2">Form 2</option>
<option value="form_name3">Form 3</option>
</select>
</form>
{this.renderSelectedForm(this.state.selectedValue)}
</div>
</div>
);
}
}
export default Form;
Your display is set to 'none'.
handleChange = (event) => {
this.setState({ selectedValue: event.target.value });
}
You either need to make a constructor that binds this for the handleChange function or just declare it in the way above, which auto-binds.
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
Set a default value in your state for the first form to be shown, such as form_name1. Or add an option like 'Select a form' to your select element.
If you're doing conditional rendering, you don't really need to set display: none in your elements. Each element will be rendered according to your current state and null is returned for unmounting the component.
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedValue: 'form_name1'
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({selectedValue: event.target.value});
}
renderSelectedForm (param) {
switch(param) {
case 'form_name1':
return (
<form name="form_name1" id="form_name1">
form 1
</form>
)
case 'form_name2':
return (
<form name="form_name1" id="form_name2">
form 2
</form>
)
case 'form_name3':
return (
<form name="form_name1" id="form_name3">
form 3
</form>
)
default: return null;
}
}
render() {
return (
<div>
<form>
<select value={this.state.selectedValue} onChange={this.handleChange}>
<option value="form_name1">Form 1</option>
<option value="form_name2">Form 2</option>
<option value="form_name3">Form 3</option>
</select>
</form>
{this.renderSelectedForm(this.state.selectedValue)}
</div>
);
}
}
Here's in action: https://codepen.io/herodrigues/pen/XOBLVb
You need to wrap inside (). Like below
switch(param) {
case 'form_name1':
return (<form name="form_name1" id="form_name1" style={formStyle}>
form 1
</form>);
...
}
I have a datalist that I want to be pre-populated based on the prop I am passing down. It correctly sets the value, but does not allow the dropdown value to be changed and basically disables it. Any ideas on how to fix this?
<input list="facilitators" name="facilitator" id="facilitatorsInput" value={this.props.breakout.facilitatorId}></input>
<datalist id="facilitators">
{
this.state.facilitators.map((facilitator) => <option value={facilitator.id} >{facilitator.firstName} {facilitator.lastName}</option>)
}
</datalist>
It looks like you are setting the value, but you have no way to change the value. Your <input /> needs an onChange handler.
Here's a working example: https://codesandbox.io/s/xrv9q019zo
const options = ["banana", "tomato", "apple"];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "banana"
};
}
render() {
return (
<div className="App">
<input
onChange={e => {
this.setState({ input: e.target.value });
}}
value={this.state.input}
list="stuff"
/>
<datalist id="stuff">
{options.map(opt => (
<option value={opt} />
))}
</datalist>
</div>
);
}
}
If you are getting the default value from the parent component, you may want to pass the onChange handler from the parent and manage the state there.
I'm doing a React coding challenge that requires a value to be updated onKeyUp. I initially set it to update onChange but the tests require onKeyUp so I tried to change it to that, but my fields are no longer updating and I can't type anything into the textarea.
class MarkdownApp extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
this.handleKeyUp = this.handleKeyUp.bind(this);
}
handleKeyUp(event) {
this.setState({ value: event.target.value })
}
render() {
return (
<form>
<label>
Enter your markdown here:
<br />
<textarea value={this.state.value} onKeyUp={this.handleKeyUp} id='editor' />
<br />
</label>
<label>
Your markup will be previewed here:
<p id='preview'>{marked(this.state.value)}</p>
</label>
</form>
);
}
}
ReactDOM.render(
<MarkdownApp />,
document.getElementById('root')
);
Like I said, this worked fine when it was onChange and my function was handleChange, but since I switched it I can't type anything.
I would just remove the value attribute from the textarea. Because if you put the value attribute to it then the user won't be able to change it interactively. The value will always stay fixed(unless you explicitly change the value in your code). You don't need to control that with React--the DOM will hold onto the value for you.
The only change I've made below is to remove value={this.state.value} from the textarea element:
import React from 'react';
import ReactDOM from 'react-dom';
class MarkdownApp extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
this.handleKeyUp = this.handleKeyUp.bind(this);
}
handleKeyUp(event) {
this.setState({ value: event.target.value })
}
render() {
return (
<form>
<label>
Enter your markdown here:
<br />
<textarea value={this.state.value} onKeyUp={this.handleKeyUp} id='editor' />
<br />
</label>
<label>
Your markup will be previewed here:
<p id='preview'>{this.state.value}</p>
</label>
</form>
);
}
}
ReactDOM.render(
<MarkdownApp />,
document.getElementById('root')
);
Since the event happens before the actual value of the textbox is changed, the result of event.target.value is an empty string. Setting the state with the empty string, clears the textbox.
You need to get the pressed key value from the event, and add it to the existing state.value.
Note: I've removed marked from the demo
class MarkdownApp extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
this.handleKeyUp = this.handleKeyUp.bind(this);
}
handleKeyUp(event) {
const keyValue = event.key;
this.setState(({ value }) => ({
value: value + keyValue
}))
}
render() {
return (
<form>
<label>
Enter your markdown here:
<br />
<textarea value={this.state.value} onKeyUp={this.handleKeyUp} id='editor' />
<br />
</label>
<label>
Your markup will be previewed here:
<p id='preview'>{this.state.value}</p>
</label>
</form>
);
}
}
ReactDOM.render(
<MarkdownApp />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
You could make the textarea uncontrolled by not giving it the value and simply storing the value in state from a ref instead.
Example (CodeSandbox)
class MarkdownApp extends React.Component {
ref = null;
state = {
value: ""
};
handleKeyUp = event => {
this.setState({ value: this.ref.value });
};
render() {
return (
<form>
<label>
Enter your markdown here:
<br />
<textarea
onKeyUp={this.handleKeyUp}
ref={ref => (this.ref = ref)}
id="editor"
/>
<br />
</label>
<label>
Your markup will be previewed here:
<p id="preview">{marked(this.state.value)}</p>
</label>
</form>
);
}
}
The issue is you have a two way binding with the state = to the value in your textbox. OnChange would update your state after a change is made and the events are done firing. Onkeyup returns the value onkeyup and since you mapped that to your state it will stay as nothing. Remove the value prop and it should work.
I was trying to handle changing of states whenever I type something inside the two text boxes and then when the user click the button, it will set the state to it's state and then console.log the current change state to the console.
Basically I have this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
catname: '',
catamt: 0
};
this.addBudget = this.addBudget.bind(this);
}
addBudget(e) {
e.preventDefault();
this.setState({
catname: e.target.value,
catamt: e.target.value
});
console.log('console log catname here.....', this.state.catname);
console.log('console log catamt here.....', this.state.catamt);
}
}
And then inside my component where the form is sitting:
import React from 'react';
export default class AddBudget extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="cat-input">
<input
type="text"
name="categoryname"
placeholder="Budget Category"
/>
<input
type="number"
name="categoryamount"
placeholder="Target Budget"
/>
</div>
<button onClick={this.addBudget}>+</button>
);
}
}
How do I pass along my input value to my function and console log the change of state?
Something more like that, I recommended using controlled input with react.
You can read more about it here https://reactjs.org/docs/forms.html
An example for you :) https://codesandbox.io/s/2486wxkn9n
First you need to keep track on the value with the state. Second with the form you can handle the submit. This way if a user click the button or press enter you can handle the submit method.
Inside the _handleChange method you receive the event. So this is the input change. If you console.log this value you can see he have the name, the name you pass in the input. This way you can use it as a key variable for your object. So one function for 2 :).
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
catname: '',
catamt: 0
};
this.addBudget = this.addBudget.bind(this);
}
addBudget = (e) => {
e.preventDefault();
console.log('console log catname here.....', this.state.catname);
console.log('console log catamt here.....', this.state.catamt);
}
_handleChange = e => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<AddBudget handleChange={this._handleChange} addBudget={this.addBudget} />
)
}
}
export default class AddBudget extends React.Component {
render() {
return (
<div className="cat-input">
<form onSubmit={this.props.addBudget}>
<input
type="text"
name="catname"
onChange={this.props.handleChange}
placeholder="Budget Category"
/>
<input
type="number"
name="catamt"
placeholder="Target Budget"
onChange={this.props.handleChange}
/>
<button type="submit">+</button>
</form>
</div>
);
}
}
I have form in which 2 fields, and on click i want to save fields data into the state object.
I don't want to make different function for every input field on change.
Code:
import React, {Component} from 'react';
export default class Todo extends Component {
constructor(props){
super(props);
this.state = {
data : {
"name":'',
"option":'',
},
},
this.inputChange = this.inputChange.bind(this);
this.handleForm = this.handleForm.bind(this);
}
inputChange = (propertyName,e) => {
this.setState({});
}
handleForm = () => {
console.log(this.state.data);
console.log(this.state.data.name);
console.log(this.state.data.option);
}
render(){
return(
<div>
<form onSubmit={this.handleForm}>
<input type="text" placeholder="Enter text" name="name" value={this.state.data.name} onChange={this.inputChange.bind(this, "name")} />
<select name="option" value={this.state.data.option} onChange={this.inputChange.bind(this, "option")}>
<option> Select Option </option>
<option value="1"> Option 1 </option>
<option vlaue="2"> Option 2 </option>
</select>
<button type="submit"> Submit </button>
</form>
</div>
);
}
}
As per MDN DOC:
The object initializer syntax also supports computed property names.
That allows you to put an expression in brackets [], that will be
computed and used as the property name.
Use this:
inputChange = (propertyName,e) => {
let data = {...this.state.data};
data[propertyName] = e.target.value;
this.setState({ data });
}
Working Code:
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {
data : {
"name":'',
"option":'',
},
},
this.inputChange = this.inputChange.bind(this);
this.handleForm = this.handleForm.bind(this);
}
inputChange = (propertyName,e) => {
let data = {...this.state.data};
data[propertyName] = e.target.value;
this.setState({ data });
}
handleForm = () => {
console.log(this.state.data);
console.log(this.state.data.name);
console.log(this.state.data.option);
}
render() {
console.log(this.state.data)
return (
<div>
<form onSubmit={this.handleForm}>
<input type="text" placeholder="Enter text" name="name" value={this.state.data.name} onChange={this.inputChange.bind(this, "name")} />
<select name="option" value={this.state.data.option} onChange={this.inputChange.bind(this, "option")}>
<option> Select Option </option>
<option value="1"> Option 1 </option>
<option vlaue="2"> Option 2 </option>
</select>
<button type="submit"> Submit </button>
</form>
</div>
);
}
}
ReactDOM.render(<Todo />, document.body)
<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>
Following the react tutorial:
class Example extends React.Component {
constructor() {
super();
this.state = {email:''};
this.handleEmailChange = this.handleEmailChange.bind(this);
}
handleEmailChange(event) {
this.setState({email: event.target.value});
}
render() {
return(
<div>
<input type="email" id="email" class="form-control" placeholder="Email"
value={this.state.email} onChange={this.handleEmailChange} />
</div>
);
}
}