get value from other react component - javascript

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.

Related

Load more data using vue js when page is bottom area

I tried to make my Load More data when my page scroll to the bottom. The first thing is I make a div element that I put at the end of the data loop.
<div class="products">
<p>{{ status }}</p>
<div class="product" v-for="(item, index) in items">
<div>
<div class="product-image"><img :src="item.link" alt=""></div>
</div>
<div>
<h4 class="product-title">{{ item.title }}</h4>
<p>Price : {{ price }}</p>
<button class="add-to-cart btn" #click="addItem(index)">Add Item To Cart</button>
</div>
</div>
<div id="product-list-bottom"></div>
</div>
Div element with id product-list-bottom I will detect it using scrollMonitor.js
My default data :
data: {
status: 'Empty product',
total: 0,
items: [],
cart: [],
newSearch: 'anime',
lastSearch: '',
price: STATIC_PRICE,
result: []
}
Inside mounted I detected scroll to bottom :
mounted: function() {
this.onSubmit()
var vueInstance = this
var elem = document.getElementById('product-list-bottom')
var watcher = scrollMonitor.create(elem)
watcher.enterViewport(function() {
vueInstance.appendItems()
})
}
Inside mounted I call onSubmit :
onSubmit: function() {
this.items = ''
this.status = "Searching keyword '" + this.newSearch + "' on server ..."
this.$http.get('/search/'.concat(this.newSearch))
.then(function(response) {
this.lastSearch = this.newSearch,
this.status = 'Find ' + response.data.length + ' data'
this.result = response.data
this.appendItems()
})
}
And inside onSubmit I call appendItems function :
appendItems: function() {
if(this.items.length < this.result.length) {
var start = this.items.length
var end = parseInt(this.items.length + 5)
var append = this.result.slice(start, end)
this.items = this.items.concat(append)
console.log(append)
}
}
All goes well, but when I scroll down I get an error message :
This is because this line :
this.items = this.items.concat(append)
How do I make the data on xxx change (always added five new data from the array) according to the command on the line :
var end = parseInt(this.items.length + 5)
Thanks
it seems '/search/'.concat(this.newSearch) gets evaluated into function and not an actual string value
Try this if you are using babel/webpack
this.$http.get(`/search/`${this.newSearch}`)
Or if not
this.$http.get('/search/' + this.newSearch)
I think since Vue 2.3+ or so you can get this done without any jQuery stuff or any other dependencies:
<style>
.scrollbar{
overflow-y: scroll;
//...
}
.styled-scrollbar::-webkit-scrollbar
.styled-scrollbar::-webkit-scrollbar-thumb
.styled-scrollbar::-webkit-scrollbar-track{
//styling
}
</style>
<template>
//...
<div #scroll="scroll" class="scrollbar">
<div v-for="item in items" :key="item.id">
//TODO: item content
</div
</div>
//...
</template>
<script>
{
data: {
//..
lastScrollUpdate:0
}
//..
mounted: {
scroll:function (e) {
var scrollBar=e.target;
if((scrollBar.scrollTop + scrollBar.clientHeight >= scrollBar.scrollHeight-20)){
var t=new Date().getTime();
if((t-this.lastScrollUpdate)>3000){
this.lastScrollUpdate=t;
console.log('reached end: '+scrollBar.scrollTop+' '+scrollBar.clientHeight+' '+scrollBar.scrollHeight);
//TODO: load more data
}else{
console.log("< 3sec between scoll. no update");
}
}
},
//..
}
}
</script>
You may also want to adjust this to "#scroll.passive", in order to let the scroll-function be executed parallel to the UI (https://v2.vuejs.org/v2/guide/events.html#Event-Modifiers)

How to bind onclick handlers to `this` properly on React

Explanation to why this is not a duplicate: My code is already working, I have included as a comment. The question is why the this context change when I include it to click handler function.
I'm attempting a calculator project in React. The goal is to attach onclick handlers to number buttons so the numbers are displayed on the calculator display area. If the handler is written directly to render method it is working, however, if I'm trying from the ComponentDidMount I get an error this.inputDigit is not a function. How do I bind this.inputDigit(digit) properly?
import React from 'react';
import './App.css';
export default class Calculator extends React.Component {
// display of calculator initially zero
state = {
displayValue: '0'
}
//click handler function
inputDigit(digit){
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
componentDidMount(){
//Get all number keys and attach click handler function
var numberKeys = document.getElementsByClassName("number-keys");
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber); // This is not working
};
for (var i = 0; i < numberKeys.length; i++) {
numberKeys[i].onclick = myFunction;
}
}
render() {
const { displayValue } = this.state;
return (
<div className="calculator">
<div className="calculator-display">{displayValue}</div>
<div className="calculator-keypad">
<div className="input-keys">
<div className="digit-keys">
{/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}}
<button className="number-keys">0</button>
<button className="number-keys1">1</button>
<button className="number-keys">2</button>
<button className="number-keys">3</button>
<button className="number-keys">4</button>
<button className="number-keys">5</button>
<button className="number-keys">6</button>
<button className="number-keys">7</button>
<button className="number-keys">8</button>
<button className="number-keys">9</button>
</div>
</div>
</div>
</div>
)
}
}
Thats because you are writing it inside a function which is not bound,
Use
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber);
}.bind(this);
or
const myFunction = () => {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber);
}
After this you need to bind the inputDigit function as well since it also uses setState
//click handler function
inputDigit = (digit) => {
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
Since you want to use the button text as well, in that case you should use a separate variable in place of this to call the inputDigit function like
class Calculator extends React.Component {
// display of calculator initially zero
state = {
displayValue: '0'
}
//click handler function
inputDigit(digit){
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
componentDidMount(){
//Get all number keys and attach click handler function
var numberKeys = document.getElementsByClassName("number-keys");
var that = this;
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
console.log(targetNumber);
return that.inputDigit(targetNumber); // This is not working
};
for (var i = 0; i < numberKeys.length; i++) {
numberKeys[i].onclick = myFunction;
}
}
render() {
const { displayValue } = this.state;
return (
<div className="calculator">
<div className="calculator-display">{displayValue}</div>
<div className="calculator-keypad">
<div className="input-keys">
<div className="digit-keys">
{/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}
<button className="number-keys">0</button>
<button className="number-keys">1</button>
<button className="number-keys">2</button>
<button className="number-keys">3</button>
<button className="number-keys">4</button>
<button className="number-keys">5</button>
<button className="number-keys">6</button>
<button className="number-keys">7</button>
<button className="number-keys">8</button>
<button className="number-keys">9</button>
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<Calculator/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Bind it in the constructor
constructor(props) {
super(props);
this.inputDigit = this.inputDigit.bind(this);
}

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

Digging into state in render of Reactjs app components

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.

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