I am trying ReactJS and following their documentation. I am trying to store new comment and displaying it but it is not showing new comments after submitting the form here is my code
/** #jsx React.DOM */
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="CommentList">
{commentNodes}
</div>
);
}
});
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data:data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data:data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="CommentBox">
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
<h1>Comments</h1>
<CommentList data={this.state.data} />
</div>
);
}
});
var converter = new Showdown.converter();
var Comment = React.createClass({
render: function() {
var rawMarkup = converter.makeHtml(this.props.children.toString());
return(
<div className="Comment">
<h2 className ="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.com.getDOMNode().value.trim();
if(!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.getDOMNode().value = '';
this.refs.com.getDOMNode().value = '';
return;
},
render: function() {
return(
<form className="commentForm" onSubmit={this.handleSubmit} >
<input type="text" placeholder="Your Name" ref="author" />
<input type="text" placeholder="Say Something.." ref="com" />
<input type="submit" value="Post" />
</form>
);
}
});
React.render(
<CommentBox url="comment.json" pollInterval={2000} />,
document.getElementById('content')
);
and comment.json
[
{"author": "abc", "text": "This is one comment"},
{"author": "pqr", "text": "This is *another* comment"}
]
Just remove the line
var newComments = comments.concat([comment]);
in handleCommentSubmit function and add the line
comments.push(comment);
it will work see the full code of handleCommentSubmit function below
handleCommentSubmit: function(comment) {
var comments = this.state.data;
comments.push(comment);
this.setState({data: comments}, function() {
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
});
}
Related
I am trying to extend the React.JS CommentBox example to include the ability to delete comments. I have added a simple button to the Comment class and all of the necessary methods and listeners. Now I am trying to add functionality to a handleDelete method in my CommentBox class to do the necessary Ajax calls to delete the comment on server as well as locally
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
handleDelete: function(id) {
var comments = this.state.data;
var newComments = [];
var commentToDelete = null;
for (var i = 0, len = comments.length; i < len; i++) {
if (comments[i].id != id) {
newComments.push(comments[i]);
} else {
commentToDelete = comments[i];
}
}
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'DELETE',
data: commentToDelete,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
<h1>Comments</h1>
<CommentList data={this.state.data} onCommentDelete={this.handleDelete} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
var CommentList = React.createClass({
handleDelete: function(id) {
console.log('handleDelete2 ' + id);
this.props.onCommentDelete(id);
},
render: function() {
var commentNodes = this.props.data.map(function(comment) {
var handle_delete = this.handleDelete.bind(this, comment.id);
return (
<Comment author={comment.author} key={comment.id} onCommentDelete={handle_delete}>
{comment.text}
</Comment>
);
}, this);
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.author}
onChange={this.handleAuthorChange}
/>
<input
type="text"
placeholder="Say something..."
value={this.state.text}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var md = new Remarkable();
var rawMarkup = md.render(this.props.children.toString());
return { __html: rawMarkup };
},
handleDelete: function(e) {
e.preventDefault();
console.log("handleDelete");
this.props.onCommentDelete();
},
render: function() {
var md = new Remarkable();
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
<button onClick={this.handleDelete}>Delete</button>
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={2000} />,
document.getElementById('content')
);
However, the item won't delete permanently and there is an error in the console:
Failed to load resource: the server responded with a status of 405 (Method Not Allowed)
/api/comments error Method Not Allowed
I really am not an Ajax expert so I am sure I am making a very obvious mistake.
Does anyone have any suggestions? Thanks in advance.
I have a tweet box component which has a getAllTweets function called on submit. The handle submit functionis supposed to capture the value of the field and pass it to thegetAllTweets functionso it the URL, so I can make a dynamic Ajax request. I am unable to save the variable containing thehandle` value and append it. Secondly, when I get the response I need to bind it to the TweetList Component. I can't get either to work..
var Tweet = React.createClass({
render: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="tweet">
<h2 className="tweetAuthor">
{this.props.handle}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var TweetBox = React.createClass({
getAllTweets: function(handle) {
var handle = this.state.data;
$.ajax({
url: this.props.url + handle,
dataType: 'json',
type: 'POST',
data: tweet,
success: function(data) {
console.log(data);
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
render: function() {
return (
<div className="tweetBox">
<h1>Tweets</h1>
<TweetList data={this.state.data} />
<TweetForm onTweetSubmit={this.getAllTweets} />
</div>
);
}
});
var TweetList = React.createClass({
render: function() {
var tweetNodes = this.props.data.map(function(tweet, index) {
return (
<Tweet handle={tweet.handle} key={index}>
{tweet.message}
</Tweet>
);
});
return (
<div className="tweetList">
{tweetNodes}
</div>
);
}
});
var TweetForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var handle = React.findDOMNode(this.refs.handle).value.trim();
if (!handle) {
return;
}
this.props.onTweetSubmit({handle: handle, message: message});
React.findDOMNode(this.refs.handle).value = '';
},
render: function() {
return (
<form className="tweetForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="handle" />
<input type="submit" value="Post" />
</form>
);
}
});
React.render(
<TweetBox url="http://localhost:4000/api/"/>,
document.getElementById('content')
);
First of all, you forget to declare the message variable in your TweetForm component.
this.props.onTweetSubmit({handle: handle, message: message});
In your getAllTweets function, you pass in a handle variable and create another new handle variable again (var handle = this.state.data). That's why you can not get the tweet account name.
Don't use the same name to multiple variables in a method.
I have used getInitialState on my BrowseWidgetBox component. Although when passing the data to my MainMenu component, the data remains empty, as if the AJAX call to the api was never run in the BrowseWidgetBox.
My question then, is why is this happening? Shouldn't componentDidMount call the ajax api and re-set the state to include the contents of the ajax call? I want the state of my groupsData and my itemData to be present when the page is initially loaded. I am a bit worried that getInitialState is hindering the calls to ajax at least 'initially' which is causing my error.
Here is the full code of the two components:
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData); // console.log here
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>obj.description</li>);
});
return (<div className="MainMenu">
<ul>{categories}</ul>
</div>);
}
});
var BrowseWidgetBox = React.createClass({
getInitialState: function () {
return {groupsData: {}, itemsData: {}};
},
getGroupsApi: function(){
$.ajax({
url: this.props.groupsApi,
dataType: 'json',
type: 'GET',
success: function(groupsData){
this.setState({groupsData: groupsData});
console.log(groupsData) // Console.log here
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
getItemsApi: function() {
$.ajax({
url: this.props.itemsApi,
dataType: 'json',
type: 'GET',
success: function(itemsData){
this.setState({itemsData: itemsData});
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.getGroupsApi();
this.getItemsApi();
},
render: function() {
return (<div className="BrowseWidgetBox">
<MainMenu groupsData={this.state.groupsData} itemsData={this.state.itemsData} />
<Display />
</div>);
}
});
React.render(
<BrowseWidgetBox groupsApi="http://this/is/a/good/url" itemsApi="http://this/is/a/good/api/call" />, document.getElementById('widget-container')
);
You're trying to use the map in object...
In
getInitialState: function () {
return {groupsData: {}, itemsData: { objects: [] }};
},
the first render are getting a object in groupsData
try change to
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData); // console.log here
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>obj.description</li>);
});
return (<div className="MainMenu">
<ul>{categories}</ul>
</div>);
}
});
var BrowseWidgetBox = React.createClass({
getInitialState: function () {
return {groupsData: { objects: [] }, itemsData: []};
},
getGroupsApi: function(){
$.ajax({
url: this.props.groupsApi,
dataType: 'json',
type: 'GET',
success: function(groupsData){
this.setState({groupsData: groupsData});
console.log(groupsData) // Console.log here
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
getItemsApi: function() {
$.ajax({
url: this.props.itemsApi,
dataType: 'json',
type: 'GET',
success: function(itemsData){
this.setState({itemsData: itemsData});
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.getGroupsApi();
this.getItemsApi();
},
render: function() {
return (<div className="BrowseWidgetBox">
<MainMenu groupsData={this.state.groupsData} itemsData={this.state.itemsData} />
<Display />
</div>);
}
});
React.render(
<BrowseWidgetBox groupsApi="http://this/is/a/good/url" itemsApi="http://this/is/a/good/api/call" />, document.getElementById('widget-container')
);
Actually, I've been trying to add a 'Delete a comment' functionality to my Comment Box system.
Here is my code:-
var Comment = React.createClass({
handleClick: function(e){
e.preventDefault();
var commentId = this.props.comment.id;
return this.props.onDelete(commentId);
},
render: function () {
return (
<div className="comment">
<h4 className="commentAuthor">
{this.props.comment.email}
</h4>
{this.props.comment.comment}
<a onClick={this.handleClick}> × </a>
</div>
);
}
});
var CommentList = React.createClass({
handleDelete: function(commentId){
return this.props.del(commentId);
},
render: function () {
var commentNodes = this.props.comments.map(function (comment, index) {
return (
<Comment comment = {comment} onDelete = {this.handleDelete} key = {index} />
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentBox = React.createClass({
getInitialState: function () {
return {comments: []};
},
componentDidMount: function () {
this.loadCommentsFromServer();
},
loadCommentsFromServer: function () {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function (comments) {
this.setState({comments: comments});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.comments;
var newComments = comments.concat([comment]);
this.setState({comments: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: {"comment": comment},
success: function(data) {
this.loadCommentsFromServer();
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
delc: function(commentId){
$.ajax({
url: this.props.url,
data: {"id" : commentId},
type: 'DELETE',
dataType: 'json',
success: function (comments) {
this.setState({comments: comments});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList del={this.delc} comments={this.state.comments} />
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
</div>
);
}
});
var CommentForm = React.createClass({
handleSubmit: function() {
var email = this.refs.email.getDOMNode().value.trim();
var comment = this.refs.comment.getDOMNode().value.trim();
this.props.onCommentSubmit({email: email, comment: comment});
this.refs.email.getDOMNode().value = '';
this.refs.comment.getDOMNode().value = '';
return false;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="email" placeholder="Your email" ref="email" />
<input type="text" placeholder="Say something..." ref="comment" />
<input type="submit" value="Post" />
</form>
);
}
});
var ready = function () {
React.renderComponent(
<CommentBox url="18/comments.json" />,
document.getElementById('art')
);
};
$(document).ready(ready);
Now, when I try to delete a comment, it throws an error that says 'Uncaught TypeError: undefined is not a function'
Switching to 'source' tab on devTool, I found that the problem is in 'onDelete' function.
It says, 'onDelete' function is undefined.
I think the problem is due to 'this' keyword, but I'm not sure.
What should I do to resolve this issue? Let me know if I'm missing out on something.(I'm a newbie)
Thank you in advance.
The function passed to map will not automatically share a this pointer with your react class.
To use this inside the anonymous function, call .bind(this) at the end of the function definition to yield a function with the expected this inside.
var commentNodes = this.props.comments.map(function (comment, index) {
...
});
Becomes:
var commentNodes = this.props.comments.map(function (comment, index) {
...
}.bind(this));
I am a newbie in the Reactjs, I am trying to perform CRUD operation. But I am having issues when performing Delete event. This is How my screen looks:
![enter image description here][1]
And my code looks like this:
var DALHandler=React.createClass({
getInitialState:function(){
return{data: {objects:[]}} // variable should be created with array
},
DeleteOrganizationFromServer: function(id) { // this is Json Function
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'URL/'+id,
dataType: 'json',
type: 'Delete',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url='url', status, err.toString());
}.bind(this)
});
},
loadOrganizationFromServer: function() {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'URL/'+id,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url='url', status, err.toString());
}.bind(this)
});
},componentWillMount: function() {
this.loadOrganizationFromServer();
//setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},render: function() {
return (
<div >
<OrganizationAPP onOrganizationSubmit={this.handleOrganizationSubmit} />
<OrganizationList data= {this.state.data} />
<Organization DeleteOrganization= {this.DeleteOrganizationFromServer} />
</div>
);
}
});
var OrganizationList=React.createClass({
render:function(){
var results = this.props.data;
var parsed_results = results.objects;
var organizations = parsed_results.map(function(organization){
return <Organization id={organization.id} name={organization.name} description={organization.description}> </Organization>
});
return(<div>
{organizations}
</div>)
}
});
//var converter = new Showdown.converter();
var Organization = React.createClass({
handleDeleteClick: function (e) {
//alert(this.props.id);
var currentId=this.props.id;
this.props.DeleteOrganization(); // ERROR CAUSES HERE:
},
render: function() {
return (
<div className="row">
<div className="small-2 large-2 columns">{this.props.id} </div>
<div className="small-4 large-4 columns">{this.props.name} </div>
<div className="small-4 large-4 columns">{this.props.description}</div>
<div className="small-2 large-2 columns">
<input type="button" onClick={this.handleDeleteClick} data-order={this.props.id} value="Delete" />
</div>
</div>
);
}
});
I know I am doing some stupid mistake but I don't find help to solve this. Please help me to sort it out.
Thank you in advance.
You create your Organization elements like this:
<Organization id={organization.id}
name={organization.name}
description={organization.description}></Organization>
So inside Organization you will have three props: id, name, and description. You try to call this.props.DeleteOrganization() from inside Organization, which is undefined because it's not one of the three props you're passing to it.
To make it work with OrganizationList, you need to pass the delete function down to it:
<OrganizationList data={this.state.data} onDelete={this.DeleteOrganizationFromServer} />
And inside OrganizationList's render function, you can pass it down again.
<Organization id={organization.id}
name={organization.name}
description={organization.description}
onDelete={this.props.onDelete.bind(null, organization.id}></Organization>
And inside Organization:
handleDeleteClick: function(){
this.props.onDelete()
}
I have managed to the solve problem myslef but this fiddle helped me greatly: http://jsfiddle.net/ammit/wBYHY/5/
So mainly I added one more function in OrganizationList that calls DeleteOrganizationFromServer function, so now my code looks like this:
var OrganizationHandler=React.createClass({
getInitialState:function(){
return{data: {objects:[]}} // variable should be created with array
},
DeleteOrganizationFromServer: function(id) {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'url/'+id,
dataType: 'json',
type: 'Delete',
success: function(data) {
this.loadOrganizationFromServer();
}.bind(this),
error: function(xhr, status, err) {
console.error('url="url', status, err.toString());
}.bind(this)
});
},
loadOrganizationFromServer: function() {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'url',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url="url', status, err.toString());
}.bind(this)
});
},
componentWillMount: function() {
this.loadOrganizationFromServer();
},render: function() {
return (
<div >
<OrganizationAPP onOrganizationSubmit={this.handleOrganizationSubmit} />
<OrganizationList external_DeleteOrganization={this.DeleteOrganizationFromServer} data= {this.state.data} />
</div>
);
}
});
var OrganizationList=React.createClass({
internal_DeleteOrganization: function(id) {
this.props.external_DeleteOrganization(id);
},
render:function(){
var results = this.props.data;
var parsed_results = results.objects;
var that = this; // Not equally that to this was also causing error,
var organizations = parsed_results.map(function(organization){
return <Organization onDeleteOrganization={that.internal_DeleteOrganization} id={organization.id} name={organization.name} description={organization.description} />
});
return(<div>
{organizations}
</div>)
}
});
var Organization = React.createClass({
handleDeleteClick: function () {
this.props.onDeleteOrganization(this.props.id);
},
});