Reactjs multiple form submit - reactjs

In my case, i would like to submit multiple forms in reactjs. But i have no idea on how to get the multiple form at Parent Component and submit.
here is my code:
class BulkEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
customCompanies: []
};
this.forms = [];
this.onAddChild = this.onAddChild.bind(this);
this.handleBulkSaveClick = this.handleBulkSaveClick.bind(this);
}
handleBulkSaveClick(event) {
event.preventDefault();
}
/*
* -- Add Children
*/
onAddChild() {
this.state.items.push(BulkEditorForm.defaultProps);
this.setState({
items: this.state.items
});
}
render() {
var forms = this.state.items.map(function(item, index) {
return (
<li className="list-group-item" key={index}>
<BulkEditorForm companies={this.state.customCompanies} item={item}
ref="editorform"></BulkEditorForm>
</li>
);
}.bind(this));
return (
<ul className="list-group">
{forms}
<li className="list-group-item">
<div className="btn-group btn-group-sm pull-right" role="group" aria-label="bulk-buttons">
<a href="javascript:;" className="btn btn-primary" onClick={this.onAddChild.bind(this)}>
<span className="glyphicon glyphicon-plus"></span>
</a>
<a href="javascript:;" className="btn btn-default" onClick={this.handleBulkSaveClick}>Bulk Save</a>
</div>
<div className="clearfix"></div>
</li>
</ul>
);
}
}
Here is next class
export default class BulkEditorForm extends React.Component {
constructor(props) {
super(props);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit(event) {
event.preventDefault();
alert("Submit");
}
render() {
return (
<form action='#' method="post" onSubmit={this.handleFormSubmit}>
<button type="submit" className="btn btn-link">Save</button>
</form>
);
}
}

In your loop of rendering form list, use different ref value for each form:
<BulkEditorForm companies={this.state.customCompanies} item={item}
ref={"editorform"+index}></BulkEditorForm>
Then after all forms are rendered, access the form list by refs in your Parent Component, which means adding componentDidMount() function as follows:
class BulkEditor extends React.Component {
constructor(props) {
}
componentDidMount() {
//using basic javascript "FOR" loop ^^
for (i = 0; i < this.state.items.length; i++) {
this.forms.push(this.refs["editorform"+index]);
}
}
}
I didn't have time for testing all the code, but that's the idea! If it doesn't work yet, feel free to post here some error logs, then we may solve it together, thanks ^^

Related

How to pass methods with parameters to children in React? (Cannot read property 'props' of undefined)

I'm currently making a to-do list, but unfortunately ran into some errors where I am unable to pass methods to a child component.
const React = require('react');
class App extends React.Component {
constructor(props){
super(props)
this.state = {
items: [],
input: ""
}
this.inputChange = this.inputChange.bind(this);
this.addItems = this.addItems.bind(this);
this.remItems = this.remItems.bind(this);
}
inputChange(event){
this.setState({
input: event.target.value
})
}
addItems(){
event.preventDefault()
const temp = this.state.input
this.setState({
input: "",
items: this.state.items.concat(temp)
})
}
remItems(id){
const temp = [...this.state.items]
const updatedItems = temp.filter(item => item.id!==id)
this.setState({
items: updatedItems
})
}
render(){
return (
<div class = "w-100 p-3">
<p>Enter your item here:</p>
<form onSubmit = {this.addItems} class="form.inline">
<input class = "form.control mb-2 mr-sm-2" value = {this.state.input} onChange = {this.inputChange}/>
<button type="submit" class="btn btn-primary mb-2">Add</button>
</form>
<UnorderedList items={this.state.items} remove = {this.remItems}/>
</div>
);
}
}
class UnorderedList extends React.Component {
constructor(props){
super(props)
}
render(){
return (
<ul>
{this.props.items.map(function(item, i) {
return (<div id = "entry" class = "d-flex">
<li key={i}>{item}</li>
{console.log(i)}
<button class = 'btn btn-sm btn-primary ml-auto' onClick = {() => this.remItems}> Done! </button>
</div>)
})}
</ul>
)
}
}
I expected to be able to remove the items by id key but it gives me "Cannot read property 'props' of undefined". Thanks for your help!

TypeError: this.props.likeMovie is not a function

i am using ReactJs and firebase.
I have this error when i try to push "like data" to the db.
All i want is to set the movie like to 1 instead of 0.
I have a Movie DB api which i use to fetch and display movie info to the user.
then i use firebase authentication to log in and then the user clicks the like button and it should save the value 1 to the db.
I am new to React and don't fully understand the concepts, so if its something stupid i'm sorry.
Here is the error:
TypeError: this.props.likeMovie is not a function
AddLike.likeMovie
C:////
23 | this.setState({
24 | newMovieContent: 1,
25 | })
> 26 | this.props.likeMovie(this.state.newMovieContent);
27 | }
28 |
29 | render(){
Here is the code:
import React, { Component } from 'react';
class AddLike extends Component{
constructor(props){
super(props);
this.state = {
newMovieContent: 0,
};
this.handleUserInput = this.handleUserInput.bind(this);
this.likeMovie = this.likeMovie.bind(this);
}
likeMovie(){
this.setState({
newMovieContent: 1,
})
this.props.likeMovie(this.state.newMovieContent);
}
render(){
return(
<div>
<button
onClick={this.likeMovie}>Like</button>
</div>
)
}
}
export default AddLike;
Movie row
addMovieRecord is adding like value 1, will have more data later.(that is why called that instead of addlike)
class MovieRow extends React.Component {
constructor(props, context) {
super(props, context);
this.addMovieRecord = this.addMovieRecord.bind(this);
this.removeMovie = this.removeMovie.bind(this);
this.app = fire;
this.database = this.app.database().ref().child('movies');
this.state = {
show: false,
movies: [],
};
componentWillMount(){
const previousMovies = this.state.movies;
//ADD
this.database.on('child_added', snap => {
previousMovies.push({
id: snap.key,
movieRecord: snap.val().movieRecord,
})
this.setState({
movies: previousMovies
})
})
//REMOVE
this.database.on('child_removed', snap => {
for(var i=0; i < previousMovies.length; i++){
if(previousMovies[i].id === snap.key){
previousMovies.splice(i, 1);
}
}
this.setState({
movies: previousMovies
})
})
}
//ADD
addMovieRecord(movie){
this.database.push().set({movieRecord: movie});
}
//REMOVE
removeMovie(movieId){
this.database.child(movieId).remove();
}
render() {
return <div key={this.props.movie.id}>
<div className="row">
<div className="col-md-4 card card-body">
<img src={this.props.movie.poster_src} style={{width:'15em', height:'20em'}}
className="thumbnail" alt="Poster" />
</div>
<div className="col-md-8">
<h2 className="mb-4">{this.props.movie.title}</h2>
<ul className="list-group">
<li className="list-group-item">
<strong>Released:</strong> {this.props.movie.release_date}
</li>
<li className="list-group-item">
<strong>Rated:</strong> {this.props.movie.popularity}
</li>
<li className="list-group-item">
<AddLike addMovieRecord={this.addMovieRecord}/>
{
this.state.movies.map((movie) => {
return (
<Movie movieRecord={movie.movieRecord}
moviId={this.props.movie.id}
key={this.props.movie.id}
removeNote ={this.removeNote}/>
)
})
}
</li>
</ul>
</div>
</div>
<>
<Button variant="primary" onClick={this.handleShow}>
Show More Information
</Button>
<Modal show={this.state.show} onHide={this.handleClose}>
<Modal.Header closeButton>
<Modal.Title>{this.props.movie.title}</Modal.Title>
</Modal.Header>
<Modal.Body>{this.props.movie.overview}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
<hr/>
</div>
}
}
export default MovieRow
Thanks in advance.
The function passed in as a prop to the AddLike component is called addMovieRecord, so you need to call this.props.addMovieRecord instead of this.props.likeMovie.
setState is also asynchronous, so using this.state.newMovieContent directly after updating it will not give you the latest value. You can call addMovieRecord in a callback given to setState instead:
likeMovie(){
this.setState({
newMovieContent: 1,
}, () => {
this.props.addMovieRecord(this.state.newMovieContent);
});
}

How to remove an element in reactjs by an attribute?

I want to get unknown key attribute using known ID so that i may delete corresponding div.
I tried using document.getElementById("a").getAttribute('key'); , but it isn't working. May be my concept is wrong.
class PostAdded extends Component {
constructor(props) {
super();
this.deletepost = this.deletepost.bind(this);
}
deletepost() {
let ab =document.getElementById("a").getAttribute('key');
console.log(ab)
}
render() {
return (
<div>
{ this.props.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={this.deletepost}/>
</div>
) }
</div>
)
}
}
export default PostAdded;
If you were able to delete the div, that probably wouldn't end up working for you anyway because any state change would cause a re-render and it would appear again. Instead, you could keep track of your posts in state and then remove one of the posts from state in your deletepost method.
class PostAdded extends Component {
constructor(props) {
super();
this.state = {
posts: props.posts
}
this.deletepost = this.deletepost.bind(this);
}
deletepost(index) {
const newPosts = this.state.posts.splice(index)
this.setState({posts: newPosts})
}
render() {
return (
<div>
{ this.state.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={() => this.deletepost(i)}/>
</div>
) }
</div>
)
}
}
export default PostAdded;

error while using state and map in reactjs

I am new to react. I am fetching github user info on search. I unable
to fetch data in my child component. this is my code below.
whats the problem , cant i use this.state.userList.map
class SearchHeader extends Component {
constructor(props) {
super(props);
this.state = {
errorMessage: '',
userList: [],
isOpen: false,
userName:''
};
this.toggle = this.toggle.bind(this);
this.getUsers = this.getUsers.bind(this);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
// componentWillMount(){
// this.getUsers();
// }
getUsers(e) {
console.log('get users called='+e.target.value);
fetch('https://api.github.com/search/users?q='+ e.target.value)
.then(res => res.json())
.then(
userList =>{
this.setState({userList: userList})
console.log(userList);
}
);
}
render() {
return (
<div>
<nav className="navbar navbar-expand-lg navbar-light bg-primary navbar-inner">
<div className="collapse navbar-collapse navbar-inner navb" >
<ul className="navbar-nav bg-light mr-auto">
<li className="nav-item dropdown">
<a className="nav-link dropdown-toggle auto" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sort
</a>
<div className="dropdown-menu" aria-labelledby="navbarDropdown">
<a className="dropdown-item" href="#">Sort by Name (ascending)</a>
<a className="dropdown-item" href="#">Sort by Name (descending)</a>
<div className="dropdown-divider"></div>
<a className="dropdown-item" href="#">Sort by Rank (ascending)</a>
<a className="dropdown-item" href="#">Sort by Rank (descending)</a>
</div>
</li>
</ul>
<form className="form-inline my-2 my-lg-0 auto" onSubmit={this.getUsers}>
<div className="form-group">
<input className="form-control mr-sm-2" type="Search" placeholder="Search"
aria-label="Search"
id="userName"
onKeyUp={this.getUsers} >
</input>
</div>
</form>
</div>
</nav>
<div >
<UserList userList={this.state.userList}/>
</div>
</div>
);
}
}
export default SearchHeader;
This is my child component below where I am fetching data from parent
component
This is my child component below where I am fetching data from parent
component
class UserList extends Component {
constructor(props) {
super(props)
this.state ={
users:this.props.userList
}
}
render() {
return (
<div className="container-fluid">
<br />
{
this.state.users.map((user)=>
<div className="jumbotron container">
{user.login}
</div>
)
}
</div>
);
}
}
export default UserList;
You have several problems in your components:
do not copy parent's state into chilren states: users:this.props.userList. Use this.props directly instead and React will know it must re-render children
do not rely on current state to set new state. Use function with prevState instead of isOpen: !this.state.isOpen.
make a copy of event's value before passing it to setState like this const {value} = e.target;
assign unique key to each user in your list (not indexes!), or it won't re-render correctly on list update
So your code would look like this:
class SearchHeader extends Component {
constructor(props) {
super(props);
this.state = {
errorMessage: '',
userList: [],
isOpen: false,
userName:''
};
}
toggle = () => {
this.setState( (prevState) => ({
isOpen: !prevState.isOpen
}));
}
getUsers = (e) => {
const {value} = e.target;
console.log('get users called='+value);
fetch('https://api.github.com/search/users?q='+ value)
...
}
}
and:
class UserList extends Component {
// Use default constructor
render() {
const users = this.props.userList.map( (user) => (
<div className="jumbotron container" key={user.login}>
{user.login}
</div>
));
return (
<div className="container-fluid">
<br />
{users}
</div>
);
}
}
parent component change should be.
getUsers(e) {
console.log('get users called='+e.target.value);
fetch('https://api.github.com/search/users?q='+ e.target.value)
.then(res => res.json())
.then(
userList =>{
this.setState({userList: userList.items})
console.log(userList);
}
);
}
Change your user list and check initially values are there or not and you dont need to user state in userList component.
that is all because initially there are no values also there can be an case when you are setting state for userList value after fetching data that might be coming as null undefined or something else so put an console log there and check that too.
class UserList extends Component {
render() {
return (
{
this.props.userList && this.props.userList.length && this.props.userList.map((user)=>
{user.login}
)
}
</div>
);
}
}
export default UserList;

React issue with composition - _constructComponentWithoutOwner [duplicate]

Everything seems to work with this small app except adding a new note. Button is located on the Board component.
i know this problem is usually caused by not binding value of 'this' properly. I'm not sure if that's the issue here or if i'm missing something else. Thanks
Demo: http://jsbin.com/pewahi/edit?js,output
/* jshint asi:true */
class Note extends React.Component {
constructor(props) {
super(props)
this.state = { editing: props.editing }
}
render() {
if (this.state.editing) {
return this.renderForm()
} else {
return this.renderDisplay()
}
}
edit() {
this.setState({editing: true})
}
save() {
this.props.changeHandler(this.refs.newText.getDOMNode().value, this.props.index)
this.setState({editing: false})
}
remove() {
this.props.removeHandler(this.props.index)
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button className="btn btn-sm glyphicon glyphicon-pencil" onClick={this.edit.bind(this)}></button>
<button className="btn btn-sm glyphicon glyphicon-trash" onClick={this.remove.bind(this)}></button>
</span>
</div>
)
}
renderForm() {
return (
<div className="note">
<textarea ref="newText" defaultValue={this.props.children} className="form-control"></textarea>
<button onClick={this.save.bind(this)} className="btn btn-success btn-sm"><span className="glyphicon glyphicon-floppy-disk"></span> Save</button>
</div>
)
}
}
Note.propTypes = {
editing: React.PropTypes.bool,
onChange: React.PropTypes.func,
onRemove: React.PropTypes.func
}
Note.defaultProps = { editing: false }
class Board extends React.Component {
constructor(props) {
super(props)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
update(newText, i) {
var arr = this.state.notes
arr[i].note = newText
this.setState({notes: arr})
}
remove(i) {
var arr = this.state.notes
arr.splice(i, 1)
this.setState({notes: arr})
}
addNote(text) {
var arr = this.state.notes
arr.push({
id: this.nextId(),
note: text
})
console.log(arr)
this.setState({notes: arr})
}
nextId() {
this.uniqueId = this.uniqueId || 0
return ++this.uniqueId
}
eachNote(note, i) {
return (
<Note key={note.id}
index={i}
changeHandler={this.update.bind(this)}
removeHandler={this.remove.bind(this)}
>{note.note}
</Note>
)
}
render() {
return (
<div className="board">
{this.state.notes.map(this.eachNote, this)}
<button onClick={this.addNote.bind(this, "new note")} className="btn btn-success btn-sm glyphicon glyphicon-plus"></button>
</div>
)
}
}
React.render(
<Board />,
document.getElementById('message-board')
)
Your code is fine. This is likely a bug with JSBin, and how it handles transpilation with Babel. If you add the pragma // noprotect to the top of your code you will see that it works.
I was facing the same error. I was using a base component and I noticed that I had removed componentDidMount method of the base component. And when I call super.componentDidMount in sub component it was giving the error. So I have removed super call and problem solved.
Binding this is something of a hassle with ES6 classes in React. One way is to bind them in your constructor like so;
constructor(props) {
super(props)
this.nextid = this.nextid.bind(this)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
Another is to use babel.configure({stage: 0}) and arrow functions.
nextid = () => {}

Resources