Updating state with React - ajax

I am creating a blog using React, MongoDB, Express and Node. I have three components: App, List, and Item. The item is a blog post; the list is a list of the blog posts, and the app includes a place to enter text and submit it. I will eventually add more functionality, but I want to determine if I am adhering to best practices for React (I doubt I am).
So in App, I getInitialState with an array of posts (posts) and a string of text for the input (postbody). I used the componentDidMount to make an AJAX GET request to my database, so the user can see all the posts.
To handle entering text I just made a simple handleChange function which updates the state of postbody.
I also have a handleClick function, which grabs this.state.postbody and then POSTs it database. However the same function also makes a separate GET request of the database to update the state of the posts array. This doesn't seem right! Shouldn't that be handled some other way and updated automatically? * This is the primary question I have. *
Also, please let me know if I need to break the components down further, or if I am violating best practices using React (e.g. changing state in the wrong place, or using props incorrectly).
var React = require('react');
var Item = React.createClass({
render: function() {
return (
<div>
<h2>{this.props.postbody}</h2>
</div>
)
}
})
var List = React.createClass({
render: function() {
return (
<div>
{this.props.array.map(function(post) {
return (
<Item postbody={post.postbody}></Item>
)
})}
</div>
)
}
})
var App = React.createClass({
getInitialState: function() {
return {
posts: [],
postbody: ''
}
},
componentDidMount: function() {
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState({posts: data});
}.bind(this)
})
},
handleClick: function() {
event.preventDefault();
var blogPost = this.state.postbody;
$.ajax({
type: 'POST',
url: '/api/blogPosts',
data: { postbody: blogPost }
});
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState({posts: data});
}.bind(this)
})
},
handleChange: function(event) {
this.setState({ postbody: event.target.value})
},
render: function() {
return (
<div>
<form action="/api/blogPosts" method="post">
<input onChange={this.handleChange} type="text" name="postbody"></input>
<button type="button" onClick={this.handleClick}>Submit</button>
</form>
<List array={this.state.posts} />
</div>
)
}
})

Well, actually since you only have an Add api call, you could do this. You just push a blogPost to the array of posts in a post request. Also, you might want to use the form's onSubmit.
var React = require('react');
var Item = React.createClass({
render: function() {
return (
<div>
<h2>{this.props.postbody}</h2>
</div>
)
}
})
var List = React.createClass({
render: function() {
return (
<div>
{this.props.array.map(function(post) {
return (
<Item postbody={post.postbody}></Item>
)
})}
</div>
)
}
})
var App = React.createClass({
getInitialState: function() {
return {
posts: [],
postbody: ''
}
},
componentDidMount: function() {
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState({posts: data});
}.bind(this)
})
},
handleSubmit: function() {
event.preventDefault();
var blogPost = this.state.postbody;
$.ajax({
type: 'POST',
url: '/api/blogPosts',
data: { postbody: blogPost },
success:function(){
this.setState({posts: Object.assign([],this.state.posts.push({postbody:postbody}))});
}.bind(this)
});
},
handleChange: function(event) {
this.setState({ postbody: event.target.value})
},
render: function() {
return (
<div>
<form action="/api/blogPosts" method="post" onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} type="text" name="postbody"></input>
<input type="submit" value="Submit" >Submit</button>
</form>
<List array={this.state.posts} />
</div>
)
}
})
The idea is that you maintain a store, which exists outside of the components and you manage the store through various events/actions. In this case, the app is relatively simple, so we can afford to have the "store" as a state prop and change it through the POST XHR.
However, as your app logic keeps increasing, have the posts data in a store. And have actions CRUD data into the store. And add a listener on the store to publish updates to the React component and update it, using a state variable.
Whenever something changes in the store, change the state variable from within the store using a listener(which passes data to and fro between stores,components and api calls) and your component updates. This is how Flux works. There are other ways to do this. Just a start.

Related

How to call an action method upon selection from the <select> dropdwon in ASP.NET Core 6 MVC?

I created a dropdown using the <select> HTML element. Now I want to call an action after user makes a selection from the list.
<select name="ddAircraft" id="ddAircraft" class="form-control form-select-sm form-select"
asp-items="#(new SelectList(ViewBag.ddaircraft,"id","name"))">
</select>
I would also like to know if user enter a value in a input box. Then I want to run a Javascript method. How I can do that?
I tried to do onClick but I am getting usual error.
It would help if you could show the details of your error,
I Tried with the codes below:
#{
var sel = new List<SelectListItem>()
{
new SelectListItem(){Text="1",Value="1" },
new SelectListItem(){Text="2",Value="2" },
new SelectListItem(){Text="3",Value="3" }
};
}
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$(function () {
$('#selectlist').change(function () {
window.location.href = "Privacy";
})
});
</script>
<script>
$(function () {
$('#input').change(function () {
window.location.href = "Privacy";
})
});
</script>
<input id="input" value=""></input>
<select id="selectlist" asp-items=sel></select>
The result:
To pass the selected item value ,you could try :
<script>
$(function () {
$('#selectlist').change(function () {
window.location.href = "Home/Privacy?sel=" + $(this).val();
})
});
</script>
The result:
if you want to make a post request, you could try with ajax as below:
<script>
$(function () {
$('#selectlist').change(function () {
var sel = $(this).val();
var input = document.getElementById("input").value;
$.ajax({
url: "Home/Test",
contentType: "application / json; charset = utf - 8",
type: "post",
data: JSON.stringify({
sel: sel,
input: input
}),
datatype: "json",
success: function (data) {
console.log(data);
}
})
})
});
</script>
The result:

React, update DOM elements on submit on posted data without refresh

I am playing around with React and Rails and I am working on a function to submit a post to the server, which works as intended, but then re-renders the DOM elements without refreshing the page.
I am aware I am missing a function that would get the new JSON object and map it over the DOM again but am unsure how to properly formulate this.
From my research, I would have to do a new $.ajax request on the '/posts' route, which is already set up as a JSON only render pulling all posts.
My code is below:
var New = React.createClass ({
handleClick(e) {
e.preventDefault();
let text = this.refs.text.value;
$.ajax({
url: '/new',
type: 'POST',
data: { post: { text: text} },
success: (post) => {
this.handleSubmit(post);
}
});
},
handleSubmit(post) {
console.log(post);
this.refs.text.value = ""
},
render: function() {
return( <div>
<div className="post-div">
<form>
<input className="form-control" ref='text' placeholder='Post Something' />
<button className="btn btn-primary" onClick={this.handleClick}>Submit</button>
</form>
</div>
</div>
)
}
})
and the other react file:
var Post = React.createClass ({
render: function() {
return
<div className="text-box">
<p className="text">{this.props.text}</p>
<div className="text-stamps">{this.props.timestamps}</div>
</div>;
}
})
Any help would be appreciated. Thank you.
The ReactJS introductory tutorial has exactly the same functionality explained in a great detail.
I'd definitely direct you to look at it here. And here's the section that directly does what you want. POSTing a comment to the server and re-rendering it back to the client. And it also shows how to optimistically render the new comment in the UI.
Update: Here is how you can do it. The comments are the place where you will add hooks into the server call.
var posts = [
{id: 1, text: "iPhone 7 release date"},
{id: 2, text: "Samsung 7 release date"}
];
var Post = React.createClass({
render: function(){
return (<p>{this.props.text}</p>);
}
});
var PostList = React.createClass({
render: function() {
var response = this.props.posts.map(function(post){
return (<Post text={post.text}></Post>);
});
return (<div>
{response}
</div>);
}
});
var PostForm = React.createClass({
getInitialState: function() {
return {newPost: ""};
},
handleTextChange: function(e){
this.setState({newPost: e.target.value});
},
onSubmit: function(e) {
e.preventDefault();
var newPost = this.state.newPost.trim();
if(!newPost) {
return ;
}
this.props.onAddition(newPost);
this.setState({newPost: ""})
},
render: function() {
return (
<form onSubmit={this.onSubmit}>
<h4>Add some post here</h4>
<input type="text" value={this.state.newPost} onChange={this.handleTextChange}></input>
<input type="submit" value="Add Post" />
</form>
);
}
});
var Page = React.createClass({
getInitialState: function() {
return {posts: posts};
},
onAddition: function(newPost) {
console.log("Adding new: ",newPost);
posts.push({id: Date.now(), text:newPost});
//POST to the server here and set the state if successful
this.setState({posts: posts});
},
componentDidMount: function() {
//Load from there server here
//And keep reloading it from the server every few seconds
},
render: function() {
return (
<div>
<PostForm onAddition={this.onAddition}/>
<PostList posts={this.state.posts}/>
</div>
);
}
});
var div = document.getElementById("app");
ReactDOM.render(<Page/>, div);
And here's a JSBin for this. https://jsbin.com/pokoja/edit?html,js,output
A slight modification to Kumar's answer because his solution mutates state and might be difficult for people who do not use the getInitialState function.
onAddition = (newPost) => {
const posts = [...this.state.posts]
posts.push({
_id: Date.now,
text: newPost.post
})
this.setState({
posts: posts,
postForm: {
post: ''
}
})}
In this instance, the contents of posts in state are copied (using the spread operator) and assigned to a posts constant. Then the new data is pushed into the constant, which is then set as the new state (along with the copied contents of the existing state).

React Tutorial - Page Refreshing after Comments Added

I'm working through the official React tutorial and having a little trouble. When I add a comment I expect the comment to appear in the view, and for a split second it does, but then the page refreshes and the comment's gone.
On a related matter (and really just a request for a little FYI as I'm still learning AJAX), the code is supposed to add the comment to the JSON. I'm presuming that this wouldn't work on the Plunker but is there enough code there to actually update a JSON if the page is live?
Thanks for any help! Plunker link and code follows:
https://plnkr.co/edit/p76jB1W4Pizo0rDFYIwq?p=preview
<script type="text/babel">
// To get started with this tutorial running your own code, simply remove
// the script tag loading scripts/example.js and start writing code here.
var CommentBox = React.createClass({
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)
});
},
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)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
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 };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
ReactDOM.render(
<CommentBox url="comments.json" pollInterval={2000} />,
document.getElementById('content')
);
</script>
As you said, your problem is that the information in the json file is static (see last paragraph), so every time the comments are refreshed, you lose the new one. The way you could handle it is using the json file during the first load and then just prevent refreshing them, just adding the new ones to the comment box state (after all this is just a example and you just want to see some eye candy, don't you?).
Checking the browser's console you can see that your AJAX request to store the new file is failing, you cannot update it on Plunker, that file is immutable.

Redirecting a successful ajax call with React-Router

I'm getting Cannot read property 'router' of undefined with the code below.
this.transitionTo('home') is bugging, and I'm guessing it's because of the context of this. I tried binding the ajax call to this, and it didn't help either.
Any thoughts on how to simply redirect to either 'home' or '/' after this successful ajax call?
I've tried both the Navigation (transitionTo) and the History (this.pushState) mixins.
Edit: In the meantime I found a hacky working solution that uses a page refresh. Within the ajax .done section:
history.pushState({},'','/')
window.location.reload()
Code:
var Router = ReactRouter;
var Route = ReactRouter.Route;
var Routes = ReactRouter.Routes;
var Navigation = ReactRouter.Navigation;
var History = ReactRouter.History;
var Login = React.createClass({
mixins: [ History ],
mixins: [ Navigation ],
getInitialState: function(){
return{
email: "",
password: ""
}
},
submit: function(e){
e.preventDefault()
var data = {
email: this.state.email,
password: this.state.password,
}
// Submit form via jQuery/AJAX
$.ajax({
type: 'POST',
url: '/sessions',
data: data
})
.done(function(data) {
App.logIn(data.email)
alert('login successful!')
this.transitionTo('home')
// this.history.pushState(null, '/home')
// this.pushState(null, '/home')
})
.fail(function(data) {
alert('No Such Email or Incorrect Password')
});
},
handleEmailChange: function(event) {
this.setState({email: event.target.value});
},
handlePasswordChange: function(event) {
this.setState({password: event.target.value});
},
render: function(){
return(
<div>
Login To Your Account
<br/>
<form onSubmit={this.submit} >
Email: <input label="Email:" onChange={this.handleEmailChange} />
<br/>
Password: <input label="Password:" type="password" onChange={this.handlePasswordChange} />
<button type="submit">Submit</button>
</form>
</div>
)
}
})
Your context is changing in the inner function. The easiest way (in my opinion) to fix this is to place var _this = this in your outer function, then use _this.transitionTo in your callback.

React.js how to I go about getting data from my form and posting it

I'm currently learning React.js and am having trouble using a jquery or ajax call that returns the information in my form to a post. Basically what ever info is in the form, after submit, make a post of the data in a tag.
Here is my code:
var BasicInputBox = React.createClass({
render: function() {
return (
<div>
<label>{this.props.label}</label>
<br/>
<input type="text" onChange={this.props.valChange} value={ this.props.val} />
<br/>
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div>
<label>Have a question?</label>
<br/>
<textarea type="text" onChange={this.props.valChange} value={ this.props.val} />
<br/>
</div>
);
}
});
var SubmitButton = React.createClass({
render: function() {
return (
<div>
<button type="submit" onClick={this.props.submit}>
Submit
</button>
</div>
);
}
});
var Contact = React.createClass({
getInitialState: function() {
return {}
},
submit: function(e) {
e.preventDefault()
console.log(this.state);
this.setState({
name: "",
email: "",
comment: ""
})
},
nameChange: function(e) {
this.setState({
name: e.target.value
})
},
emailChange: function(e) {
this.setState({
email: e.target.value
})
},
commentChange: function(e) {
this.setState({
comment: e.target.value
})
},
render: function() {
return (
<form>
<BasicInputBox label="Name:" valChange={this.nameChange} val={this.state.name}/>
<BasicInputBox label="Email:" valChange={this.emailChange} val={this.state.email}/>
<CommentBox valChange={this.commentChange} val={this.state.comment}/>
<SubmitButton submit={this.submit}/>
</form>
);
}
});
React.render(
<Contact></Contact>,
document.body
);
As #BinaryMuse noted the problem here is that your submit method is not really doing any submitting. You mentioned that the way you want to do this is via AJAX, and thus you need to 1) include jQuery (or Zepto) on your page, and 2) make the ajax call. Here is one way to accomplish the second part:
1)
First, you don't really need to provide the submit method as a property to the submit button. When the submit button is clicked inside a form, it will trigger the form's onSubmit event, so you can simply attach the this.submit method there.
Also, you don't really need I don't think to create a separate component for the Submit button. That kind of granularity may not be justified here since you can accomplish the same thing with far fewer lines of code. So I'd remove your SubmitButton component and update your Contact component render function to be:
render: function(){
return (
<form onSubmit={this.submit}>
<BasicInputBox label="Name:" valChange={this.nameChange} val={this.state.name}/>
<BasicInputBox label="Email:" valChange={this.emailChange} val={this.state.email}/>
<CommentBox valChange={this.commentChange} val={this.state.comment}/>
<button type="submit">Submit</button>
</form>
);
}
2) Next you can change your submit method in this way, adding an AJAX call. Depending on the details of the server/API to which you are sending the form you may need to modify the AJAX call a bit, but what I have put here is a fairly generic form that has a good chance of working:
submit: function (e){
var self
e.preventDefault()
self = this
console.log(this.state);
var data = {
name: this.state.name,
email: this.state.email,
comment: this.state.comment
}
// Submit form via jQuery/AJAX
$.ajax({
type: 'POST',
url: '/some/url',
data: data
})
.done(function(data) {
self.clearForm()
})
.fail(function(jqXhr) {
console.log('failed to register');
});
}
Note: that I also encapsulated the code you had previously for clearing the form inside its own function, which is called if the AJAX call returns a success.
I hope this helps. I put the code in a jsFiddle where you could test it a bit: https://jsfiddle.net/69z2wepo/9888/

Resources