Correct way to take input values in React.js - reactjs

I am new to react and i am wondering if this is the correct way (somewhat quallity code) to get values from input forms.
import React from 'react';
class Form extends React.Component {
constructor() {
super();
this.state = {
nameValue: '',
ageValue: ''
}
}
onChangeName(event) {
this.setState({
nameValue:event.target.value
});
}
onChangeAge(event) {
this.setState({
ageValue: event.target.value
});
}
showValue(){
alert('name '+ this.state.nameValue + ' age: ' + this.state.ageValue)
}
render() {
return (
<form>
<label>Name:
<input type="text" onChange={this.onChangeName.bind(this)}/>
</label>
<label>Age:
<input type="text" onChange={this.onChangeAge.bind(this)}/>
</label>
<input type="submit" value="Submit" onClick={this.showValue.bind(this)}/>
</form>
);
}
}
export default Form;
I mean i heared that the way it's done in react is so that every component should be somewhat independant and have it's own behaviour. Btw the code works just asking for the qualiity cause i have project in from of me. Or should i just add an event to the button and make the other componenents stateless, i.e the 2 input fields

Yes, this is the correct way.
Suggestions:
1. Instead of using two different change function, you can handle all the input elements change by a single change function. For that assign the name (same as state variable name) property with input element, and inside change function access that state variable name by event.target.name and update that.
Run this snippet:
class Form extends React.Component {
constructor() {
super();
this.state = {
nameValue: '',
ageValue: ''
}
this.commonChange = this.commonChange.bind(this);
this.showValue = this.showValue.bind(this);
}
commonChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
showValue(){
event.preventDefault();
alert('name '+ this.state.nameValue + ' age: ' + this.state.ageValue)
}
render() {
return (
<form>
<label>Name:
<input type="text" name="nameValue" onChange={this.commonChange}/>
</label>
<label>Age:
<input type="text" name="ageValue" onChange={this.commonChange}/>
</label>
<input type="submit" value="Submit" onClick={this.showValue}/>
</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'/>
2. You are not controlling the input elements by state values (not using the value property of input elements), just storing the values in state, to get those value during submit call. So storing the values in state variable is not required, you can use uncontrolled component, and use ref to get the values.
Define the ref like this:
<input type="text" ref={el => this.nameValue} />
And access the value by this.nameValue.value
Run this snippet:
class Form extends React.Component {
constructor() {
super();
this.showValue = this.showValue.bind(this);
}
showValue(){
alert('name '+ this.nameValue.value + ' age: ' + this.ageValue.value)
}
render() {
return (
<form>
<label>Name:
<input type="text" ref={el => this.nameValue=el} />
</label>
<label>Age:
<input type="text" ref={el => this.ageValue=el} />
</label>
<input type="submit" value="Submit" onClick={this.showValue}/>
</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'/>
3. Always define binding of the functions in the constructor.
Check this answer for more details: why do you need to bind a function in a constructor

Related

Possible to Interact with Custom Javascript Object Handler

I am trying to migrate the handling of my bootstrap date range picker from jQuery to ReactJS and while I am able to handle most interactions, I am struggling to figure out how I can migrate the following method to my reactjs setup>
This interaction takes the values selected from the calendar component on "Apply" and then sets two hidden input fields that I have that are sent to my server on form submission.
jQuery:
//Set annotationDateRange value on picker selection
$('input[name="annotationDateRange"]').on('apply.daterangepicker', function(ev, picker) {
$(this).val(picker.startDate.format('MM/DD/YYYY') + ' - ' + picker.endDate.format('MM/DD/YYYY'));
$("input[name='annotationStartDate']").val(picker.startDate.format('MM/DD/YYYY'));
$("input[name='annotationEndDate']").val(picker.endDate.format('MM/DD/YYYY'));
});
ReactJS (I thought add the handleChange() to the field would pick up on the calendar selection changes, but it appears they populate the text field in a way that the virtual DOM does not pick up on it):
import React from 'react';
import isEqual from 'lodash/isEqual';
export default class DateFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
endDateValue:this.props.endDateQuery ? this.props.endDateQuery: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
console.log("New Handle Change")
/*console.log(input + " " + value)
this.setState({
[input]: value
})*/
}
componentDidMount() {
this.setState({
startDateValue: this.props.startDateQuery,
endDateValue: this.props.endDateQuery
});
}
componentWillReceiveProps(nextProps) {
if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
render() {
return (
<div className="col-md-3">
<div className="input-group annotation-filter-date-range-picker">
<p>Annotation Date Range:</p>
</div>
<div className="input-group annotationFilterDatePicker">
<span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
<input type="text" name="annotationDateRange" className="form-control annotationFilterDatePicker" onChange={this.handleChange} autoComplete="off" />
</div>
<input type="hidden" name="annotationStartDate" className="form-control" value={this.state.startDateValue ? this.state.startDateValue : ""} onChange={this.handleChange} />
<input type="hidden" name="annotationEndDate" className="form-control" value={this.state.endDateValue ? this.state.endDateValue : ""} onChange={this.handleChange} />
</div>
);
}
}
Use arrow functions to not lose the Component scope.
handleChange = (event) => {
this.setState({
[input]: value
})
}
Or you can just call it as an arrow function
<input type="text" name="annotationDateRange" className="form-control annotationFilterDatePicker" onChange={(event) => this.handleChange(event)} autoComplete="off" />
In a NON ES6 way you can just bind 'this' to the function.
<input type="text" name="annotationDateRange" className="form-control annotationFilterDatePicker" onChange={this.handleChange.bind(this)} autoComplete="off" />

How to dynamically generate pair of input fields using reactjs

I have tried the following code to create a react form to dynamically generate input fields to enter the series of person's name one by one. But user needs to enter the first name and last name instead of just name. So that, the form needs to generate pair of dynamic input fields. I am new to react. Can anyone please give an hint on how to accomplish this.
Note : The following code has been taken from the stackoverflow answer of #Mayank Shukla at How to implement a dynamic form with controlled components in React.JS?.
class App extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
this.handleSubmit = this.handleSubmit.bind(this);
}
createUI(){
return this.state.values.map((el, i) =>
<div key={i}>
<input type="text" value={el||''} onChange={this.handleChange.bind(this, i)} />
<input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
</div>
)
}
handleChange(i, event) {
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
}
addClick(){
this.setState(prevState => ({ values: [...prevState.values, '']}))
}
removeClick(i){
let values = [...this.state.values];
values.splice(i,1);
this.setState({ values });
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.values.join(', '));
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<input type='button' value='add more' onClick={this.addClick.bind(this)}/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(<App />, document.getElementById('container'));
Idea is, maintain an array of object in state variable. Each object will have two keys firstName and secondName (you can add more fields). Treat each object as a single unit and for all the keys render input element, and whenever user will click on add more, add one more object/entry the the array with two keys.
Working Fiddle.
Working Snippet:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [{firstName: "", lastName: ""}]
};
this.handleSubmit = this.handleSubmit.bind(this);
}
addClick(){
this.setState(prevState => ({
users: [...prevState.users, { firstName: "", lastName: "" }]
}))
}
createUI(){
return this.state.users.map((el, i) => (
<div key={i}>
<input placeholder="First Name" name="firstName" value={el.firstName ||''} onChange={this.handleChange.bind(this, i)} />
<input placeholder="Last Name" name="lastName" value={el.lastName ||''} onChange={this.handleChange.bind(this, i)} />
<input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
</div>
))
}
handleChange(i, e) {
const { name, value } = e.target;
let users = [...this.state.users];
users[i] = {...users[i], [name]: value};
this.setState({ users });
}
removeClick(i){
let users = [...this.state.users];
users.splice(i, 1);
this.setState({ users });
}
handleSubmit(event) {
alert('A name was submitted: ' + JSON.stringify(this.state.users));
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<input type='button' value='add more' onClick={this.addClick.bind(this)}/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(<App />, document.getElementById('container'));
<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="container" />
Try this
state = {
number: [""],
dataArr: []
}
render() {
return (
<div>
<div onClick={()=>this.setState(prevState => ({number: [...prevState.number, ""]}))}>Add More element</div>
{this.state.number.map((e, i)=> {
return (
<input value={this.state.number[i]} onChange={(data) => this.setState({dataArr: update(this.state.dataArr, {i: {$set: data}})})} />
)
})}
</div>
)
}
You will have to handle the data inside this.state.dataArr. For instance, this.state.dataAtt[0] will contain the starting value in input field before user presses "Add More Element" button and then when user presses the same button again data will be added in this.state.dataArr[1] and so on.
You will need react-addons-update lib.

How to initialize form data using async data

experts!
I make web service using react.
I want to make page to modify user info.
I can receive user data and set data to input's value.
But, react occur warning it.
Warning: A component is changing an uncontrolled input of type text to be controlled. ~~
I think. I did something wrong. when componentDidMount().
I want to know, how to initialize form data using async data.
Sorry about my poor english skill.
This code is part of my code.
export class UpdatedUser {
#observable private _name: string;
#observable private _phone: string;
#observable private _email: string;
// skip setters, getters.
}
interface MyPageComponentProps extends RouteComponentProps<{}> {
global?: GlobalService;
}
interface MyPageComponentState {
user: UpdatedUser;
}
#inject('global')
#observer
class MyPageComponent extends React.Component<MyPageComponentProps, MyPageComponentState> {
constructor(props: MyPageComponentProps) {
super(props);
this.state = {
user: new UpdatedUser(),
};
}
componentDidMount() {
if (this.props.global) {
userService.getUser(this.props.global.loginedInfo.idx + '').subscribe((res: any) => {
if (res.result === 'success') {
this.setState((prev: MyPageComponentState) => ({
user: update(prev.user, {$set: {
name: res.memberInfo.name,
phone: res.memberInfo.phone,
email: res.memberInfo.email,
} as UpdatedUser})
}));
} else {
alert(res.msg);
}
});
}
}
render() {
return (
<form className="user-info" onSubmit={this.updateUser}>
<h3 className="title">Modify User Info</h3>
<div className="form-group">
<label htmlFor="name" className="form-label">name</label>
<input type="text" id="name" className="form-control" value={this.state.user.name} onChange={this.onChangeInfo} />
</div>
<div className="form-group">
<label htmlFor="phone" className="form-label">phone</label>
<input type="text" id="phone" className="form-control" value={this.state.user.phone} onChange={this.onChangeInfo} />
</div>
<div className="form-group">
<label htmlFor="email" className="form-label">email</label>
<input type="text" id="email" className="form-control" value={this.state.user.email} onChange={this.onChangeInfo} />
</div>
<div className="btn-group">
<button type="submit" className="btn raised primary">수정</button>
<button className="btn raised secondary" onClick={() => this.props.history.goBack()}>취소</button>
</div>
</form>
);
}
export default MyPageComponent;
<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>
The warning you get is about controlled and uncontrolled components. Basically, you can work with elements such as input, textarea and select having the state in the internal state of a React component or keeping it in the DOM.
In your code, you are keeping the UpdatedUser info in the local state and propagating its data with value and onChange, so you are controlling the inputs. However, where is your onChange callback? It does not exist. Try adding that method to your class and it should work. Take a look at the documentation about controlled components
The alternative approach is having uncontrolled inputs, in which case, you need to pass a defaultValue prop to your inputs instead of value and onChange, like this:
<input
defaultValue="Bob"
type="text"
ref={(input) => this.input = input}
/>
If the updateUser method gets triggered, you can simply get the new values from your ref elements:
updateUser(event) {
event.preventDefault()
console.log('New value: ' + this.input.value)
this.props.onSubmit(this.input.value)
}

react js - how to populate values based on another DOM element value

I have 2 input boxes. Based on one input(1) box value (on key up event), I am populating another input(2) box value. Currently, I am using document.getElementByID option to retrieve element id to populate the values. Is it recommended in react js ? pls suggest. Like to find a better way to to this in react js.
handleChange(e) {
if(document.getElementById("getId").value.length > 4) {
console.log("a")
document.getElementById("getName").value =
document.getElementById("getId").value
}
}
render () {
return (
<div>
<Card>
<div>
<label>Id</label>
<input type="text" id="getId" onKeyUp= {this.handleChange.bind(this)}/>
<div>
<label>Name</label>
<input type="text" id="getName" readOnly/>
</div>
</div>
</Card>
</div>
);
You could store the value of the first input box in your component state and set the value of the second input box to the value from the state.
Then when the value of the input box changes, update the state, using the handleChange method, which in turn re-renders the component updating the second input box.
...
constructor(props) {
super(props)
this.state = {
inputText: ''
}
this.handleChange = this.handleChange.bind(this)
}
handleChange({ target }) {
if (target.value.length > 4) {
this.setState({
inputText: target.value
})
}
}
render () {
return (
<div>
<Card>
<div>
<label>Id</label>
<input type="text" id="getId" onKeyUp={ this.handleChange } />
<div>
<label>Name</label>
<input type="text" id="getName" value={ this.state.inputText } />
</div>
</div>
</Card>
</div>
)
}
...
You can handle issue with two ways.
First way is to use React refs and DOM.
So in code below I have done two things, I have added ref props to getName input and accessed it from handleChange method by this.refs.inputOfName', as well ase.targetas your DOM input without accessing again bygetElementById`.
handleChange(e) {
let value = e.target.value;
if (value.length > 4) {
this.refs.inputOfName.value = value
}
}
render() {
return (
<div>
<Card>
<div>
<label>Id</label>
<input type="text" id="getId" onKeyUp=
{this.handleChange.bind(this)} />
<div>
<label>Name</label>
<input type="text" id="getName" ref="inputOfName" readOnly />
</div>
</div>
</Card>
</div>
);
You can read more about refs here.
Second way is to use states.
I suggest to use states because it's more React "style" approach as well as one of the React advantages, so spend more time learning about state and lifecycle of React.
You can read more about states here.
handleChange(e) {
let value = e.target.value;
if (value.length > 4) {
this.setState({
name: value
});
}
}
render() {
return (
<div>
<Card>
<div>
<label>Id</label>
<input type="text" id="getId" onKeyUp=
{this.handleChange.bind(this)} />
<div>
<label>Name</label>
<input type="text" id="getName" value={this.state.name} readOnly />
</div>
</div>
</Card>
</div>
);
}
As already mention, It's not common to user getElementById within react component, think of what will happen if you will have 2 components rendered.
You can use component state to update your elements.
constructor(props, context) {
super(props, context);
// This will represent your component state to hold current input value.
this.state = { value: "" };
// Do handler bindings in one place and not inside the render
// function as it will create a new function reference on each render.
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({value: e.target.value});
}
render () {
return (
<div>
<Card>
<div>
<label>Id</label>
<input type="text" id="getId" onKeyUp={this.handleChange}/>
<div>
<label>Name</label>
<input type="text" value={this.state.value} readOnly/>
</div>
</div>
</Card>
</div>
);
}

Pass data from input field into variable without submitting

Trying to get my head around react. I want to take data from an input field and pass it into a variable (let) so I can then pass it back into the html in a separate string. Would this be 'two way binding'? Does anyone have a simple example of this working?
http://codepen.io/IanHazelton/pen/ygEomV?editors=0110
let name="{name from input}";
let age="{age from input}";
class App extends React.Component {
render() {
return (
<div className ="wrap">
<h1 className="string">What's your name?</h1>
<input type="text" id="name" />
<h1 className="string">How old are you?</h1>
<input type="text" id="age" />
<h1 className="string">Hi {name}! How are you today? You're {age} years old.</h1>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('Root'))
For that you need to use state in App component, Concept is whenever user provide any input in the fields, store those values in state variable, And whenever you make any change in state values, React will render whole component again, try this:
class App extends React.Component {
constructor(){
super();
this.state={name: '', age: ''}
}
render() {
return (
<div className ="wrap">
<h1 className="string">What's your name?</h1>
<input value={this.state.name} type="text" id="name" onChange={(e)=>{this.setState({name: e.target.value})}}/>
<h1 className="string">How old are you?</h1>
<input value={this.state.age} onChange={(e)=>{this.setState({age: e.target.value})}} type="text" id="age" />
<h1 className="string">Hi {this.state.name}! How are you today? You're {this.state.age} years old.</h1>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('Root'))
Check the working example: http://codepen.io/anon/pen/egKyvJ?editors=0110
You need to take advantage of the state of the component. I modified your pen to work with the name property, you can do the same to your other inputs:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleNameOnChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div className="wrap">
<h1 className="string">What's your name?</h1>
<input type="text" id="name" value={this.state.name} onChange={ (e) => this.handleNameOnChange(e) } />
<h1 className="string">How old are you?</h1>
<input type="text" id="age" />
<h1 className="string">Hi {this.state.name}! How are you today? You're {age} years old.</h1>
</div>
)
}
}
CodePen

Resources