I am new to react and am in learning phase. What i am trying to do is to have a todo list with a due date.
The following are my React components:
var TaskTitle = React.createClass({
render: function() {
return (
<div className="taskTitle">
<textarea ref="tskTitle" onBlur={this.props.handleTitleBlur} onChange={this.props.setTaskTitle} name="tasktitle[]" id="tasktitle" value={this.props.taskTitle} className="tasktitle" ref="tskTitle"></textarea>
</div>
);
}
});
Calendar Component
var CalendarWithTime = React.createClass({
componentDidMount: function() {
this.myCalendarInput = $(ReactDOM.findDOMNode(this.refs.showCal));
var self = this;
this.CalendarWithTime = this.myCalendarInput.datetimepicker({
format:'Y-m-d H:m',
onShow:function(){
self.props.calendarOpen(); //Updating parent state
},
onClose:function(){
self.props.calendarClosed();//Updating parent state
},
onChangeDateTime:function(dp,input){
self.props.dateChange(dp,input); //Updating parent state
}
});
},
render: function() {
return (
<div className="datetimeWrapper">
<div className="datetime clearfix">
<div ref="showCal" className="showCalendar">
</div>
</div>
</div>
);
}
});
Single Todo component
var ToDo = React.createClass({
handleCalendarOpen:function(){
this.setState({ canUpdate:false });
},
handleCalendarClose:function(){
this.setState({ canUpdate:true });
this.updateTaskData();
},
stateUpdate:function(obj){
for(var key in obj){
this.state[key] = obj[key];
}
},
getInitialState: function() {
return {taskID:null,taskTitle:null,taskDueDateTime:null};
},
setTaskTitle:function(event){
this.setState({ taskTitle: event.target.value,canUpdate:false });
},
componentWillReceiveProps:function(newProps){
this.setState({
taskTitle: newProps.tskTitle,
taskDueDateTime:newProps.dueDate,
canUpdate:true
});
},
updateTaskTitle:function(event){
this.setState({taskTitle:event.target.value,canUpdate:false});
this.updateTaskData();
},
updateTaskData:function(){
var self =this;
setTimeout(function(){
$.ajax({
url: $('meta[name="baseurl"]').attr('content')+'api/save_task',
dataType: 'json',
method:'post',
data:self.state,
cache: false,
success: function(data) {
if(data.status){
self.setState({canUpdate:true});
}
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.dataURL, status, err.toString());
}.bind(this)
});
},500);
},
componentDidMount:function(){
var seldate=this.props.tskDueDate,dueDate=null,dueTime=null,dueLabel=null;
if(seldate){
dueDate = moment(seldate,'YYYY-MM-DD hh:mm a').format('YYYY-MM-DD'),
dueTime = moment(seldate,'YYYY-MM-DD hh:mm a').format('hh:mm a'),
dueLabel = moment(seldate,'YYYY-MM-DD hh:mm a').format('MMM Do, hh:mm a');
}
this.setState({
taskTitle:this.props.tskTitle,
taskDueDateTime:this.props.tskDueDate,
canUpdate:true,
});
},
render: function() {
return (
<tr className="resp-table">
<td><TaskTitle taskTitle={this.state.taskTitle} setTaskTitle={this.setTaskTitle} canUpdate={this.state.canUpdate} handleTitleBlur={this.updateTaskTitle} onUpdate={this.stateUpdate} key={this.state.taskID +"0"+this.state.taskID} taskID={this.props.tskID} /></td>
<td><CalendarWithTime calendarClosed={this.handleCalendarClose} calendarOpen={this.handleCalendarOpen} dateChange={this.handleDateChange} dueDateTime={this.state.taskDueDateTime} dueDate={this.state.taskDueDate} dueTime={this.state.taskDueTime} dueLabel={this.state.taskDueLabel} key={this.state.taskID +"2"+this.state.taskID} /></td>
</tr>
);
}
});
//Load Previous tasks if any
var PrevTaskList = React.createClass({
loadTaskList:function(){
$.ajax({
url: $('meta[name="baseurl"]').attr('content')+'api/get_task/',
dataType: 'json',
method:'post',
data:{wsID:$('body').attr('data-wsid')},
cache: false,
success: function(data) {
if(data.status){
this.setState({taskListData: data.taskList});
}
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.dataURL, status, err.toString());
}.bind(this)
});
},
componentWillMount: function() {
this.loadTaskList();
setInterval(this.loadTaskList,this.props.pollInterval); // to render any new task added by other users
},
render: function() {
if(this.state.taskListData){
var TaskNodes = this.state.taskListData.map(function(task){
return(
<ToDo key={task.task_id} tskID={task.task_id} tskTitle={task.task_title} tskDueDate={task.task_due_date} />
);
},this);
return (
<table ref="taskTable" id="tasktable">
<tbody >
{TaskNodes}
</tbody>
</table>
);
}
}
});
Rendering Final Component
ReactDOM.render(<PrevTaskList pollInterval="5000"/>,$('#taskWrapper').get(0));
Now how can i add a new task on pressing enter key in the text area(TaskTitle) component? i am confused here of where to provide the event and how to render a
empty task. Is my component modelling even right? Any help would be greatly appreciated.. Waiting for help.. hopefully
Related
UPD: How can I display elment of the array in my component (WeatherCityName - h2)? It can be seen that the array is loaded, but when I point out the property - an error occurs, it may be a problem in the syntax?
var WeatherBox = React.createClass({
handleWeatherSubmit: function(text) {
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: 'cityName=' + text,
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: []};
},
render: function() {
return (
<div className="wetherBox">
<h1> Weather</h1>
<WeatherForm onWeatherSubmit={this.handleWeatherSubmit} />
<WeatherCityName data={this.state.data[0]} />
<WeatherList data={this.state.data} />
</div>
);
}
});
var WeatherCityName = React.createClass({
render: function(){
return(
<h2>{this.state.data[0].cityName}</h2>
);
}
});
Sure, just put <h2>{weatherItem.cityName}</h2> directly in the <div>.
var WeatherList = React.createClass({
render: function() {
var weatherNodes = this.props.data.map(function(weatherItem) {
return (
<div className="city-item-with-title">
<h2>{weatherItem.cityName}</h2>
<WeatherItem
cityid={weatherItem.cityid}
type={weatherItem.type}
src={weatherItem.src}
temp={weatherItem.temp}
tempFrom={weatherItem.tempFrom}
tempTo={weatherItem.tempTo}
key={weatherItem.id}
/>
</div>
);
});
return (
<div className="weatherList">
{weatherNodes}
</div>
);
}
});
Edit: I'm not sure which weather item you want to use as a title, perhaps this.props.data[0]?
Edit 2: Just assumed you want to use the first weather item.
Edit 3: One title for each weatherItem, grouped inside a div together with the corresponding weatherItem.
I have the following ReactJS code:
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>
);
}
});
And in CommentBox....
var CommentBox = React.createClass({
loadCommentFromServer: 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: []
};
},
componentDidMount: function() {
this.loadCommentFromServer();
setInterval(this.loadCommentFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className = "commentBox" >
<h1> Comments </h1>
<CommentList data ={ this.state.data } />
<CommentForm />
</div>
);
}
});
In getInitialState(), I already assign value for data and in CommentList also add property data which get value from state.
When I try to run, I got this error:
Cannot read property 'map' of undefined in CommentList.
It looks like you simply aren't getting the data you want back from the jQuery.ajax.
success: function(data) {
// before setting state, log here and find out what data is
// it could need to be turned into JSON? data = data.toJSON();
// or simply this to be safe
data = data || [];
this.setState({
data: data
});
}
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)});
}
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);
}
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}>