Digging into state in render of Reactjs app components - javascript

I cannot reference elements of objects and arrays in a state passed to a subcomponent.
I have not been able to clearly identify the difference between the cases when I cannot reference them in some sub components.
What is the thing I miss?
Sample props of the components is given under the components. Commented.
In that I cannot reference for example, current.main.temp or current.weather[0]. it gives an undefined error:
var Current = React.createClass({
render: function () {
var current = this.props.current;
var dateArray = new Date(current.dt * 1000).toDateString().split(" ");
console.log("datearray: ", dateArray[0]);
var main = current.main;
console.log(main);
return (
<div style={{float: "left", clear:"left"}}>
<div style={{float: "left"}}>
<div>{dateArray[0]}</div>
<div>{dateArray[1]} {dateArray[2]}</div>
<div>{dateArray[3]}</div>
</div>
<div style={{float: "left"}}>
<div>current: {JSON.stringify(current)}</div>
</div>
<div style={{float: "left", clear: "right"}}>
<div>{JSON.stringify(current.weather)}</div>
</div>
</div>
);
},
});
// {"coord":{"lon":32.85,"lat":39.92},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"cmc stations","main":{"temp":28,"pressure":1018,"humidity":34,"temp_min":28,"temp_max":28},"wind":{"speed":2.1},"clouds":{"all":20},"dt":1442483400,"sys":{"type":1,"id":6022,"message":0.0026,"country":"TR","sunrise":1442460682,"sunset":1442505249},"id":323786,"name":"Ankara","cod":200}
however, in that I can reference for example, current.main.temp or current.weather[0]:
var Hour = React.createClass({
render: function () {
var hour = this.props.hour; // is an object.
return (
<div style={{float: "left"}}>
<div>{hour.weather[0].main}</div>
<div>{hour.weather[0].description}</div>
<div><img src={"http://openweathermap.org/img/w/"+hour.weather[0].icon+".png"} /></div>
<div>{hour.main.temp}</div>
<div>{hour.main.temp_min}</div>
<div>{hour.main.temp_max}</div>
<div>{hour.main.humidity}</div>
<div>{new Date(hour.dt * 1000).getHours()} o,clock</div>
</div>
);
}
});
// {"dt":1442437200,"main":{"temp":16.39,"temp_min":14.78,"temp_max":16.39,"pressure":914.76,"sea_level":1026.82,"grnd_level":914.76,"humidity":67,"temp_kf":1.62},"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],"clouds":{"all":0},"wind":{"speed":1.41,"deg":308.503},"sys":{"pod":"n"},"dt_txt":"2015-09-16 21:00:00"}

It's because of your data structure.
You don't have any property called data in your weather data making current.data.dt and current.data.main be undefined.

Related

ReactJs: target.key undefined after method is reached from li rendered by map function

I'm currently trying to coding a react app that would do the following:
- Create a list of questions from an array using a map function.
- Making each list element clickable using a onClick prop
- The linked onClick method changes the state in another file with my 'qsChange' prop.
I had a hard time making my list clickable and finally managed following this question: React: trying to add an onClick to a li tag but the click handler function is undefined
However, now I cannot make it so that my variable 'choice' returns a defined value. I would want var choice to be equal to "A ?", "B ?" or "C ?" depending on which I click.
Here's my code:
var questions = ["A ?", "B ?", "C ?"];
var Questions = React.createClass({
handleClick: function() {
var visibility;
if(this.props.visibility) {
document.getElementById('second').style.display = 'none';
visibility = false;
this.props.onChange(visibility);
} else {
document.getElementById('second').style.display = 'block';
visibility = true;
this.props.onChange(visibility);
}
},
/* Here is where my problem lies */
onItemClick: function(e){
var choice = e.target.key;
this.props.qsChange(choice);
alert(choice);
},
render: function() {
return (
<div className="bigqs">
<div id="first" className="small" style={firstStyle}>
<h1>Question :</h1>
<button style={btnStyle} onClick={this.handleClick}>
<img id="arrow" src="../../img/arrow.png" />
</button>
<h3 id="selectedQuestion">{this.props.selected}</h3>
</div>
<div id="second" className="small" style={{display: 'none'}}>
<h4>
<ul>
{questions.map(function(qs, i) {return <li key={qs[i]} onClick={this.onItemClick}>{qs}</li>;}, this)}
</ul>
</h4>
</div>
</div>
);
}
});
I am still a newbie, so please be indulgent ;-)
I hope I was clear enough.
Ps: I have also tried this guide but it didn't work for me: http://derpturkey.com/react-pass-value-with-onclick/
Instead of grabbing the question from target, you can pass question through to your handler. Also, since inside map qs is a string, qs[i] will be getting the character in the string from that index. You just need to make sure your key is unique.
onItemClick: function(choice) {
this.props.qsChange(choice)
alert(choice)
},
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.onItemClick(qs)}>{qs}</li>
)}
...
</div>
)
}
In fact, your intermediate function isn't doing much, you can just call your props function inside render:
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.props.qsChange(qs)}>{qs}</li>
)}
...
</div>
)
}

React Redux only displaying one element

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 ];

get value from other react component

I am learning react, and a little bit confused by how to get value from other components. I am building a Podomoro clocks app. these are consists of:
- setting of session time,
- setting of rest time,
- a clock for counting down.
how do I get value of session time and rest time, assigned to clock?
here is my code:
$(document).ready(function(){
var TimerSetting = React.createClass({
getInitialState: function(){
return {
time: 0
};
},
afterUpdateTime: function(callback){
this.afterUpdateTimeAction = callback;
},
increment: function(){
var time = this.state.time + 1;
time = Math.max(time, 0);
this.setState({
time: time
});
this.afterUpdateTimeAction(time);
},
decrement: function(){
var time = this.state.time - 1;
time = Math.max(time, 0);
this.setState({
time: time
});
this.afterUpdateTimeAction(time);
},
render: function(){
return (
<div className="timerSetting">
<div className="titleSetting">
{this.props.title}
</div>
<div className="minus" onClick={this.decrement}>
-
</div>
<div className="time-setting">
{this.state.time}
</div>
<div className="plus" onClick={this.increment}>
+
</div>
</div>
)
}
});
var PodomoroClock = React.createClass({
getInitialState: function(){
return {
time: 0
};
},
updateSession: function(time){
this.setState({
time: time
});
},
render: function(){
return(
<div className="clock">
{this.state.time}
</div>
);
}
});
var TimerWrapper= React.createClass({
updateClockSession: function(com){
com.afterUpdateTime(this.updateChildSession);
},
updateChildSession: function(time){
this.ref.podomoro.updateSession(time);
},
render: function(){
return (
<div className="timeWrapper">
<div className="timer">
<TimerSetting title="Session Length" ref={this.updateClockSession} />
<TimerSetting title="Break Length" ref="break"/>
</div>
<div className="podomoroClock">
<PodomoroClock ref='podomoro'/>
</div>
</div>
);
}
});
React.render(<TimerWrapper title="class2" />, $('#content')[0]);
});
please I need your help? get stuck with it. I'm trying to use callback, but it doesn't work
Try this instead:
<TimerSetting title="Session Length" onTimeChanged={this.updateClockSession} />
And then you need to call your handler inside the afterUpdate function of the TimerSetting:
afterUpdateTime: function(time){
this.props.onTimeChanged && this.props.onTimeChanged(time)
}
Note that your TimerWrapper.onTimeChanged function will now receive the time (integer) as input.

nested component in React

I have 3 level nested components. Html structure is like below. C and D's render node/element need to access state and custom function from Main, but C and D are inside B element. I am not sure if this is the best way to structure components. In addition, how do you pass down state and custom functions from Main to grandchildren C and D's render node/element?
<div id="Main">
<div class="A"></div>
<div class="B">
<div class="C"></div>
<div class="D"></div>
</div>
</div>
My attempt:
var Main = React.createClass({
render: function(){
return (
<div className="Main">
<A/>
<B />
</div>
);
}
});
// Putting C, D into B, but C,D both need to access state from Main.
var B = React.createClass({
render: function(){
return (
<div className="B">
<C />
<D />
</div>
);
}
});
var C =....;
var D =....;
React.render(<Main />, document.body)
I am not sure there is a direct way to access the state of child components. But you can use ref.
<Field ref="field1"/>
Then you can use
this.refs.field1.state
Your case:
var B = React.createClass({
render: function(){
return (
<div>
<C ref = "c1"/>
<D ref = "d1"/>
</div>
);
}
});
But this is not recommended as per this link
You may pass the state from Main as props of B
var Main = React.createClass({
getInitialState:function(){
return {mainState:"Main state"}
}
render: function(){
return (
<div>
<A/>
<B mainState={this.state.mainState}/>
</div>
);
}
});
// Putting C, D into B, but C,D both need to access state from Main.
var B = React.createClass({
propTypes:{mainState:React.PropTypes.string}
render: function(){
return (
<div>
{this.props.mainState}// The state from main is received as propps in child component
<C mainState={this.props.mainState}/>// Now youcan access mainState a props of C
<D />
</div>
);
}
});

How to turn objects in to HTML

How do i get this :
<li>
<div class='myClass1'>myData1</div>
<div class='myClass2'>myData2</div>
<div class='myClass3'>myData3</div>
<div class='myClass4'>myData4</div>
</li>
from this code
var data1 = {"Columns":[{"Title":"Title1","HTMLClass":"g1_Title"},{"Title":"Title2","HTMLClass":"g2_Title"},{"Title":"Title3","HTMLClass":"g3_Title"}],"Rows":[{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
var data = [], columns;
// puts all the data in to a better structure (ideally the props would have this structure or this manipulation would be done on onReceiveProps)
if(this.props.columns){
for(var ii = 0; ii < this.props.columns.length; ii++){
data.push({
class: this.props.columns[i].HTMLClass,
contents: this.props.Cell[i]
})
}
}
// Its best to map JSX elements and not store them in arrays
columns = data.map(function(col) {
return <div className= + {col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
var GridHead = React.createClass({
render: function() {
if(this.props.data){
var cell = this.props.data.Title;
var htmlClass = this.props.data.HTMLClass;
}
return (
<div className={htmlClass}>{cell}</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var header = this.props.data.Columns.map(function (columns) {
return (
<GridHead data={columns} />
);
});
var row = this.props.data.Rows.map(function (row, i) {
return (
<GridRow columns={data1.Columns} cells={row.Cells} key={i} />
);
});
}
return (
<ul>
<li>{header}</li>
{row}
</ul>
);
}
});
var GridBox = React.createClass({
render: function(){
return (
<GridList data={data1} />
);
}
});
The output right now is this
In file "~/Scripts/Grid.jsx": Parse Error: Line 26: XJS value should
be either an expression or a quoted XJS text (at line 26 column 35)
Line: 52 Column:3
As your question initially asked was to do with just the GridRow component and nothing else I have not touched any other component.
Your main problem was you were assigning className = + //something in your GridRow component which isn't the correct way to assign. There were other errors like missing div tags.
Better GridRow
When the component mounts a columndata variable is created and is populated with formatted data using formatData();.
I do not recommend you do data formatting in this component (although it is doable). You should either format your data at a top level component and pass down formatted data or accept data in the correct structure.
My GridRow component to this:
var GridRow = React.createClass({
componentWillMount: function() {
this.columndata = [];
this.formatData();
},
formatData: function() { // Formats prop data into something manageable
if (this.props.columns && this.props.cells) {
for(var ii = 0; ii < this.props.columns.length; ii++){
this.columndata.push({
class: this.props.columns[ii].HTMLClass,
contents: this.props.cells[ii]
})
}
this.forceUpdate(); // Forces a rerender
}
},
componentDidUpdate: function(prevProps, prevState) {
// If this component receives the props late
if (!prevProps.cells && !prevProps.columns) {
this.formatData();
}
},
render: function() {
var columns;
// Its best to map JSX elements and not store them in arrays
columns = this.columndata.map(function(col) {
return <div className={col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
I think it's important to note that you should avoid storing JSX elements in arrays.
I think you were basically on the money, except you were missing classname and div tags.

Categories