How do you display an array of objects in react - javascript

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 :-)

Related

How to save input entered by the user in a state which is an array in React

The whole idea is to take users input in a form and display their input in a JSON object. In state I have an array and inside it another array.
My Form.js looks like this,
state= {
groups:[{
typeA:[{}],
typeB:[{}]
}],
credentials: false
};
change = e =>{
this.setState({[e.target.name]: e.target.value})
};
handleSubmit(event) {
event.preventDefault();
this.setState({
credentials: true
});
}
render(){
return(
<div class="classform">
<form >
<label>
Field1:
<br/>
<input type="text"
name="typeA"
placeholder="Type A"
//store it as the first element of the type A
value={this.state.groups.typeA[0]}
onChange={this.change.bind(this)}
/>
//other fields with the same code
Subsequently, Field2 will be stored as the second element of type A
Then, Field3 and Field4 will be stored as 2 of type B array
I expect the code to give me an output like :
"typeA": ["field1 value", "field2 value"],
"typeB": ["field3 value", "field4 value"]}
I'm a beginner with React and I'm not able to store the field values in the state which is an array.
For the sake of simplicity, I will recommend below solution,
Instead of having a nested array in the state, you can manage the input values in the different state variables and at the time of submitting, change them to the output you want.
state = {
field1: '',
field2: '',
field3: '',
field4: '',
}
change = e => {
this.setState({[e.target.name]: e.target.value})
};
handleSubmit(event) {
event.preventDefault();
const { field1, field2, field3, field4 } = this.state;
// This is your output
const output = [{typeA: [field1, field2], typeB: [field2, field3]}];
this.setState({
credentials: true
});
}
render(){
return(
<div class="classform">
<form >
<label>
Field1:
<br/>
<input type="text"
name="field1"
placeholder="Type A"
value={this.state.field1}
onChange={this.change}
/>
</label>
<label>
Field2:
<br/>
<input type="text"
name="field2"
placeholder="Type A"
value={this.state.field2}
onChange={this.change}
/>
</label>
try this:
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
groups: [
{
typeA: [{}],
typeB: [{}]
}
],
credentials: false
};
}
change = (e, key) => {
let { groups } = this.state;
let myVal = groups[0][e.target.name];
// or if you want to add value in an object likr [{value: 'abcd'}]
myVal[0][key] = e.target.value;
groups[0][e.target.name] = myVal;
console.log("TCL: App -> groups", groups);
this.setState({ groups });
};
render() {
return (
<div>
<input
type="text"
name="typeA"
placeholder="Type A"
value={this.state.groups[0].typeA[0].value2}
onChange={e => this.change(e, "value2")}
/>
<input
type="text"
name="typeA"
placeholder="Type A2"
value={this.state.groups[0].typeA[0].value}
onChange={e => this.change(e, "value")}
/>
<br />
<input
type="text"
name="typeB"
placeholder="Type b"
value={this.state.groups[0].typeB[0].value}
onChange={e => this.change(e, "value")}
/>
</div>
);
}
}
Give each input a custom attribute, for example data-group="typeA". In your on change function get that value and add the values to the correct array.
<input
type="text"
name="col2"
placeholder="Type A"
data-group="typeA" // add custom attribute typeA | typeB etc.
onChange={e => this.change(e)}
/>
In your change handle get the custom attribute, and use it to add the value to the correct array.
change = e => {
// create a copy of this.state.groups
const copyGroups = JSON.parse(JSON.stringify(this.state.groups));
// get data-group value
const group = event.target.dataset.group;
if (!copyGroups[0][group]) {
copyGroups[0][group] = []; // add type if it doesn't exists
}
const groups = copyGroups[0][group];
const index = this.findFieldIndex(groups, e.target.name);
if (index < 0) {
// if input doesn't exists add to the array
copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
} else {
// else update the value
copyGroups[0][group][index][e.target.name] = e.target.value;
}
this.setState({ groups: copyGroups });
};
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function formatState(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => match);
}
const Credentials = ({ value }) => {
return <pre>{formatState(value)}</pre>;
};
class App extends React.Component {
state = {
groups: [
{
typeA: [],
typeB: []
}
],
credentials: false
};
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
// get the current input index in the array typeA | typeB
findFieldIndex = (array, name) => {
return array.findIndex(item => item[name] !== undefined);
};
change = e => {
// create a copy of this.state.groups
const copyGroups = JSON.parse(JSON.stringify(this.state.groups));
// get data-group value
const group = event.target.dataset.group;
if (!copyGroups[0][group]) {
copyGroups[0][group] = []; // add new type
}
const groups = copyGroups[0][group];
const index = this.findFieldIndex(groups, e.target.name);
if (index < 0) {
// if input doesn't exists add to the array
copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
} else {
// update the value
copyGroups[0][group][index][e.target.name] = e.target.value;
}
this.setState({ groups: copyGroups });
};
removeKey = (key) => {
const temp = {...this.state};
delete temp[key];
return temp;
}
render() {
return (
<div>
<input
type="text"
name="col1"
placeholder="Type A"
data-group="typeA"
onChange={e => this.change(e)}
/>
<input
type="text"
name="col2"
placeholder="Type A"
data-group="typeA"
onChange={e => this.change(e)}
/>
<input
type="text"
name="col2"
placeholder="Type B"
data-group="typeB"
onChange={e => this.change(e)}
/>
<input
type="text"
name="typec"
placeholder="Type C | New Type"
data-group="typeC"
onChange={e => this.change(e)}
/>
<input
type="text"
name="typed"
placeholder="Type D | New Type"
data-group="typeD"
onChange={e => this.change(e)}
/>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.removeKey('credentials'), undefined, 2)} />
)}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>

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

Performing state and array manipulation more than once by passing props only once

I am new to React and this thing is confusing me a lot. I have root component that has an array and I am passing functions ADD and DELETE as props to the child component ui_forms. In the child component, I am taking input parameters and pushing or deleting from array depending on the button pressed. However, I cannot seem to perform push/delete operation more than once because I guess I am injecting the child component only once in the root component.
Is there by any chance a way to perform push or delete as many times a user wants by sending props only once?
Thank you
App.js
import FormsMap from './form_map';
class App extends Component {
state = {
maps : [
{'regno' : 'Karan', 'id' : 1},
{regno : 'Sahil', 'id' : 2},
{'regno' : 'Rahul', id : 4},
{regno : 'Mohit', id : 5},
{regno : 'Kartik', id : 3}
]
};
function_as_props(list1) {
console.log(this.state.maps);
let ninja = [...this.state.maps];
console.log(ninja);
ninja.push({"regno" : list1, "id" : Math.random()*10});
this.setState({
maps : ninja
});
console.log(ninja);
function_as_props1(name) {
let t = this.state.maps.indexOf(name);
let x = this.state.maps.splice(t,1);
console.log(x);
this.setState({
maps : x
});
console.log(this.state.maps);
}
}
render() {
const p = this.state.maps.map(list => {
return(
<div key={list.id}> {list.regno} </div>
);
})
return(
<FormsMap transpose = {this.function_as_props.bind(this)} traverse ={this.function_as_props1.bind(this)} /> <br />
);
}
}
export default app;
form_map.js
import React, { Component } from 'react';
class FormsMap extends Component {
state = {
name : null,
age : null,
hobby : null
};
changes(e) {
this.setState({
[e.target.id] : e.target.value
});
}
handle = (e) => {
e.preventDefault();
console.log(this.state);
this.props.transpose(this.state.name);
}
dels = (e) => {
this.setState({
[e.target.id] : e.target.value
});
}
del_button(e) {
e.preventDefault();
//console.log(this.state.name);
this.props.traverse(this.state.name);
}
render() {
return(
<React.Fragment>
<form onSubmit={this.handle}> {/* After entering all info, Press Enter*/}
<label htmlFor="labels"> Name : </label>
<input type="text" id="name" placeholder="Your name goes here..." onChange={this.changes.bind(this)} />
<label htmlFor="labels"> Age : </label>
<input type="text" id="age" placeholder="Your age goes here..." onChange={this.changes.bind(this)} />
<label htmlFor="labels"> Hobby : </label>
<input type="text" id="hobby" placeholder="Your hobby goes here..." onChange={this.changes.bind(this)} /> <br /><br />
<input type="submit" value="SUBMIT" /><br /><br />
</form>
<input type="text" id="name" placeholder="Enter name to delete..." onChange={this.dels} /> <button onClick={this.del_button.bind(this)}> DELETE </button>
</React.Fragment>
);
}
}
export default FormsMap;
Try this
App.js
import React, { Component } from "react";
import FormsMap from "./components/FormsMap";
class App extends Component {
constructor(props) {
super(props);
this.state = {
maps: [
{ regno: "Karan", id: 1 },
{ regno: "Sahil", id: 2 },
{ regno: "Rahul", id: 4 },
{ regno: "Mohit", id: 5 },
{ regno: "Kartik", id: 3 }
]
};
}
function_as_props(list1) {
let ninja = this.state.maps.concat({
regno: list1,
id: Math.random() * 10
});
this.setState({ maps: ninja });
}
function_as_props1(name) {
let x = this.state.maps.filter(
list => list.regno.toLocaleLowerCase() !== name.toLocaleLowerCase()
);
this.setState({
maps: x
});
}
render() {
return (
<React.Fragment>
{this.state.maps.map(list => <div key={list.id}>{list.regno}</div>)}
<FormsMap
transpose={this.function_as_props.bind(this)}
traverse={this.function_as_props1.bind(this)}
/>
</React.Fragment>
);
}
}
export default App;
FormsMap.js
import React, { Component } from "react";
class FormsMap extends Component {
state = {
name: null,
age: null,
hobby: null
};
changes(e) {
this.setState({
[e.target.id]: e.target.value
});
}
handle = e => {
e.preventDefault();
this.props.transpose(this.state.name);
};
dels = e => {
this.setState({
[e.target.id]: e.target.value
});
};
del_button(e) {
e.preventDefault();
this.props.traverse(this.state.name);
}
render() {
return (
<React.Fragment>
<form onSubmit={this.handle}>
{" "}
{/* After entering all info, Press Enter*/}
<label htmlFor="labels"> Name : </label>
<input
type="text"
id="name"
placeholder="Your name goes here..."
onChange={this.changes.bind(this)}
/>
<label htmlFor="labels"> Age : </label>
<input
type="text"
id="age"
placeholder="Your age goes here..."
onChange={this.changes.bind(this)}
/>
<label htmlFor="labels"> Hobby : </label>
<input
type="text"
id="hobby"
placeholder="Your hobby goes here..."
onChange={this.changes.bind(this)}
/>{" "}
<br />
<br />
<input type="submit" value="SUBMIT" />
<br />
<br />
</form>
<input
type="text"
id="name"
placeholder="Enter name to delete..."
onChange={this.dels}
/>{" "}
<button onClick={this.del_button.bind(this)}> DELETE </button>
</React.Fragment>
);
}
}
export default FormsMap;
This is the demo: https://codesandbox.io/s/xl97xm6zpo

In React, how to bind an input's value when rendering a list of inputs?

I'm rendering a list of inputs and I want to bind each input's value to a link's href. My current attempt renders https://twitter.com/intent/tweet?text=undefined:
class App extends React.Component {
tweets = [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
];
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => tweet.text = e.target.value} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.text}`}>Tweet</a>
</div>
)}
</div>
);
}
}
This probably needs to involve setState but I have no idea how to achieve that when rendering a list. I've tried to do some research on this but didn't found anything helpful.
JSFiddle: https://jsfiddle.net/nunoarruda/u5c21wj9/3/
Any ideas?
You can move the tweets variable to the state to maintain consistency in that array.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
};
setTweets = index => e => {
const { tweets } = this.state
tweets[index].text = e.target.value
this.setState({ tweets })
}
render() {
const { tweets } = this.state
return (
<div>
{tweets.map((tweet, index) =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={this.setTweets(index)} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.text}`}>Tweet</a>
</div>
)}
</div>
);
}
}
Updated Jsfiddle: https://jsfiddle.net/u5c21wj9/6/
You can reach the desired result using state.
return (
<div>
{tweets.map(({ id, link }) =>
<div key={id}>
<input type="text" placeholder="text" onChange={({ target }) => this.setState({ [id]: target.value })} />
<a href={`https://twitter.com/intent/tweet?text=${this.state[id] || link}`}>Tweet</a>
</div>
)}
</div>
);
Note: I would move tweets outside the component and implement few ES6 features.
Updated Jsfiddle: https://jsfiddle.net/u5c21wj9/7/
You really should use a state here and make your tweets variable be part of it. To do that, add a constructor:
constructor() {
super();
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
}
Then you need to mutate each linkwhenever you type in one of the inputs. There are a few pitfalls here, so let me go through them one-by-one:
changeTweet = (id, e) => {
let arr = this.state.tweets.slice();
let index = arr.findIndex(i => i.id === id);
let obj = Object.assign({}, arr[index]);
obj.link = e.target.value;
arr[index] = obj;
this.setState({tweets: arr});
}
First, you need to create a copy of your state variable. This gives you something to work with, without mutating the state directly which is anti-pattern. This can be done with slice().
Since you are sending in the id of the object to modify, we need to find it in our array (in case the items are unordered). This is done with findIndex(). You might want to handle the scenario in which such index is not found (I have not done that).
Now we know where in the array the object with the given id key is. Now, create a copy of that item (which is an object). This is also to prevent mutating the state directly. Do this with Object.assign().
Now change the link to the input value we typed in. Replace the old item object with the new one (obj) and replace the old tweets array with the new one (arr).
Here's the full example:
class App extends React.Component {
constructor() {
super();
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
}
changeTweet = (id, e) => {
let arr = this.state.tweets.slice();
let index = arr.findIndex(i => i.id === id);
let obj = Object.assign({}, arr[index]);
obj.link = e.target.value;
arr[index] = obj;
this.setState({tweets: arr});
}
render() {
return (
<div>
{this.state.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={(e) => this.changeTweet(tweet.id, e)} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.link}`}>Tweet</a>
</div>
)}
</div>
);
}
}
ReactDOM.render(<App />, 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"></div>
You need to save the text from the input in the state (using setState), not in the tweets array. Then you can render it getting the text from the state.
class App extends React.Component {
tweets = [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
];
state = {
tweetsText :{}
}
handleTextChange = (event, tweetId) => {
const tweetsTextCopy = Object.assign({}, this.state.tweetsText)
tweetsTextCopy[tweetId] = event.target.value
this.setState({tweetsText: tweetsTextCopy})
}
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => this.handleTextChange(e, tweet.id)} />
<a href={`https://twitter.com/intent/tweet?text=${this.state.tweetsText[tweet.id]}`}>Tweet</a>
</div>
)}
</div>
);
}
}
Links info is in the link property of your tweets array. The property text is not defined.
So, your render function should look like this
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => tweet.text= e.target.value} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.link}`}>Tweet</a>
</div>
)}
</div>
);
}

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

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'/>

Categories