File upload component sending two ajax requests - javascript

I'm new to react I've cobbled this react component together from different pieces i've found on the web. It's supposed to upload a file via ajax on submit. For some reason it's double firing. So when I hit submit my server is getting two requests instead of one.
Does anything stand out that a react beginner might miss?
/** #jsx React.DOM */
var $ = require("jquery")
var React = require('react');
var FileForm = React.createClass({
getInitialState: function() {
return {
myFileName: "",
myFileHandle: {}
};
},
handleChange: function(event) {
this.setState({
files: [event.target.files[0]] // limit to one file
});
},
handleSubmit: function(e) {
e.preventDefault();
var data = new FormData();
$.each(this.state.files, function(i, file) {
data.append('file-'+i, file)
})
$.ajax({
url: "/api/content/csv/upload.json",
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data) {
this.refs.fileInput.value = null
console.log(this.refs.fileInput)
console.log(data)
}.bind(this),
error: function(xhr, status, err) {
console.log(xhr)
console.log(status)
console.log(err)
}.bind(this)
})
},
render: function() {
return (
<form onSubmit={this.handleSubmit} method="POST" encType="multipart/form-data">
<input ref="fileInput" type="file" onChange={this.handleChange}/>
<input type="submit" value="Submit"/>
</form>
)
}
})
module.exports = FileForm

Related

Different Form Submit Actions for Different Forms

Super basic javascript question incoming...
I have two forms, one for uploading a file and one for providing text. I want to have a unique submit action for each of these forms. For the former, to upload the file, and for the latter, to serialize the form into JSON and POST it.
To attempt to accomplish this, I have one function called submit and another called submit2. The file upload form, which invokes submit works just fine.
The problem is with the second form, which invokes submit2. In particular, when I load the page, I get the following errors:
Query.Deferred exception: undefined is not a function (near '...$('form').submit2...').
TypeError: undefined is not a function (near '...$('form').submit2...')
Here's my HTML.
Upload an image
<form method="POST" enctype="multipart/form-data" action="upload">
<input id="img" name="file" type="file" accept=".jpeg,.jpg,.png">
<input class="btn btn-primary" type="submit" value="Submit">
</form>
Paste a URL
<form method="POST" name="urlForm" onclick="submit2()">
<input id="imgurl" name="url" type="text">
<input class="btn btn-primary" value="Submit">
</form>
And here's my javascript.
function ConvertFormToJSON(form){
var array = jQuery(form).serializeArray();
var json = {};
console.log(array)
jQuery.each(array, function() {
json[this.name] = this.value || '';
});
return json;
}
$(document).ready(function () {
var $status = $('.status');
$('#img').change(function (event) {
var obj = $(this)[0];
console.log(obj)
$status.html('');
if (obj.files && obj.files[0]) {
console.log(obj.files)
var fileReader = new FileReader();
fileReader.onload = function (event) {
$('.img-area').html(
`<img class='loaded-img' src='${event.target.result}' style="width:500px;height:500px;"/>`
);
}
fileReader.readAsDataURL(obj.files[0]);
}
});
$('#imgurl').change(function (event) {
var obj = $('#imgurl').val()
console.log(obj)
$('.img-area').html(
`<img class='loaded-img' src='${obj}' style="width:500px;height:500px;"/>`
);
});
$('form').submit(function (event) {
event.preventDefault();
var imageData = new FormData($(this)[0]);
console.log(imageData)
$status.html(
`<span class='eval'>Evaluating...</span>`
);
$.ajax({
url: 'some_api_endpoint',
type: 'POST',
processData: false,
contentType: false,
dataType: 'json',
data: imageData,
success: function (responseData) {
console.log(responseData)
if (responseData.error != null) {
$status.html(
`<span class='result failure'>Failed</span>`
);
} else {
$status.html(
`<span class='result success'>${responseData.message}</span>`
);
}
},
error: function () {
$status.html(
`<span class='eval'>Something went wrong, try again later.</span>`
);
}
});
});
$('form').submit2(function (event) {
event.preventDefault();
var json = ConvertFormToJSON($('form'))
console.log(json)
$status.html(
`<span class='eval'>Evaluating...</span>`
);
$.ajax({
url: 'some_api_endpoint',
type: 'POST',
processData: false,
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(json),
success: function (responseData) {
console.log(responseData)
if (responseData.error != null) {
$status.html(
`<span class='result failure'>Failed</span>`
);
} else {
$status.html(
`<span class='result success'>${responseData.message}</span>`
);
}
},
error: function () {
$status.html(
`<span class='eval'>Something went wrong, try again later.</span>`
);
}
});
});
});
Edit: Added the ConvertFormToJSON function for completeness, although I think that's orthogonal to the issue I'm facing.
Problem in there Jquery Object dont have submit2 method and when you want to access submit2 method this is return undefined and when call this is return undefined is not function.

React Tutorial and Sinatra API: Uncaught TypeError: this.props.data.map is not a function

I know, there are hundreds of questions with the same title, but nothing helped my get a solution for my problem. so I worked through the official react js tutorial and build a small API with sinatra to test things.
so everything works really good. except of one error I see in the console when submitting a new "Joke" (called them jokes instead of comments ;)) via AJAX.
app.js:66 Uncaught TypeError: this.props.data.map is not a function
This happens when I click on submit. I logged the state when submitting the form and everything seems to be okay (array with the temporary objects).
so the new Joke is being added and written to the database. It works but i don't know why I'm getting the Uncaught TypeError in the console.
thanks in advance!
var JokeBox = React.createClass({
loadJokesFromServer: 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)
});
},
handleJokeSubmit: function(joke) {
var jokes = this.state.data;
var tmpJoke = jQuery.extend({}, joke)
tmpJoke.id = new Date();
tmpJoke.likes = 0;
jokes.unshift(tmpJoke);
this.setState({data: jokes}, function(){
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: joke,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: jokes});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadJokesFromServer();
setInterval(this.loadJokesFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="jokes">
<h1>Jokes</h1>
<JokeForm onJokeSubmit={this.handleJokeSubmit} />
<JokeList data={this.state.data} />
</div>
);
}
});
var JokeList = React.createClass({
render: function() {
var jokeNodes = this.props.data.map(function(joke) {
return (
<Joke content={joke.content} key={joke.id} likes={joke.likes} />
);
});
return (
<div className="jokeList">
{jokeNodes}
</div>
);
}
});
var JokeForm = React.createClass({
getInitialState: function() {
return {content: ''};
},
handleContentChange: function(e) {
this.setState({content: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var content = this.state.content.trim();
if (!content) {
return;
}
this.props.onJokeSubmit({content: content});
this.setState({content: ''});
},
render: function() {
return (
<form className="jokesForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your Joke!"
value={this.state.content}
onChange={this.handleContentChange}
/>
<input type="submit" value="Send joke" />
</form>
);
}
});
var Joke = React.createClass({
render: function() {
return (
<div className="joke">
<p className="jokeContent">{this.props.content}</p>
<p className="jokeLikes">{this.props.likes}</p>
</div>
);
}
});
ReactDOM.render(
<JokeBox url="/api/jokes" pollInterval={2000} />,
document.getElementById('app')
);
// EDIT
So I played around with the sample tutorial repo from the tutorial. I log the data in the handleSubmit in the success function right before the state is set. And I figured out: my data is ja object of the actual new Joke, in the sample tutorial it is an array of all comments. How could this be? I can't find my mistake...
try
handleJokeSubmit: function(joke) {
let {data}= this.state;
$.post(this.props.url, joke, res=>{
this.setState({data:[res,...data]});
})
.fail( err=>alert('Error: ' + err.responseText));
}
///EDIT es5
handleJokeSubmit: function(joke) {
var data = this.state.data;
$.post(this.props.url, joke, function(res){
this.setState({data:[res].concat(data)});
}.bind(this))
.fail( function(err){alert('Error: ' + err.responseText)});
}

react.js call parent function from child

I know there are a few similar questions here and here but I am having a tough time understanding what is the correct thinking today on this and extrapolating it to my situation.
I have a simple app, ScoreBox has a ScoreList which has many Scores. I want to have a Score onClick call ScoreList handleScoreRemove. I am showing the full js file, but the most important lines are line 5 and line 77.
var Score = React.createClass({
removeRecord: function(e){
// How do I do this?
ScoreList.handleScoreRemove(e);
},
render: function() {
var team1_style = (this.props.team1_score >= this.props.team2_score) ?
{fontWeight: 'bold'} : {};
var team2_style = (this.props.team2_score >= this.props.team1_score) ?
{fontWeight: 'bold'} : {};
return (
<tr>
<td style={team1_style}>{this.props.team1_name}:</td><td style={team1_style}>{this.props.team1_score}</td>
<td style={team2_style}>{this.props.team2_name}:</td><td style={team2_style}>{this.props.team2_score}</td>
<td><a hef="#" id={this.props.id} onClick={this.removeRecord}>remove</a></td>
</tr>
);
}
});
var ScoreBox = React.createClass({
loadScoresFromServer: 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)
});
},
handleScoreSubmit: function(score) {
var scores = this.state.data;
// Optimistically set an id on the new score. 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.
score.id = Date.now();
var newScores = scores.concat([score]);
this.setState({data: newScores});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: score,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: scores});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadScoresFromServer();
setInterval(this.loadScoresFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="scoreBox">
<h1>Scores</h1>
<ScoreList data={this.state.data} />
<ScoreForm onScoreSubmit={this.handleScoreSubmit} />
</div>
);
}
});
var ScoreList = React.createClass({
handleScoreRemove: function(score) {
var scores = this.state.data;
var index_of_score = array.indexOf(score);
var newScores = scores.splice(index_of_score, 1);
this.setState({data: newScores});
$.ajax({
url: this.props.url + "/" + score[id],
dataType: 'json',
type: 'DELETE',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: scores});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
var scoreNodes = this.props.data.map(function(score) {
return (
<Score key={score.id} id={score.id} team1_name={score.team1_name} team1_score={score.team1_score} team2_name={score.team2_name} team2_score={score.team2_score} >
</Score>
);
});
return (
<div className="scoreList">
<table>
<tbody>
{scoreNodes}
</tbody>
</table>
</div>
);
}
});
var ScoreForm = React.createClass({
checkForCompleteForm: function(){
if (this.state.team1_name.length > 0 && this.state.team2_name.length > 0 && this.state.team1_score.length > 0 && this.state.team2_score.length > 0)
{
// enable the button
$("input[type=submit]").removeAttr('disabled');
}
},
getInitialState: function() {
return {id: '', team1_name: '', team1_score: '', team2_name: '', team2_score: ''};
},
handleChange : function (e) {
// this is a generic handle change function that uses the html id to set the state instead of
// having a bunch of if statements
var stateObject = function() {
var returnObj = {};
returnObj[this.target.id] = this.target.value;
return returnObj;
}.bind(e)();
// setState is async which makes this painful
// JCN - why when I pass checkForCompleteForm as 2nd param it doesnt work, but instead I need this
// function bind stuff... need to understand exactly what this is doing
this.setState( stateObject, function(){
this.checkForCompleteForm();
}.bind(this));
},
handleSubmit: function(e) {
e.preventDefault();
var team1_name = this.state.team1_name.trim();
var team1_score = this.state.team1_score.trim();
var team2_name = this.state.team2_name.trim();
var team2_score = this.state.team2_score.trim();
if (!team1_name || !team1_score ) {
return;
}
this.props.onScoreSubmit({team1_name: team1_name, team1_score: team1_score,team2_name: team2_name, team2_score: team2_score });
this.setState({team1_name: '', team1_score: '', team2_name: '', team2_score: ''});
},
render: function() {
return (
<form className="scoreForm" onSubmit={this.handleSubmit}>
<input
id='team1_name'
type="text"
placeholder="Team1 Name"
value={this.state.team1_name}
onChange={this.handleChange}
/>
<input
id='team1_score'
type="number"
placeholder="Team1 Score"
value={this.state.team1_score}
onChange={this.handleChange}
/>
<input
id='team2_name'
type="text"
placeholder="Team2 Name"
value={this.state.team2_name}
onChange={this.handleChange}
/>
<input
id='team2_score'
type="number"
placeholder="Team2 Score"
value={this.state.team2_score}
onChange={this.handleChange}
/>
<input type="submit" value="Post" disabled />
</form>
);
}
});
ReactDOM.render(
<ScoreBox url="/api/scores" pollInterval={2000} />,
document.getElementById('content')
);
You need to pass handleScoreRemove through props
var scoreNodes = this.props.data.map(function(score) {
return <Score
key={score.id}
id={score.id}
team1_name={score.team1_name}
team1_score={score.team1_score}
team2_name={score.team2_name}
team2_score={score.team2_score}
handleScoreRemove={this.handleScoreRemove.bind(this)}>
</Score>
}, this);
and in Score component call it like this
removeRecord: function(e) {
this.props.handleScoreRemove( /* add arguments what do you need */ );
},
call parent function from child
You don't (like what the other posts say). You pass handleScoreRemove into the child as a prop. Inside the child, you call the function by calling the prop. In the following, handleScoreRemove is passed as the onScoreRemove prop inside the child.
<Score ...stuff... onScoreRemove={this.handleScoreRemove}></Score>
You're already doing the same thing with the ScoreBox (parent) and ScoreForm (child). You're passing a reference of handleScoreSubmit as onScoreSubmit prop in the child.
<ScoreForm onScoreSubmit={this.handleScoreSubmit} />
You should pass handleScoreRemove as a prop to Score:
In ScoreList:
var scoreNodes = this.props.data.map(function(score) {
return (
<Score key={score.id} (...) handleScoreRemove={this.handleScoreRemove}>
</Score>
);
});
In Score:
removeRecord: function(e){
this.props.handleScoreRemove(this);
}

ReactJS tutorial- Comment form not posting to local server

I'm trying to set up a local server that hosts a comment submission system (as per the React tutorial). In the CommentForm class, I want to have the form handle a comment submission, which uses a POST request to modify a local file called comments.json. This POST request isn't working. Can anyone figure out why? I have the following code (I have excluded the Comment and CommentList classes to reduce clutter but I will include them if it will be helpful). The console.log line that prints "submitting" is never executed:
var Comment = React.createClass({
// code excluded for brevity
});
var CommentForm = React.createClass({
handleSubmit: function(e) {
console.log("submitting");
e.preventDefault();
var author = this.refs.author.value.trim();
var text = this.refs.text.value.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.value = '';
this.refs.text.value = '';
return;
},
render: function() {
return (
<form className = "commentForm">
<input type="text" placeholder="Your name" ref="author"/>
<input type="text" placeholder="Say something..." ref="text"/>
<input type="submit" value="Post"/>
</form>
);
}
});
var CommentList = React.createClass({
// code excluded for brevity
});
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)
});
},
getInitialState: function() {
return {data: []};
},
handleCommentSubmit: function(comment) {
$.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)
});
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
console.log("rendering box");
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval = {2000} />,
document.getElementById('content')
);
You forgot to bind the onSubmit event of the form the handler.
<form className="commentForm" onSubmit={this.handleSubmit}>

Understanding how Ajax success callback updates state in this ReactJS example

I am working thought the Reactjs Tutorial. I am trying to understand how the CommentForm component submits (or updates the server) with the data it has collected via passing it up to the CommentBox.
Here are the two components that work for reference:
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = React.findDOMNode(this.refs.author).value.trim();
var text = React.findDOMNode(this.refs.text).value.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
React.findDOMNode(this.refs.author).value = '';
React.findDOMNode(this.refs.text).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="text" />
<input type="submit" value="Post" />
</form>
);
}
});
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) {
$.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">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
My source of confusion comes from handleCommentSubmit in the CommentBox component, specifically the Ajax success callback.
Since we set data: comment, data is now merely the comment the form collected. But on success we take data and do this.setState({data: data});. Wouldn't that be setting the state to only one comment (the one we collected in the form?). Wouldn't we need to pull from the server to get all of the data, including the POST we just made with something like loadCommentsFromServer? How does this work?
Since we set data: comment, data is now merely the comment the form
collected. But on success we take data and do this.setState({data:
data});. Wouldn't that be setting the state to only one comment (the
one we collected in the form?).
No, in the example, the comment passed in to the function is setting the data property for the ajax request. The data parameter in the success callback is the data from the ajax response.
So, here they are setting the data state property to whatever the server responds with. I think the example assumes that the server is reflecting the same comment, but this allows the server to save the comment during the HTTP call.

Categories

Resources