Iam new to ReactJS and I am working on a filterable gallery but now Iam looking at an example of thinking in ReactJS and I saw that they are building different classes for each component. I didn't do that but now Iam trying to do this, First my code looked like this: `
var SearchBar = React.createClass({
getInitialState() {
return { text:'', array: this.props.array};
},
handleChange(event) {
var array = this.filterList(event.target.value);
this.setState({ text: event.target.value, array: array });
return this.state.text;
},
render() {
var arrayComponents = this.state.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
return <div>
<h1>Hello, {this.props.name}</h1>
<p>{this.state.text}</p>
<input type="text" onChange={this.handleChange} />
<ul>
{arrayComponents}
</ul>
</div>;
},
filterList (filterText) {
var updatedList = this.props.array,
filterTextLength = filterText.length;
return updatedList.filter(function(item){
var splitName = item.name.toLowerCase().slice(0, filterTextLength);
var lowerCaseFilterText = filterText.toLowerCase();
return splitName === lowerCaseFilterText;
});
}
});
Now I want to create an other Class of ImageList which has to include the var arrayComponents but if i do this:
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
and than in the render function add <ImageList array={array}/> instead of <ul>{arrayComponent}</ul> it throws me the error that Cannot read property 'map' of undefined How do I pass the state of array into that ImageList class.
Here is a codepen: LINK
I've made the following changes in your code: http://codepen.io/PiotrBerebecki/pen/zKRAGZ
1) Pass the state of array into that ImageList class
<ImageList array={this.state.array} />
2) Add a return statement in the render method of ImageList
// Add return
return (
<ul>
{arrayComponents}
</ul>
);
3) Add key attribute to the li tag when using map method:
var arrayComponents = this.props.array.map(function(photo, index) {
// -------------------------------
// Add index to the li tag
// ----------------vvvvvvvvvvv
return <li key={index} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
React Docs: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
You need to pass the current state of the array. So your component declaration should look like this:
<ImageList array={this.state.array}/>
I glance three problems here.
First,
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
you should say:
return (<ul>
{arrayComponents}
</ul>)
Second, when you use dynamically generated codes, you should add a key prop to it:
var arrayComponents = this.props.array.map(function(photo) {
return <li key={SOME_KEY} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
Third, in your codepen when you say :
<ImageList array={array}/>
you are referencing the global variable array (you declare it on the top of your code), do you mean:
<ImageList array={this.state.array}/>
Learning react and whilst I understand the life cycle of a component, I don't understand that whilst my JSON call is successful it does not render the list of items I've pulled initially. It does render when I add a new item to the list, but that requires me to click the edit button and add a new element.
var InterBox = React.createClass({
getInitialState: function(){
return {
"content" : [],
"editing" : false,
"pulled" : false
}
},
componentDidMount: function(){
var url = "http://jsonplaceholder.typicode.com/posts";
var tempdata = this.state.content;
$.getJSON(url, function(data){
for (var i = 0; i < data.length; i++) {
var element = data[i];
tempdata.push(element.body);
console.log(tempdata);
}
});
this.setState({"content":tempdata});
this.forceUpdate();
},
addContent: function(e){
var currentContent = this.state.content;
currentContent.push(this.refs.content.value);
this.refs.content.value = "";
this.setState({"content" : currentContent, "editing" : false});
this.render();
},
changeMode: function(e){
this.setState({"editing": true});
this.render();
},
render: function(){
if (this.state.editing)
return(
<div>
<h1>{this.props.title}</h1>
<input type="text" ref="content"/>
<button onClick={this.addContent}>Add</button>
</div>
);
else{
var items = [];
for (var i = 0; i < this.state.content.length; i++) {
var element = this.state.content[i];
items.push(<li>{element}</li>);
}
return(
<div>
<h1>{this.props.title}</h1>
<button onClick={this.changeMode}>Edit</button>
<ul>
{items}
</ul>
</div>
);
}
}
});
ReactDOM.render(
<InterBox title="Hello"></InterBox>,
document.getElementById('main')
);
This is the initial output
This what it should look like
1.) Since $.getJSON func is async, therefore you should wait response, and after that invoke setState in callback.
let {content} = this.state;
$.getJSON(url,data=>{
let result = data.map(e=>e.body);
this.setState({content:[...content, ...result]});
});
2.) You don't need forceUpdate() and render(), because setState invoke rendering component for you.
3.) You shouldn't mutate your state by push().It will causing problems and bugs. Instead you may use spread operator or concat, etc
//concat returns new array
let {content} = this.state;
this.setState({content:content.concat(result)});
//spread operator
let {content} = this.state;
this.setState({content:[...content, ...result]});
4.) If you don't want to use arrow func in callback, you should use bind(this), otherwise you will have undefined for setState in callback
let {content} = this.state;
$.getJSON(url,function(data){
let result = data.map(e=>e.body);
this.setState({content:content.concat(result)});
}.bind(this));
Dont call render function manually i.e. supposed to call by react.
2.You were setting setState outside your AJAX call response.
var InterBox = React.createClass({
getInitialState: function(){
return {
"content" : [],
"editing" : false,
"pulled" : false
}
},
componentDidMount: function(){
var url = "http://jsonplaceholder.typicode.com/posts";
var tempdata = this.state.content;
$.getJSON(url, function(data){
for (var i = 0; i < data.length; i++) {
var element = data[i];
tempdata.push(element.body);
console.log(tempdata);
}
this.setState({"content":tempdata});
});
},
addContent: function(e){
var currentContent = this.state.content;
currentContent.push(this.refs.content.value);
this.refs.content.value = "";
this.setState({"content" : currentContent, "editing" : false});
},
changeMode: function(e){
this.setState({"editing": true});
},
render: function(){
if (this.state.editing)
return(
<div>
<h1>{this.props.title}</h1>
<input type="text" ref="content"/>
<button onClick={this.addContent}>Add</button>
</div>
);
else{
var items = [];
for (var i = 0; i < this.state.content.length; i++) {
var element = this.state.content[i];
items.push(<li>{element}</li>);
}
return(
<div>
<h1>{this.props.title}</h1>
<button onClick={this.changeMode}>Edit</button>
<ul>
{items}
</ul>
</div>
);
}
}
});
ReactDOM.render(
<InterBox title="Hello"></InterBox>,
document.getElementById('main')
);
There is no need for a this.forceUpdate() or this.render() call. The screen will be refreshed, i.e. render() called by React after this.setState().
The reason the data isn't being shown is because you're calling this.setState() immediately after calling $.getJSON() and not in the success callback of getJSON().
This should work:
$.getJSON(url, function(data){
for (var i = 0; i < data.length; i++) {
var element = data[i];
tempdata.push(element.body);
console.log(tempdata);
this.setState({"content":tempdata});
}
});
I'm new to React and am having some trouble getting it to work.
I have a react class that puts a bunch of JSON in the store as an object, a PushNotification with two elements: pushId and count. So, the store should have a list of PushNotifications.
However, when I try and display that information to the screen, it only outputs one of them.
My React code is:
socket.onmessage = function(event) {
console.log("Received message" + event.data.toString());
store.dispatch(receivedPushNotification(event.data));
};
var App = React.createClass({
render: function () {
var pushNotifications = _.map(this.props.pushNotifications, function(value, key, notification) {
var percentage = (notification.count / 50) * 100;
return (
<div className="row" key={notification.pushid}>
<div className="col-sm-12">
<Card>
<h1 className="marB15">{notification.pushid}</h1>
<div className="clearfix">
<div className="progress progress-striped active marB10">
<div className="progress-bar" style={{'width': percentage + '%'}}></div>
</div>
<div className="pull-right">
<p>Total: {notification.count}</p>
</div>
</div>
</Card>
</div>
</div>
)
});
}
});
My Reducer is:
var pushNotificationDefaultState = {};
var pushNotificationReducer = function(state, action) {
switch(action.type) {
case 'RECEIVED_PUSH_NOTIFICATION':
var obj = JSON.parse(action.PushNotification);
console.log(obj.pushid);
console.log(obj.count);
return obj;
default:
if (typeof state === 'undefined') {
return pushNotificationDefaultState;
}
return state;
}
};
module.exports = Redux.combineReducers({
pushNotifications: pushNotificationReducer
});
Thanks in advance,
The problem is, that you are storing only one notification in redux state. Instead of this, you should store an array of them.
// Using an emty array as default state, instead of object.
var pushNotificationDefaultState = [];
var pushNotificationReducer = function(state, action) {
switch(action.type) {
case 'RECEIVED_PUSH_NOTIFICATION':
var obj = JSON.parse(action.PushNotification);
// Returning new array, which contains previous state and new notification.
return [].concat(state, [obj]);
default:
if (typeof state === 'undefined') {
return pushNotificationDefaultState;
}
return state;
}
};
module.exports = Redux.combineReducers({
pushNotifications: pushNotificationReducer
});
Also, you are not returning notifications elements from render function:
socket.onmessage = function(event) {
console.log("Received message" + event.data.toString());
store.dispatch(receivedPushNotification(event.data));
};
var App = React.createClass({
render: function () {
// To render notifications, return it array from render function
return _.map(this.props.pushNotifications, function(value, key, notification) {
var percentage = (notification.count / 50) * 100;
return (
<div className="row" key={notification.pushid}>
<div className="col-sm-12">
<Card>
<h1 className="marB15">{notification.pushid}</h1>
<div className="clearfix">
<div className="progress progress-striped active marB10">
<div className="progress-bar" style={{'width': percentage + '%'}}></div>
</div>
<div className="pull-right">
<p>Total: {notification.count}</p>
</div>
</div>
</Card>
</div>
</div>
)
});
}
});
add return statement in your render, after map
return (<div>{pushNotifications}</div>);
in reducer you should add new notif in array
case 'RECEIVED_PUSH_NOTIFICATION':
var notif = JSON.parse(action.PushNotification);
return [...state, notif ];
I have the following ReactJS code :
var data1 = {"Columns":["Title1","Title2","Title3"],"Rows":[{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]},{"CellText":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
if(this.props.data){
}
return (
<div>Text</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
<GridRow data={columns}>
);
});
var Row = this.props.data.Rows.map(function (rows) {
return (
<GridRow data={rows}>
);
});
}
return (
<ul>
<li>{Header}</li>
<li>{Row}</li>
</ul>
);
}
});
var GridBox = React.createClass({
render: function(){
return (
<GridList data={data1} />
);
}
});
I'm trying to pass the data1 variable to the GridList where it is split up to Columns (for header) and rows. The problem is that I get the following exception at runtime:
In file "~/Scripts/Grid.jsx": Parse Error: Line 30: Unexpected token
return (at line 30 column 6) Line: 52 Column:3
I'm running this from within Visual Studio 2013 with ReactJS.
The stated Line nr and colum makes no sense
Im trying to render a table based on metadata(columns) and row data from service.
You need to close tags either with a matching closing tag, or using self closing tags.
// ERROR
<GridRow data={rows}>
// OK
<GridRow data={rows}></GridRow>
// Best
<GridRow data={rows} />
The error message isn't very helpful.
Also, when creating an array of nodes, it's good to give them keys.
Rows.map(function(row, i){
return <GridRow data={rows} key={i} />;
});
I played around with it some more, and the weirdness comes from JSX accepting anything between an opening tag and <, {, or } as raw text. If you did something like this:
var GridList = React.createClass({
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
<GridRow data={columns}>
);
});
var Row = this.props.data.Rows.map(function (rows) </GridRow>
)});
}
return (
<ul>
<li>{Header}</li>
<li>{Row}</li>
</ul>
);
}
});
It'll happily output this:
var GridList = React.createClass({displayName: "GridList",
render: function() {
if(this.props.data){
var Header = this.props.data.Columns.map(function (columns) {
return (
React.createElement(GridRow, {data: columns},
");" + ' ' +
"});" + ' ' +
"var Row = this.props.data.Rows.map(function (rows) ")
)});
}
return (
React.createElement("ul", null,
React.createElement("li", null, Header),
React.createElement("li", null, Row)
)
);
}
});
It's completely content until it encounters the { after Rows.map(function (rows), which means "go back into JavaScript expression mode", and it encounters a return in an expression, which is invalid, so it bails, and gives the best error it can.