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
Related
I'm learning React, but not strong yet. I use a component with form, that recieve data from express backend using axios.
There is no problems with get the correct data and render it in the form inputs, but i can't cope how to change input values and post using axios. I read something about handleChange() and other staff, but it's too hard yet.
The JSON looks like this:
{
"data": [
{
"_id": "5d28a6fcec97b111c2f5867d",
"phone": "+1 (111) 111 11 11",
"email": "shutruk#gmail.com",
"title": "khkjhkjhkj",
"longTitle": "lkjlkjlkjlk",
"introTitle": "Shutruk",
"introLongTitle": "Shutruk-Nahhunte",
"videoLink": "khkjhkjhkj",
"introText": "lkjlkjlkjlk",
"__v": 0
}
]
}
Here is the component:
import React, { Component } from 'react';
import axios from 'axios';
class Misc extends Component {
state = {
data: [],
loading: true,
error: false,
}
componentDidMount() {
axios.get('http://localhost:5555/data')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
console.log(data);
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
})
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occured </p>
</div>
)
}
return (
<form action="">
<h2 className="center" >Change data</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Phone:</h5>
<input type="text" value={ this.state.data[0].phone } />
<h5>Email:</h5>
<input type="text" value={ this.state.data[0].email } />
<h5>Title:</h5>
<input type="text" value={ this.state.data[0].title }/>
<h5>Longtitle:</h5>
<input type="text" value={ this.state.data[0].longTitle }/>
<h2 className="center" >Intro:</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Title:</h5>
<input type="text" value={ this.state.data[0].introTitle } />
<h5>Longtitle:</h5>
<input type="text" value={ this.state.data[0].introLongTitle } />
<h5>Link to video:</h5>
<input type="text" value={ this.state.data[0].videoLink } />
<h5>Text:</h5>
<textarea name="" id="" cols="30" rows="10" value={ this.state.data[0].introText }></textarea>
<button type="submit" className="btn-large waves-effect waves-light xbutton">Save</button>
</form>
);
}
}
export default Misc;
Many many thanks for any help!))
One of the major differences between React and Vue/Angular is the focus on one-way databinding. While you supply a value through state to the input components, they cannot themselves update your state when changes happen.
From the React Docs on Forms:
In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().
We call this controlled components. The component state is the single source of truth, and state can only explicitly be changed by setState. You would typically do this in -- as you said -- a change handler:
class MyForm extends React.Component {
state = {
name: ''
};
handleChange = (evt) => {
// evt is a change event
this.setState({
name: evt.target.value,
});
}
render() {
return (
<input type="text" value={this.state.name} onChange={this.handleChange} />
);
}
}
ReactDOM.render(<MyForm />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This can of course be cumbersome, especially with many different types of inputs. However it is easy to reason about where changes come from.
You can also use uncontrolled components, where you often supply your inputs with default values. These inputs can change their value, but it will not be reflected in your state. You can use refs to read their value when needed:
class MyForm extends React.Component {
state = {
name: 'Foo'
};
constructor() {
super();
this.inputRef = React.createRef();
}
handleSubmit = (evt) => {
evt.preventDefault();
const name = this.inputRef.current.value;
console.log({ name });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input ref={this.inputRef} type="text" defaultValue={this.state.name} />
<button type="submit">Submit</button>
</form>
);
}
}
ReactDOM.render(<MyForm />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Here is how you can do it.
handleChange(key, value){
const data = this.state.data;
data[0][key] = value;
this.setState({data});
}
handleSubmit(){
// You can make post call here.
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occured </p>
</div>
)
}
return (
<form action="" onSubmit={this.handleSubmit.bind(this)}>
<h2 className="center" >Change data</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Phone:</h5>
<input type="text" value={ this.state.data[0].phone } onChange={this.handleChange.bind(this, "phone")}/>//Here you have to pass key in this case it's phone
<h5>Email:</h5>
<input type="text" value={ this.state.data[0].email } />
<h5>Title:</h5>
<input type="text" value={ this.state.data[0].title }/>
<h5>Longtitle:</h5>
<input type="text" value={ this.state.data[0].longTitle }/>
<h2 className="center" >Intro:</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Title:</h5>
<input type="text" value={ this.state.data[0].introTitle } />
<h5>Longtitle:</h5>
<input type="text" value={ this.state.data[0].introLongTitle } />
<h5>Link to video:</h5>
<input type="text" value={ this.state.data[0].videoLink } />
<h5>Text:</h5>
<textarea name="" id="" cols="30" rows="10" value={ this.state.data[0].introText }></textarea>
<button type="submit" className="btn-large waves-effect waves-light xbutton">Save</button>
</form>
);
}
}
You can more read it on form control https://reactjs.org/docs/forms.html
I have a component that accepts another component with 3 fields, and I want that I can add new entries to different fields
now I can add only the entry in firstname and I do not know how to make that for for the lastname and telegrams
if I just copy them, then the values are accepted only from one field
import React, { Component } from 'react';
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
dataItems: []
}
}
addItem(value) {
let newListItems = this.state.dataItems.slice();
newListItems.push(value);
this.setState({
dataItems : newListItems
});
}
render() {
return (
<div>
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item} lastname={item2} telegram={item3}/>
);
}, this)}
<AddItem addItem={this.addItem.bind(this)} />
</div>
)
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className='form__row'>
<p className='form__input' > firstname: {this.props.firstname} </p>
<p className='form__input'> lastname: {this.props.lastname} </p>
<p className='form__input'> telegram: {this.props.telegram} </p>
</div>;
}
}
class AddItem extends React.Component{
handleClick(){
this.props.addItem(this.item.value);
}
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
}
export default Table;
There are a couple important things you got wrong. I'll break them down for you:
In your AddItem component, you are using an uncontrolled component, but overriding the same property this.item over and over again whenever setting the values for each form field. In this case, because it is the last one, telegrams value is the final one remaining. So it is the only one being set. It would be the same thing as trying to set three different value for the same variable.
item.value = 1
item.value = 2
item.value = 3
If you used this code snippet, obviously the only value of item will be 3. So we first fix this component by associating different properties to each form:
// AddItem component's render function
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item2=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item3=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
On another note, I highly recommend actually giving your three items meaningful names. So I would switch them to firstName, lastName and telegram specifically.
Your handleClick also suffers from the same problem: You were passing the same this.item.value, meaning you were just passing your last set field. For you to pass all three of them while keeping the function in it's same format, you need to create an object with all three of your item.value. With that change, it should now look like this:
handleClick(){
const value = {item: this.item.value, item2: this.item2.value, item3: this.item3.value}
this.props.addItem(value);
}
Finally, in your Table component, some changes are needed in its render function. It looks like you are confusing the item you named in your map function with item you passed in. Again, you should probably find a more meaningly names for your variables so that doesn't happen.
The item in the map function, is each item of the array you are mapping through. In this case, it would be just the object with all three of your fields. You actually want the item fields from the item of the map function.
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item.item} lastname={item.item2} telegram={item.item3}/>
);
}, this)}
Here is a working version of your app:
class Table extends React.Component{
constructor (props) {
super(props);
this.state = {
data: [],
dataItems: []
}
}
addItem(value) {
let newListItems = this.state.dataItems.slice();
console.warn(newListItems)
newListItems.push(value);
this.setState({
dataItems : newListItems
});
}
render() {
return (
<div>
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item.item} lastname={item.item2} telegram={item.item3}/>
);
}, this)}
<AddItem addItem={this.addItem.bind(this)} />
</div>
)
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className='form__row'>
<p className='form__input' > firstname: {this.props.firstname} </p>
<p className='form__input'> lastname: {this.props.lastname} </p>
<p className='form__input'> telegram: {this.props.telegram} </p>
</div>;
}
}
class AddItem extends React.Component{
handleClick(){
const value = {item: this.item.value, item2: this.item2.value, item3: this.item3.value}
this.props.addItem(value);
}
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item2=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item3=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
}
ReactDOM.render(
<Table />,
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>
I am new to ReactJS and trying to build a sample application
I am unable to get enter the value in textbox and unable to get the value in alert. Am I doing something wrong. I have tried the example given on React.Js Form, but not working.
import React, { Component } from "react";
class AddNewStudent extends Component {
constructor(props) {
super(props);
this.state = {value:''};
this.OnSubmit = this.OnSubmit.bind(this);
}
OnSubmit(event){
alert('name value is : '+this.state.value);
event.preventDefault();
}
render(){
return(
<div>
<form onSubmit={this.OnSubmit}>
<fieldset className="Student-Form" id={this.props.id}>
<legend className="Legend">Add mew student</legend>
<div>
<div className="Block">
<div className="Float-Left">
<label>
<span>Name : </span>
<input type="text" value={this.state.value} />
</label>
</div>
</div>
<div className="clearFix" />
<div className="Block">
<div className="Float-None">
<label>
<input type="submit" value="Save" />
</label>
</div>
</div>
</div>
</fieldset>
</form>
</div>
);
}
}
export default AddNewStudent;
please change :
<input type="text" value={this.state.value} />
to:
<input type="text" value={this.state.value} onChange = {this.onChange}/>
In your class, add the 'onChange' function:
onChange(e){
this.setState({
value: e.target.value
})
}
and in your constructor:
this.onChange = this.onChange.bind(this)
Edit 1:
please check my codepen here
Edit 2:
If you have too many inputs, you can use a function like this:(it's just a rough example, in real world I'll create a new component to handle this together)
//in your constructor
this.state = {}
//in class 'controlled component way'
createInput(num){
let inputArray = []
for(let i = 0; i < num; i ++){
inputArray.push(<input key = {i} name = {i} onChange = {this.onChange} value = {this.state[i] || ''} />)
}
return inputArray
}
onChange(e){
let key = e.target.name
let newState = {}
newState[key] = e.target.value
this.setState(newState)
}
// in your render function
return <div>
{this.createInput(100)}
</div>
check my codepen here for this example
and of course you can also do this using uncontrolled component.
controlled component and uncontrolled
You can either bind onChange event to your input or setState when submiting the value , you cant change the state like that
Change your code to something like this
import React, { Component } from "react";
class AddNewStudent extends Component {
constructor(props) {
super(props);
this.state = {value:''};
this.OnSubmit = this.OnSubmit.bind(this);
}
OnSubmit(event){
this.setState({value: this.refs.infield.value},()=>{
alert('name value is : '+this.state.value);
})
event.preventDefault();
}
render(){
return(
<div>
<form onSubmit={this.OnSubmit}>
<fieldset className="Student-Form" id={this.props.id}>
<legend className="Legend">Add mew student</legend>
<div>
<div className="Block">
<div className="Float-Left">
<label>
<span>Name : </span>
<input type="text" ref = "infield"/>
</label>
</div>
</div>
<div className="clearFix" />
<div className="Block">
<div className="Float-None">
<label>
<input type="submit" value="Save" />
</label>
</div>
</div>
</div>
</fieldset>
</form>
</div>
);
}
}
export default AddNewStudent;
Hi to get the value add a name to the input and an onChange function to set the value to the state and then do what evere you want in onsubmit function
I cant write you a working code now because I'm using my phone but my this Will help
onfieldchange(e) {
this.setState({[e.target.name]: e.target.value});
}
onaSubmit(){
//do your code here
}
....
....
<input type="text" name="firstName" value={this.state.firstName} />
Hope this help you
There are two ways to handle form elements in reactjs:
Controlled and uncontrolled components(using refs).In controlled component approach ,you have to add the state of the input field to the input field value and onChange event to it, which is the one you are trying to do.Here is the working code.
import React, { Component } from "react";
class AddNewStudent extends Component {
constructor(props) {
super(props);
this.state = {
firstname:''
};
this.OnSubmit = this.OnSubmit.bind(this);
this.OnChange = this.OnChange.bind(this);
}
OnSubmit(event){
alert('name value is : '+this.state.firstname);
event.preventDefault();
}
OnChange(){
this.setState({
[e.target.name] : [e.target.value]
});
}
render(){
return(
<div>
<form onSubmit={this.OnSubmit}>
<fieldset className="Student-Form" id={this.props.id}>
<legend className="Legend">Add mew student</legend>
<div>
<div className="Block">
<div className="Float-Left">
<label>
<span>Name : </span>
<input type="text"
name="firstname"
value={this.state.firstname}
OnChange={this.OnChange}
/>
</label>
</div>
</div>
<div className="clearFix" />
<div className="Block">
<div className="Float-None">
<label>
<input type="submit" value="Save" />
</label>
</div>
</div>
</div>
</fieldset>
</form>
</div>
);
}
}
export default AddNewStudent;
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>
);
}
Iam doing an app in react.js in which iam passing a component to higher order component.Now after form submission in PersonalInformation component the form needs to reset which is not happening.It is not happening by refs.
class PersonalInformation extends Component{
render(){
return(
<div>
<h3>Personal Information</h3>
<Form onSubmit={this.props.handleSubmit} ref="form" name="form">
<fieldset disabled={this.props.disabled}>
<Input type="text" name="firstName" title="FirstName" value=""/><br/>
<Input type="text" name="lastName" title="LastName" value=""/><br/>
<Input type="text" name="fatherName" title="Fathers's Name" value=""/><br/>
<Input type="text" name="motherName" title="Mother's Name" value=""/><br/>
</fieldset>
<button className="btn btn-default">{this.props.buttonName}</button>
</Form>
</div>
)
}
}
Now the Higher Order component is:
var MyHoc = function(AbstractComponent){
return class extends React.Component {
constructor(props){
super(props);
this.state={
buttonName:'Edit',
disabled:true,
anotherForm:false
}
this.handleSubmit=this.handleSubmit.bind(this);
this.newForm=this.newForm.bind(this);
}
handleSubmit(data){
this.setState({
buttonName:'Save',
disabled:false
})
if(!this.state.disabled){
console.log(data);
this.refs.form.reset();
}
}
newForm(){
this.setState({
anotherForm:true
})
}
render() {
return <AbstractComponent {...this.state} handleSubmit={this.handleSubmit}
newForm={this.newForm} />;
}
}
}
export default MyHoc(PersonalInformation);
You cannot access refs from different components. this.refs refers to the ref defined in that particular component.
You can pass a callback method in handleSubmit function and handlesubmit will call that method when it want to reset form. Something like this
handleSubmit(data, resetFormCallback){
this.setState({
buttonName:'Save',
disabled:false
})
if(!this.state.disabled){
console.log(data);
resetFormCallback();
}
}
And then in your child component you pass a callback method while calling handleSubmit
class PersonalInformation extends Component{
resetForm = () => {
this.refs.form.reset();
}
render(){
return(
<div>
<h3>Personal Information</h3>
<Form onSubmit={(data) => {this.props.handleSubmit(data, this.resetForm)}} ref="form" name="form">
<fieldset disabled={this.props.disabled}>
<Input type="text" name="firstName" title="FirstName" value=""/><br/>
<Input type="text" name="lastName" title="LastName" value=""/><br/>
<Input type="text" name="fatherName" title="Fathers's Name" value=""/><br/>
<Input type="text" name="motherName" title="Mother's Name" value=""/><br/>
</fieldset>
<button className="btn btn-default">{this.props.buttonName}</button>
</Form>
</div>
)
}
}
this refers to the component in hand. You are looking for a ref on another component, so that's not going to work.
At this line: this.refs.form.reset(); instead set some state like resetForm:true, which then gets passed down to your child component. Then you can reset your form from the component it lives in based on the state that gets passed to it via props.