React: Adding an input component via a button and updating its state - javascript

I am trying to create a button that will add a new input element to a page and then as I type display its changes.
However when I type into the input fields in <Input />, for some reason the state isn't changing. The input fields stay blank.
Out of curiosity, I removed the button that adds the <Input /> component and ran it with one <Input /> field on the page. When I type into one of the input fields, I can see my text.
It seems that when I add a new component to the page and try to change the state, something is off.
What am I doing wrong?
function Input(props) {
console.log(props)
return (
<div>
<div><input name="pitchName" value={props.currentValue.pitchName} placeholder="Pitch Name" onChange = {props.updateNewPitch}/></div>
<div><input name="shortCut" value={props.currentValue.shortcut} placeholder="Short cut" onChange = {props.updateNewPitch} /></div>
<div><input name="subject" value={props.currentValue.subject} placeholder="Subject" onChange = {props.updateNewPitch} /></div>
<div><textarea name="pitch" value={props.currentValue.pitch} onChange = {props.updateNewPitch}/></div>
<button type="submit" onClick={props.savePitch} >Add Pitch</button>
</div>
)
}
// function SavedPitches(props)
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
inputList: [],
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
},
savedPitches: []
};
this.onAddBtnClick = this.onAddBtnClick.bind(this)
this.savePitch = this.savePitch.bind(this)
this.updateNewPitch = this.updateNewPitch.bind(this)
}
updateNewPitch(e){
this.setState({addNewPitch: {...this.state.addNewPitch, [e.target.name]: e.target.value}})
}
onAddBtnClick(event){
const inputList = this.state.inputList;
this.setState({
inputList: inputList.concat(
<Input savePitch={this.savePitch}
currentValue = {this.state.addNewPitch}
updateNewPitch={this.updateNewPitch}
/>
)
})
}
render() {
return(
<div>
<button onClick={this.onAddBtnClick}>Add input</button>
<div></div>
{
this.state.inputList
}
</div>
)
}
}
ReactDOM.render(<Form />,document.getElementById('root'));

Reason is because you are storing the Input (UI element) in state variable, and that variable is not getting update only values are getting updated in a separate state variable addNewPitch.
Suggestion:
1- Storing UI elements in state variable is not a good idea, always store value in state and all the ui logic should be inside render function.
2- Use a state variable and toggle the Input (UI element) on the basis of that.
Check working solution (check the values on addNewPitch inside render it will get updated properly):
function Input(props) {
return (
<div>
<div><input name="pitchName" value={props.currentValue.pitchName} placeholder="Pitch Name" onChange = {props.updateNewPitch}/></div>
<div><input name="shortCut" value={props.currentValue.shortcut} placeholder="Short cut" onChange = {props.updateNewPitch} /></div>
<div><input name="subject" value={props.currentValue.subject} placeholder="Subject" onChange = {props.updateNewPitch} /></div>
<div><textarea name="pitch" value={props.currentValue.pitch} onChange = {props.updateNewPitch}/></div>
<button type="submit" onClick={props.savePitch} >Add Pitch</button>
</div>
)
}
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
inputList: [],
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
},
savedPitches: []
};
this.onAddBtnClick = this.onAddBtnClick.bind(this)
this.savePitch = this.savePitch.bind(this)
this.updateNewPitch = this.updateNewPitch.bind(this)
}
savePitch() {
}
updateNewPitch(e){
this.setState({addNewPitch: {...this.state.addNewPitch, [e.target.name]: e.target.value}})
}
onAddBtnClick(event){
const inputList = this.state.inputList;
this.setState({
show: true,
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
}
})
}
render() {
console.log('addNewPitch', this.state.addNewPitch);
return(
<div>
<button onClick={this.onAddBtnClick}>Add input</button>
<div></div>
{
this.state.show && <Input
savePitch={this.savePitch}
currentValue = {this.state.addNewPitch}
updateNewPitch={this.updateNewPitch}
/>
}
</div>
)
}
}
ReactDOM.render(<Form />,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'/>

Related

In React, state is not being updated properly when concatenating individual input state values

I have seven different input fields and updating the state with the entered value. After that, I am concatenating all the state values and updating the contractNum state but it is not being updated correctly. It is missing the first state (this.state.contact.sys) value. I am not sure how to get the right concatenated value. Any help is much appreciated.
export default class test extends Component {
state = {
contact: {
sys: '',
co: '',
lgr: '',
mgr: '',
sub: '',
serial: '',
num: ''
},
contractNum: ''
};
test = testValue => {
this.setState({
contractNum: testValue
});
};
handleChangeFor = propertyName => event => {
const { contact } = this.state;
const newContact = {
...contact,
[propertyName]: event.target.value
};
this.setState({ contact: newContact });
let newValue =
contact.sub +
contact.co +
contact.mgr +
contact.lgr +
contact.sub +
contact.serial +
contact.num;
this.test(newValue);
};
render() {
return (
<div className="wrapper">
<div className="container">
<form>
<input
type="text"
onChange={this.handleChangeFor('sys')}
value={this.state.contact.sys}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('co')}
value={this.state.contact.co}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('mgr')}
value={this.state.contact.mgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('lgr')}
value={this.state.contact.lgr}
maxLength={1}
/>
<input
type="text"
onChange={this.handleChangeFor('serial')}
value={this.state.contact.serial}
maxLength={6}
/>
<input
type="text"
onChange={this.handleChangeFor('num')}
value={this.state.contact.num}
maxLength={2}
/>
<input
type="text"
onChange={this.handleChangeFor('sub')}
value={this.state.contact.sub}
maxLength={1}
/>
</form>
</div>
</div>
);
}
}
You used contact.sub instead of contact.sys when setting newValue.

How to setState using RadioButton in ReactJs

I was trying to setState using the radiobuttons in ReactJs but I dont know why this is not working, maybe I have something missing in my code
var handleRadio = (event)=>{
this.setState({views:event.target.value})
}
This is my Function "handleRadio"
<input
type="radio"
checked={this.state.views === "streets-v11"}
onClick={handleRadio}
value="streets-v11"
/> Street Map <br />
<input
type="radio"
checked={this.state.views === 'outdoors-v11'}
onClick={handleRadio}
value="outdoors-v11"
/> Topo Map <br />
<input
type="radio"
checked={this.state.views === 'satellite-v9'}
onClick={handleRadio}
value="satellite-v9"
/> Satellite Map
and that's my code for buttons..
I was trying to call the function on radio button click and then according to that I was trying to set the value of state but this code is not working to setState in my project What's Wrong in this code ?
my state name is views initialized as an empty string....
I think you should use OnChange instead the OnClick prop
change onClick={handleRadio} to OnChange={handleRadio}
Here is a complete example
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
views: [
{ txt: 'streets-v11', value: false },
{ txt: 'outdoors-v11', value: true },
{ txt: 'satellite-v9', value: true }
]
};
this.handleRadio = this.handleRadio.bind(this);
}
handleRadio(e) {
let views = this.state.views.slice(0);
views = views.map((v) => {
if (v.txt === e.target.name) v.value = !v.value;
return v;
});
this.setState({ ...this.state, views });
}
render() {
return (
<h1>
{this.state.views.map((v) => {
return (
<div key={v.txt}>
<input
name={v.txt}
type="radio"
checked={v.value}
onClick={this.handleRadio}
/>
{v.txt}
</div>
);
})}
</h1>
);
}
}
render(<App />, document.querySelector('#root'));

front-end just remove only last item in array

I'm having a issue with React.
my parent component:
class RoomPrice extends React.Component {
constructor(props){
super(props)
this.state = {
room: this.props.room,
prices: []
};
this.handleDeletePrice = this.handleDeletePrice.bind(this);
}
handleDeletePrice(price_index){
let prices = this.state.prices;
prices.splice(price_index, 1);
this.setState({prices: prices});
}
listPrices(){
console.log(this.state.prices)
return this.state.prices.map((item, index) => {
return (
<AdditionalPrice
key={index}
price={item}
index={index}
handleDeletePrice={this.handleDeletePrice}
/>
)
});
}
renderBasePrice(){
return(
<div id="list_prices">
{ this.listPrices() }
</div>
)
}
render(){
return(
<div>
{this.renderBasePrice()}
</div>
)
}
}
my child component
class AdditionalPrice extends React.Component {
constructor(props){
super(props)
this.state = {
price: this.props.price
}
this.handleKeyChange = this.handleKeyChange.bind(this);
this.handleValueChange = this.handleValueChange.bind(this);
this.handleDeletePrice = this.handleDeletePrice.bind(this);
}
componentWillReceiveProps(nextProps){
this.setState({price: nextProps.price})
}
handleKeyChange(event){
let price = this.state.price;
price.key = event.target.value
this.setState({price: price})
}
handleValueChange(event){
let price = this.state.price;
price.value = event.target.value
this.setState({price: price})
}
handleDeletePrice(){
this.props.handleDeletePrice(this.props.index);
}
renderForm(){
let key = this.state.price.key;
let value = this.state.price.value;
return(
<div className="form-row">
<div className="col-5">
<input type="text" className="form-control" placeholder="Key" onChange={this.handleKeyChange} required/>
</div>
<div className="col-5">
<input type="number" className="form-control" placeholder="Value" onChange={this.handleValueChange} required/>
</div>
<div className="col-2">
<button className="btn btn-warning" type="button" onClick={this.handleDeletePrice}>
<i className="material-icons">delete_forever</i>
</button>
</div>
<input type="hidden" className="form-control" name={"base_price["+key+"]"} value={value} />
</div>
)
}
render() {
return(
<div>
{this.renderForm()}
</div>
)
}
}
i try to delete a item which was get in children, but it always removes last element instead. I thought it have some problem with index
I want to delete the particular element, it always deletes the last element from the render list array.
please help me to sort this problem
Try doing this instead.
handleAddNewPrice(){
const { prices } = this.state;
let new_price = {"key": "", "value": ""}
this.setState({ prices: [...prices, new_price] })
}
Edit
and also this:
handleDeletePrice(price_index){
let prices = [...this.state.prices]; //make a seperate copy of state.
prices.splice(price_index, 1);
this.setState({prices: prices});
}
Problem is in your props. The props.index is received once, so if you want to the delete function worked you need use props.index as a state like price. This is sample codes you need to change in the AdditionalPrice Component:
this.state = {
price: this.props.price,
index: this.props.index
}
componentWillReceiveProps(nextProps){
this.setState({
price: nextProps.price,
index: nextProps.index
})
}
handleDeletePrice(){
this.props.handleDeletePrice(this.state.index);
}
i found the problem
my field in child component haven't set the value. see below
<input type="text" className="form-control" placeholder="Key" value={key} onChange={this.handleKeyChange} required/>
thanks all

React JS : Unable to capture the edited value

I am new to React JS. I am trying to capture the edited value from formcontrol in handle change and assign it to a state object and later retrieve assign it to a variable to pass it as a part of the query string to an API for updating the field, handle change is the function called from form-control to assign the edited value to a state object. I am unable to do so; please provide your inputs. Your help is much appreciated. I would like to follow the same for other fields too.
import React, { PropTypes, Component } from 'react';
import {
Panel,
Button,
PageHeader,
ControlLabel,
FormControl,
Pagination,
Form,
Accordion,
Col,
Radio,
FormGroup
} from 'react-bootstrap';
import StatWidget from '../../src/components/Widget';
import CRUDTable,
{
Fields,
Field,
CreateForm,
UpdateForm,
DeleteForm,
} from 'react-crud-table';
const DescriptionRenderer = ({ field }) => <textarea {...field} />;
const styles = {
container: { margin: 'auto', width: 'fit-content' },
};
class displayDetails extends Component {
constructor (props) {
super(props);
this.state = {
updatecustomer: [],
delcustomer: [],
field1 = '',
};
this.handleUpdate = this.handleUpdate.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.handleCreate = this.handleCreate.bind(this);
this.handleChange = this.handleChange.bind(this);
};
handleChange (e) {
this.state.field1 = e.target.value; // This isn't happening
}
render() {
firstName=this.props.respData.FIELD1;
lastName=this.props.respData.FIELD2;
return (
<div>
<br></br>
<div className="col-lg-3 col-md-6">
<StatWidget
style="panel-primary"
icon="fa fa-dollar fa-5x"
count={this.props.respData.FIELD1}
headerText="FIELD1"
linkTo=""
/>
</div>
<div className="col-lg-3 col-md-6">
<StatWidget
style="panel-green"
icon="fa fa-phone fa-5x"
count={this.props.respData.FIELD2}
headerText="FIELD2"
linkTo=""
/>
</div>
<div className="col-lg-3 col-md-6">
<StatWidget
style="panel-yellow"
icon="fa fa-home fa-5x"
count={this.props.respData.FIELD3}
headerText="FIELD3"
linkTo=""
/>
</div>
<div className="col-lg-3 col-md-6">
<StatWidget
style="panel-red"
icon="fa fa-shopping-bag fa-5x"
count={this.props.respData.FIELD4}
headerText="FIELD4"
linkTo=""
/>
</div>
<div>
<tr>
<td><h1>{this.props.respData.FIELD1} {this.props.respData.FIELD2}</h1></td>
</tr>
</div>
<div>
<table>
<tr>
<td>
<FormGroup style={spacing}>
<ControlLabel>FIELD 1</ControlLabel>
<FormControl onChange={((e) => this.handleChange(this.props.respData.FIELD1))}
value={this.props.respData.FIELD1}
id="field1" name="field1"
type="text"
placeholder="Enter Text"
/>
</FormGroup>
</td>
</tr>
<br></br>
</table>
</div>
<br></br>
</div>
)
}
}
export default displayDetails;
Modifying the state directly will not work. The only place you should be setting it directly, is to assign the initial state in your constructor. Everywhere else, you need to use this.setState, to notify your component to recheck the state for a possible rerender:
handleChange(e){
this.setState({
field1: e.target.value
});
}
I believe you are in need of the setState() lifecycle method.
handleChange(e){
this.state.field1 = e.target.value; // This isn't happening
this.setState({
field1: e.target.value
}); // this will update state and trigger rerender
}
class displayDetails extends Component {
constructor(props) {
super(props);
this.state = {
state1: [],
state2: [],
state3: '',
state4: '',
state5: '',
state6: '',
state7: '',
state8: '',
state9: '',
state10: '',
};
this.handleState1change = this.handleState1change.bind(this);
this.handleState2change = this.handleState2change.bind(this);
this.handleState3change = this.handleState3change.bind(this);
this.handleState4change = this.handleState4change.bind(this);
field1=this.props.respData.FIELD1;
this.setState({state3:field1});
};
handlestate1change (e) {
console.log('Inside handle state1 Change',e);
this.setState({state3:e.target.value});
console.log('state1 in handlestate1change',this.state.state3);
}
componentWillReceiveProps(){
this.setState({state3:field1});
console.log('newfirstName in Component will mount',this.state.state3);
}
render() {
return(
<td>
<FormGroup style={spacing}>
<ControlLabel>Field1</ControlLabel>
<FormControl onChange={this.handleState1change}
value={this.state.state3}
id="field1" name="field1"
type="text"
placeholder="Enter Text"
/>
</FormGroup>
</td>
);
}
}
}

How do you display an array of objects in react

I have this code which takes three input fields and i store them in an object when i click submit and displays them. But it only displays the current value and not all the value which I've entered into the array of objects.
class Emp1 extends React.Component {
constructor(props) {
super(props);
this.state = {
obj: {name :'', email: '', age: '', phone: ''},
items: []
}
}
save(e) {
e.preventDefault();
var obj1 = this.state.obj;
obj1.name = ReactDOM.findDOMNode(this.refs.field.refs.name1).value;
obj1.email = ReactDOM.findDOMNode(this.refs.field.refs.email1).value;
obj1.age = ReactDOM.findDOMNode(this.refs.field.refs.age1).value;
obj1.phone = ReactDOM.findDOMNode(this.refs.field.refs.phone1).value;
var arr = [];
arr.push(obj1)
this.setState({
obj: obj1,
items: arr
})
}
render() {
return(
<div>
<Fields ref="field" save={this.save.bind(this)}/>
<Display items={this.state.obj} />
</div>
)
}
}
class Fields extends React.Component {
render() {
return(
<div>
<label>Name</label>
<input type="text" ref="name1" /><br/>
<label>Email</label>
<input type="email" ref="email1" /><br/>
<label>Age</label>
<input type="text" ref="age1" /><br/>
<label>Phone Number</label>
<input type="text" ref="phone1" /><br/>
<button type="submit" onClick={ this.props.save }>Submit</button>
</div>
)
}
}
class Display extends React.Component {
render(){
return (
<div>
<ul>
<li><b>Name:</b> {this.props.items.name}</li>
<li><b>Email:</b> {this.props.items.email}</li>
<li><b>Age:</b> {this.props.items.age}</li>
<li><b>Phone:</b> {this.props.items.phone}</li>
</ul>
</div>
)
}
}
export default Emp1;
If what you want is to display all the form's values that have been sent, you can try what I did.
I changed your state structure, your "save" method and the render method of the class "Emp1" to display every single submitted forms.
class Emp1 extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
}
}
save(e) {
e.preventDefault();
var obj1 = {};
obj1.name = ReactDOM.findDOMNode(this.refs.field.refs.name1).value;
obj1.email = ReactDOM.findDOMNode(this.refs.field.refs.email1).value;
obj1.age = ReactDOM.findDOMNode(this.refs.field.refs.age1).value;
obj1.phone = ReactDOM.findDOMNode(this.refs.field.refs.phone1).value;
var newArr = this.state.items.slice();
newArr.push(obj1)
this.setState({
items: newArr
})
}
render() {
let displayItems = this.state.items.map((thisForm) => (
<Display items={thisForm}/>
))
return(
<div>
<Fields ref="field" save={this.save.bind(this)}/>
{displayItems}
</div>
)
}
}
class Fields extends React.Component {
render() {
return(
<div>
<label>Name</label>
<input type="text" ref="name1" /><br/>
<label>Email</label>
<input type="email" ref="email1" /><br/>
<label>Age</label>
<input type="text" ref="age1" /><br/>
<label>Phone Number</label>
<input type="text" ref="phone1" /><br/>
<button type="submit" onClick={ this.props.save }>Submit</button>
</div>
)
}
}
class Display extends React.Component {
render(){
return (
<div>
<ul>
<li><b>Name:</b> {this.props.items.name}</li>
<li><b>Email:</b> {this.props.items.email}</li>
<li><b>Age:</b> {this.props.items.age}</li>
<li><b>Phone:</b> {this.props.items.phone}</li>
</ul>
</div>
)
}
}
When you write this
var arr = [];
arr.push(obj1)
this.setState({
obj: obj1,
items: arr
})
You create a new empty array, then reset the state.items with this new array that contains only 1 obj
Try like this
//var arr = [];
//arr.push(obj1)
this.setState({
obj: obj1,
items: [...this.state.items, obj1] // this will create new array from old with new item
})
to print all values in the array.. you need to iterate over the items array..
this is what is missing in the code..
put the below modification in the render block of 1st component(Emp1)
render() {
let displayElems = this.state.items.map((d) => {
return <Display items={d} />;
});
return(
<div>
<Fields ref="field" save={this.save.bind(this)}/>
{displayElems}
</div>
)
}
hope this will work..
I just modified your code for simplicity.
you should make some naming change.. e.g. <Display items={d} />
here intstead of items - item should be used :-)

Categories