update list array from child component - reactjs

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.

Related

Why data is not rendered on refresh in react js with asynchronous call?

I am creating edit form.First i have to get data to edit form and i am calling it in componentDidMount().Please see code below.
import React from 'react';
import CompanyForm from './CompanyForm';
import { connect } from 'react-redux';
import { companyActions } from '../../../redux/actions/company-action';
class EditCompanyPage extends React.Component {
constructor(props){
super(props);
};
componentDidMount () {
const { id } = this.props.match.params
const { dispatch } = this.props;
dispatch(companyActions.getCompany(id));
}
render(){
const {editUser } = this.props;
return(
<div>
<h1>Edit Company</h1>
{
editUser && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
}
</div>
);
};
}
function mapStateToProps(state) {
const { editUser } = state.companyReducer;
return {
editUser
};
}
const EditCompany = connect(mapStateToProps)(EditCompanyPage);
export default EditCompany;
see code for CompanyForm component below:
import React from 'react';
class CompanyForm extends React.Component {
constructor(props){
super(props);
this.state = {
company :{
name : this.props.companyDataFP.name || '',
address1 : this.props.companyDataFP.address1 || '',
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleChange(e) {
const { name, value } = e.target;
const newState = Object.assign({}, this.state);
newState.company[name] = value;
this.setState(newState);
}
handleSubmit(e) {
e.preventDefault();
return false;
}
render(){
return(
<div className="col-md-12">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="name">Name</label>
<input type="text" name="name" className="form-control" onChange={this.handleChange} value={this.state.company.name} />
</div>
</div>
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="address1">Address 1</label>
<input type="text" name="address1" className="form-control" onChange={this.handleChange} value={this.state.company.address1} />
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className='form-group'>
<input type="submit" className="btn btn-info" value="submit" />
</div>
</div>
</div>
</form>
</div>
);
};
}
export default CompanyForm;
It works fine when i access this form with
<Link to="/edit-form/:id" >Edit</Link>
but when i refresh the current page then values are not rendering into form to edit.
I am using redux approach for state management, please guide me i am new to react.
Probably ComponyForm initializes form on its componentDidMount lifecycle function, so when editUser arrives nothing will change.
A way to handle this is changing:
<CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
to:
{editUser.name && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />}

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

react add many items in component

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>

ReactJS search bar child to parent

I have a search bar component which is viewed on all pages of a project. Is there a way that I can have the same search bar component but have a property that I could pass so that it knows which data to search through. My search component is <SearchBar type="PASS THIS VALUE" /> and this is rendered:
import React, { Component } from 'react';
class SearchBar extends Component {
render () {
function handleClick(e) {
e.preventDefault();
console.log("Clicked!!!");
}
return (
<div className="row main-search">
<div className="column">
<form action="">
<fieldset>
<label htmlFor="search">
<input type="text" placeholder="Start typing..." id="search-box" />
</label>
</fieldset>
</form>
</div>
<div className="column">
<div className="float-right"><button className="add-new" onClick={handleClick}>Add New</button></div>
</div>
</div>
)
}
}
export default SearchBar;
If this isn't possible is there a way that the searchBar can know which page the user is on and know which data to search through?
Update:
I am using a router element and the searchBar component is located inside parent App.js. I'm using the router like:
<SearchBar />
<Route
exact path="/" children={({ match, ...rest }) => (
<TransitionGroup component={firstChild}>
{match && <TourList {...rest} />}
</TransitionGroup>
)}/>
You can access the props through this.props and set the value of input accordingly, but beware that the component will become controlled by react flow, you won't be able to change the input, thus you need onChange event handler for that and controlled local state version of that prop instead.
It may go like this:
import React, { Component } from 'react';
class SearchBar extends Component {
state = { // pass default value from "type" prop
type: this.props.type
};
// making sure we don't call setState excessively
// as per https://reactjs.org/docs/react-component.html#componentwillreceiveprops
componentWillReceiveProps(nextProps) {
if (nextProps.type !== this.props.type) {
this.setState({ type: nextProps.type });
}
}
// pulling out handleClick function from render()
// to avoid creating it on every render call
handleClick = (e) => {
e.preventDefault();
console.log("Clicked!!!");
}
// making sure we update our controlled component state
// so we can actually type and get the change back in the value
// more info here: https://reactjs.org/docs/forms.html#controlled-components
handleChange = e => {
this.setState({ type: e.target.value });
};
render () {
return (
<div className="row main-search">
<div className="column">
<form action="">
<fieldset>
<label htmlFor="search">
<input type="text"
placeholder="Start typing..."
id="search-box"
onChange={this.handleChange}
// listening for our local and controlled version
// of "type" prop that is now reacting both to
// parent prop change and to local input change through typing
value={this.state.type}/>
</label>
</fieldset>
</form>
</div>
<div className="column">
<div className="float-right">
<button className="add-new" onClick={handleClick}>Add New</button>
</div>
</div>
</div>
)
}
}
export default SearchBar;

Cannot trigger component's function in my react app

I am currently working with react and I have runned into a problem this morning that i do not understand.
I am trying to handle a form submit from my component with a function by passing it in the onSubmit property but it does not trigger it. I then added a button to trigger a mock function with its onClick property, and i still got the same problem; it appears than I can't trigger my function and I cannot find any solution on the Google.
Here is my code so you can check it out:
import React from 'react';
import AgentsStore from '../stores/AgentsStore';
import AgentsActions from '../actions/AgentsActions';
class Agents extends React.Component {
constructor(props) {
super(props);
this.state = AgentsStore.getState();
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
AgentsStore.listen(this.onChange);
}
componentWillUnmount() {
AgentsStore.unlisten(this.onChange);
}
onChange(state) {
this.setState(state);
}
handleSubmit(event) {
event.preventDefault();
var user = {};
user.name = this.state.newUser.name.trim();
user.lastname = this.state.newUser.lastname.trim();
if (user.name && user.lastname) {
AgentsActions.createUser(user);
}
}
onClick() {
console.log('clicked');
}
render() {
return (
<div /*className='container'*/>
<div className='alert alert-info'>
Hello from Agents
</div>
<div className='panel panel-default'>
<div className='panel-heading'>Create User</div>
<div className='panel-body'>
<form onSubmit={this.handleSubmit.bind(this)}>
<div className='form-group'>
<label className='control-label'>Name</label>
<input type='text'
className='form-control'
ref='nameTextField'
value={this.state.newUser.name}
onChange={AgentsActions.updateName} />
</div>
<div className='form-group'>
<label className='control-label'>Lastname</label>
<input type='text'
className='form-control'
ref='lastnameTextField'
value={this.state.newUser.lastname}
onChange={AgentsActions.updateLastname} />
</div>
<h4>Address</h4>
<button type='submit' className='btn btn-primary'>Submit</button>
</form>
<button onClick={this.onClick} className='btn btn-primary'>Submit</button>
</div>
</div>
</div>
)
}
}
export default Agents;
I do not know if I am missing something in my code or there is an error but i am kind of desperate. Also I am basing my code on another file that i have which is working just fine...
Thank you in advance for your time

Resources