Fetched data from ajax in component is not rendering in React - javascript

Following this. I have created a component and from that trying to fetch data using ajax request. I have written code for rendering it but it's not working and also no error after page load. I am new to react so not much idea, probably that's why unable to inspect the issue. Any help will be great.
// Js
React.createElement('tbody', {}, UserItem)
// Component
var UserItem = React.createClass({
propTypes: {
name: React.PropTypes.string.isRequired,
email: React.PropTypes.string,
dob: React.PropTypes.string,
},
getInitialState: function() {
return {
users: []
}
},
showResults: function(response) {
this.setState({
users: response
})
},
search: function(URL) {
$.ajax({
type: "GET",
dataType: 'json',
url: URL,
success: function(response){
this.showResults(response);
}.bind(this)
});
},
componentDidMount: function() {
this.search("users/usersData");
},
render: function() {
return (
UserItems
)
},
})
var UserItems = React.createClass({
render: function() {
var resultItems = this.state.users.map(function(result) {
return React.createElement(ResultItem, {id:result.id, name:result.name});
});
return React.createElement('div', {}, resultItems);
},
})
var ResultItem = React.createClass({
render: function(){
return React.createElement('tr', {},
React.createElement('td', {}, this.props.id),
React.createElement('td', {}, this.props.name),
React.createElement('td', {}, this.props.name),
React.createElement('td', {}, this.props.name)
)
}
});

You should implement render() method.
Something similar to this one should work:
render: function() {
return (
<div>
{this.state.users.map(function(user){
return <h2>{user}</h2>
})}
</div>
);
}

Try this
search: function(URL) {
var self = this;
$.ajax({
type: "GET",
dataType: 'json',
url: URL,
success: function(response){
self.showResults(response);
}.bind(this)
});
}

Related

ReactJs render simple JSON

I have simple json
{"id":157,"content":"Hello, World!"}
I want to render id in one div and content in anotther. Problem for me is when I call {this.state.data.content} twice it crash.
var Stuff = React.createClass({
getInitialState: function () {
return {
data: []
};
},
componentDidMount: function() {
$.ajax({
url: "http://rest-service.guides.spring.io/greeting",
dataType: 'json',
cache: false,
success: function(response) {
this.setState({
data: response
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div>Response - {this.state.data.content}</div>
);
}
});
The problem solved by surrounding jsx with another div
return (
<div>
<div>Response - {this.state.data.content}</div>
<div>id - {this.state.data.id}</div>
</div>
);
Well, try to render a string instead of an object
render: function() {
return (<div> Whatever - {JSON.stringify(this.state.data)}</div>)
}
Tipp:
if you want to make it pretty: use JSON.stringify(this.state.data, null, 2)

How to extend React.JS CommentBox example to allow deleting of comments?

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.

ReactJS: How to best handle JSON response of newly created object

I have three React components: the first is a container (NoteContainer component) which is responsible for rendering my objects (Note component) in the UI. The data is obtained as JSON via AJAX GET. The last component is a form (NoteForm) which creates new objects (via AJAX POST).
The response from the POST is only the JSON representation of the newly created object, not the JSON for all of the objects.
Should the NoteForm send the JSON response from creating a new object to the NoteContainer which would append it to its state.data and re-render , or should the NoteContainer request the full list of objects and update its state date entirely?
I would presume the first way is better since it does not require requesting data which is already present in the state of NoteContainer. However, I'm still not sure of the "best" way to handle this. Should I give NoteContainer another function, something like addNewNote, which would take the JSON data from the NoteForm and append it to state.data?
I'm new to React so I apologize if this is not a clear question. Here are my components:
var NoteContainer = React.createClass({
getInitialState: function(){
return {data: []};
},
componentDidMount: 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)
});
},
render: function(){
var noteNodes = this.state.data.map(function(note){
return (
<Note title={note.title} body={note.body} />
);
});
return (<div className='noteContainer'>{noteNodes}</div>);
}
});
var Note = React.createClass({
render: function(){
return (
<div className="note" >
<h1>{this.props.title}</h1>
<p>{this.props.body}</p>
</div>
);
}
});
var NoteForm = React.createClass({
getInitialState: function(){
return {'title': '', 'body': ''}
},
handleTitleChange: function(e){
this.setState({title: e.target.value});
},
handleBodyChange: function(e){
this.setState({body: e.target.value});
},
handleSubmit: function(e){
e.preventDefault();
var note = {
title: this.state.title,
body: this.state.body};
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: note,
success: function(data){
// Send data to NoteContainer?
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function(){
return (
<form>
<input
type='text'
placeholder='Title'
value={this.state.title}
onChange={this.handleTitleChange} />
<textarea onChange={this.handleBodyChange}>
{this.state.body}
</textarea>
</form>
);
}
});
What #xCrZx is suggesting is that you pull the state outside of the individual components and have a one or more top-level stores to maintain state. The simplest (i.e. "vanilla") example of this would be if your NoteContainer was a parent of NoteForm. Then you could simply pass a callback from NoteContainer to NoteForm:
var NoteContainer = React.createClass({
createNote: function() {
...
},
render: function() {
return (
...
<NoteForm createNote={this.createNote}>
...
);
}
});
var NoteForm = React.createClass({
props: {
createNote: React.PropTypes.func.isRequired
},
render: function() {
return (
...
onClick={this.props.createNote}
...
);
}
});
However, that of course only works if the relationship actually exists. Now let's take a look at Reflux, where you create central stores (and actions to go with them) to keep data, and components "listen" to the stores.
var NoteActions = Reflux.createActins([
'createNote',
'getNotes'
]);
var NoteStore = Reflux.createStore({
listenables: [NoteActions],
init: {
// Notes is an empty array by default
this.notes = [];
},
getInitialState: function() {
return {
notes: this.notes
};
},
onCreateNote: function(noteFormData) { ...POST here, save note JSON to this.notes on success... },
onGetNotes: function() { ..GET here for the initial load...}
});
var NoteForm = React.createClass({
render: function() {
...
onClick={NoteActions.createNote(noteFormData)}
...
}
});
var NoteContainer = React.createClass({
mixins: [Reflux.connect(NoteStore)],
componentDidMount: function() {
NoteActions.getNotes();
},
render: function() {
return: function() {
.. same that you have now using this.state.notes
}
}
});
Hope this is starting to make sense. Highly recommend looking through the Reflux (or Redux, similar but different) examples.
The best approach is to keep all notes in global state and add new entities there one by one when needed. It can be achieved with help of global stores, like Redux or Reflux.

State not passed into prop

I'm using React with Rails 4.
I've got the following:
<%= react_component('Box',url: "blah", pollInterval: 2000) %>
and then my components:
var Box = React.createClass({
getInitialState: function () {
return {data: []};
},
loadStuffFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
console.log(data);
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.loadStuffFromServer();
setInterval(this.loadStuffFromServer, this.props.pollInterval);
},
render: function() {
return (
<div>
<Widget office="1" data="{this.state.data}"/>
</div>
);
}
});
var Widget = React.createClass({
render: function () {
return (
<div>
{this.props.data}
</div>
)
};
});
For the Box, I can see using React DevTools for Chrome that the state is being set to the JSON returned by the url. However for the Widget component, when I try to echo the props out it literally returns: {this.state.data}
So the props is being set, but to a string instead of the JSON array?
Any property inside quotes is a string:
<Widget office="1" data="{this.state.data}"/>
To use a JavaScript expression, use only the curlies:
<Widget office="1" data={this.state.data}/>

reactjs Uncaught TypeError: Cannot read property 'map' of undefined

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')
);

Categories