react add many items in component - reactjs

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>

Related

how to pass item from list in state to child component?

I am creating a wizard form - the user is creating fields, and I want to display these fields to him , so I have "fields" list in parent state and I want to call a child component (that presents the field ) by passing it an item from "fields" list in parent state in a loop - and I am struggling with it
the passing item is a JSON with the attributes fro the field (name, label & type)
class Wizard extends React. Component{
constructor(props){
super(props);
this.state = {
showCard:false,
title:'',
type:'',
name:'',
label:'',
fields:[]
};
}
render(){
const result = Object.keys(this.state.fields).map((field, index) =>{
return(
<FieldsViewer
key={index}
field={field}
/>
)
})
return(
<div className="ui segment">
<form onSubmit={this.onFormSubmit} className="ui form">
<div className="field">
<input
className="ui header"
placeholder="Place your form title here"
type="text"
value={this.state.title }
onChange={(e)=>this.setState({title:e.target.value})}
/>
<div>
<button
style={{marginTop:"50px" }}
className="ui button"
onClick={this.CreateProperty}>
Add Field
</button>
</div>
{
this.state.fields.length>0 && {result}
}
</div>
</form>
</div>
);
}
}
const FieldsViewer=(props)=>{
console.log(props);
return(
<div>
{<label>
{props.field}:
<label>
<input
type={props.type}>
</input>
</div>
);
}
With the map as you have it, you are creating a FieldsViewer for each key, not for each instance of field.
const result = this.state.fields.map((field, index) =>{
return(
<FieldsViewer
key={index}
field={field}
/>
)
})
Then you are passing the whole field object to FieldsViewer, so they can be accessed as props.field.label props.field.type etc within that component. Or, you may wish to destructure the object before passing it to the child component:
const result = this.state.fields.map((field, index) =>{
return(
<FieldsViewer
key={index}
name={field.name}
label={field.label}
type={field.type}
/>
)
})
Hope this helps.

How to accept and pass two parameter as props

Hi I need to pass two parameters, to the class Chat. Currently it is getting only one parameter and displaying correctly.
const Chat = props => (
<div >
<ul>{props.messages.map(message => <li key={message}>{message}</li>)}</ul>
</div>
);
This Chat.js file is called from the Home.js. Suppose I need to pass the Chat component two parameters and I tried it like following.
import React, { Component } from 'react';
import { User } from './User';
import Chat from './Chat';
export class Home extends Component {
displayName = Home.name
state = {
messages: [],
names: []
};
handleSubmit = (message,name) =>
this.setState(currentState => ({
messages: [...currentState.messages, message],
names: [...currentState.names,name]
}));
render() {
return (
<div>
<div>
<User onSubmit={this.handleSubmit} />
</div>
<div>
<Chat messages={this.state.messages,this.state.name} />
</div>
</div>
);
}
}
In this scenario how should I change the Chat component to accept two parameters and display inside div tags.
This is what I tried. But seems it is incorrect.
const Chat = props => (
<div >
<ul>{props.messages.map((message, name) => <li key={message}>{message}</li> <li key={name}>{name}</li>)}</ul>
</div>
);
PS: The User Method
import * as React from 'react';
export class User extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '',
message: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
render() {
return (
<div className="panel panel-default" id="frame1" onSubmit={this.handleSubmit}>
<form className="form-horizontal" action="/action_page.php" >
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="name">Your Name </label>
<div className="col-sm-10">
<input type="text" className="form-control" name="name" placeholder="Enter your Name" onChange={this.handleChange} />
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="message">Message</label>
<div className="col-sm-10">
<input type="text" className="form-control" name="message" placeholder="Enter your Message" onChange={this.handleChange}/>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button type="submit" id="submit" className="btn btn-default">Submit</button>
</div>
</div>
</form>
</div>
);
}
handleChange(evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
handleSubmit = (e) => {
e.preventDefault();
this.props.onSubmit(this.state.message, this.state.name);
this.setState({ message: "" });
this.setState({name:""});
};
}
You can do this by using separate attributes to pass different props. So for instance, you might revise your <Home/> components render method like so:
<Chat messages={this.state.messages} names={this.state.names} />
and then to access these two bits of data (messages and name) from inside the <Chat /> component you could do the following:
const Chat = props => (
<div >
<ul>{props.messages.map((message, index) => <li key={message}>
From: { Array.isArray(props.names) ? props.names[index] : '-' }
Message: {message}</li>)}
</ul>
</div>
);
Hope this helps!
You have to pass them separately:
<Chat messages={this.state.messages} name={this.state.name} />

Editing input fields in react seeding with default

I have a bootstrap modal where a user is supposed to edit what he entered in a table. When I set-state using static getDerivedStateFromProps, the user cannot modify the input box, but when I manually initialize the state with some arbitrary data, the user can modify it. What am I doing wrong?
the component is getting its props from redux.
export default class EditItem extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
handleChange = (event) => {
let { value, name } = event.target;
this.setState({
[name]: value
})
}
static getDerivedStateFromProps(nextProps, prevState){
return {
name: nextProps.itemDetails.name
}
}
render() {
let {
} = this.state;
let {itemDetails} = this.props
return (
<div id="myModal3" className="modal fade">
<div id={uuidv1()} className="modal-dialog">
<div id={uuidv1()} className="modal-content">
<div id={uuidv1()} className="modal-body">
<form id={uuidv1()} className="form-horizontal form-material">
{this.props.cat_name}
<div id={uuidv1()} className="form-group">
<label id={uuidv1()} className="col-md-8">
Item Name
</label>
<div id={uuidv1()} className="col-md-8">
<input
id={uuidv1()}
onChange={this.handleChange}
value={this.state.name}
name="name"
type="text"
placeholder="ex. Burrito"
className="form-control form-control-line"
/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
Using getDrivesStateToProps is not a good option like that. You have a prop, why don't you use it directly? You can look here for more explanation.
Set your initial state to your prop:
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
name: this.props.itemDetails.name,
};
}
handleChange = ( event ) => {
const { value, name } = event.target;
this.setState( {
[ name ]: value,
} );
};
render() {
let { } = this.state;
const { itemDetails } = this.props;
console.log( this.state );
return (
<div>
<div>
<div>
<div>
<form>
{this.props.cat_name}
<div>
<label>Item Name</label>
<div>
<input
onChange={this.handleChange}
value={this.state.name}
name="name"
type="text"
placeholder="ex. Burrito"
className="form-control form-control-line"
/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
const itemDetails = { name: "foo" };
ReactDOM.render(<App itemDetails={itemDetails}/>, 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>
If your prop is coming from an async operation, I don't know maybe you can use something like this just to be in the safe side:
this.state = {
name: ( this.props.itemDetails && this.props.itemDetails.name ) || "",
};
Maybe there is a better way ¯(°_o)/¯

update list array from child component

new react user here.
i am trying to access form data in my parent app from the child form. I am trying to alert or console the data from the parent so I can visually see what was typed in the form. Once I can access the data in the parent I will try and move it to my list array.
PARENT
class App extends Component {
constructor() {
super();
this.state = {
lists: [],
items: {}
};
}
handleAddList(s) {
alert('I am calling function from child')
console.log(this.refs.id.value) // this errors out on me
}
render() {
return (
<div className="App">
<AddList addList={this.handleAddList.bind(this)} />
<div id="listsDiv" className="List">
<Lists lists={this.state.lists} items={this.state.items} addItem {this.handleAddItem.bind(this)} />
</div>
</div>
);
}
}
CHILD
class AddList extends Component {
handleSubmit(e) {
e.preventDefault();
alert(this.refs.id.value)
this.props.addList()
}
render() {
return (
<div id="addListDiv">
<form onSubmit={this.handleSubmit.bind(this)}>
<div id='addList'>
<label>What will be on your next list?
<input type='text' ref='id' id='newID'></input>
</label>
</div><br />
<input type='submit' value='Create List' />
</form>
</div>
);
}
}
You should set the ref on the input using a callback, like this:
<input type='text' ref={input => { this.input = input; }} id='newID'></input>
Then access it in your event handler like this:
alert(this.input.value);
However, if you are new to React, you should try using controlled components before you try to use refs.
https://reactjs.org/docs/forms.html
CHILD
import ReactDOM from 'react-dom';
class AddList extends Component {
handleSubmit(e) {
e.preventDefault();
var day = ReactDOM.findDOMNode(this.refs.id).value.trim();
this.props.addList(day);
}
render() {
return (
<div id="addListDiv">
<form onSubmit={this.handleSubmit.bind(this)}>
<div id='addList'>
<label>What will be on your next list?
<input type='text' ref='id' id='newID'></input>
</label>
</div><br />
<input type='submit' value='Create List' />
</form>
</div>
);
}
}
PARENT
class App extends Component {
handleAddList(args) {
console.log(args);
}
render() {
return (
<div className="App">
<AddList addList={this.handleAddList.bind(this)} />
</div>
);
}
}
Edit it a little to work for you.

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