Can't get data from React state - javascript

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

Related

Display array element ReactJS

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.

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

Update/Append component into another component in React

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

can't get data from mongodb in React

i have an app which loads data from mongodb to list component like this
React.render(<ProductsList url="http://localhost:3000/data"/>, document.getElementById('products'));
and then i try make a http get call to ge the data like this
var ProductsList = React.createClass({
loadData: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
<ProductsList url={data}/>
},
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}
});
},
render: function() {
this.loadData();
var products = this.props.url.map(function(product) {
return (
<li key={product._id}>
<Product data={product} />
</li>
)
});
return (
<ul className="clearfix">
{products}
</ul>
);
}
});
but when i load the app i get this error Uncaught TypeError: this.props.url.map is not a function what could the problem be ?
map is not a function for strings, check the documentation for array map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Furthermore, not related to your error, but your function loadData returns undefined, because you are just making the ajax call. In order to make this work, you need to use the state object of the react component call this.setState() in your success/error hooks.
it's seems like you are not clear about the props and state of React you have to follow in below mentioned way
var ProductsList =React.createClass({
getInitialState:function()
{
return{
url:[]
}
},
componentDidMount:function()
{
var that=this;
$.ajax({
url: 'https://api.github.com/users/mralexgray/repos',
dataType: 'json',
success: function(data) {
that.setState({url:data});
},
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}
});
},
ChangeContent:function()
{
this.setState({content:"change Content"});
},
render:function()
{
var Products = this.state.url.map(function(product) {
return (
<li key={product._id}>
{product.full_name}
</li>
)
});
return(
<div>
{Products}
</div>
)
}
});
React.render(
<ProductsList />,
document.getElementById('example')
);
Demo

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

Categories

Resources