Why Is this function producing NaN in the DOM?(React) - reactjs

So I'm working on this portion of an app where I want the number displayed on the page to increase or decrease depending on the hour of the day. At this point I'm using componentDidMount() to use a setInterval() method that takes in a function ( hourCheck ) and an interval.
However, hourCheck is having issues, specifically it's returning NaN in the DOM instead of an actual number. Seems like the state isn't being changed correctly but I'm exactly sure in what way.
Here's the app.js file I'm working with. I set the interval to 8000ms for the sake of time. This is ultimately a counter app.
import React, { Component, useState } from "react";
import { Wrapper } from "./components/Wrapper";
import Title from "./components/Title";
import Count from "./components/Count";
import "./App.css";
class App extends Component {
state = {
CountNum: 0,
};
setCountNum = (val) => {
this.setState({CountNum: val})
}
hourCheck = (val) => {
let hourCount = new Date().getHours()
if (hourCount >= 9 && hourCount <= 17) {
this.setState({ CountNum: val + 1 });
} else if (hourCount <= 8) {
this.setState({ CountNum: val - 1});
} else if (hourCount >= 18) {
this.setState({ CountNum: val - 1});
}
console.log(hourCount)
};
loginSomething = (val) => {
console.log(this.state.HourNum)
};
render()
{
return (
<Wrapper setCountNum={this.setCountNum} CountNum={this.state.CountNum}>
<Title>Click Counter</Title>
<Title>Count:</Title>
<Count>{this.state.CountNum}</Count>
</Wrapper>
);
}
componentDidMount() {
setInterval(this.hourCheck, 8000);
};
}
export default App;

hourCheck function has val parameter and you are not passing that argument in setInterval in componentDidMount. Bind "this" obj to the hourCheck function in setInterval.

Related

Return multiple values over time in ES6 & React

Okay, so is there a way to return a value from a function - the way return does - but not stop the function - the way return does?
I need this so I can keep returning values every so often.
My code looks like:
loopingTitleTextHandler = () => {
const title = ["good", "cool", "887H", "Vertical"];
for (i = 0; i < 999999; i++) {
var j = i%4;
// loopingTitleTextHandler() // calls itself before return terminates execution
return title[j]; //return 0,1,2,3 in a loop with 3 second delay
}
}
My React Component
<ShowTitle size={230}
title={this.loopingTitleTextHandler()}
color="#FDA7DC" />
Edit: I'm after a solution that solves this problem in the function like this python answer:
Return multiple values over time
but using ES6.
import time
def myFunction(limit):
for i in range(0,limit):
time.sleep(2)
yield i*i
for x in myFunction(100):
print( x )
In the context of React, I think it would make more sense to manage these values through state. Assuming you want to return one new title every 3 seconds, you can do the following:
Here's a sandbox: https://codesandbox.io/s/elated-rgb-n4j6z
App.js
import React from "react";
import ReactDOM from "react-dom";
import ShowTitle from "./ShowTitle";
import "./styles.css";
class App extends React.Component {
state = {
title: ["good", "cool", "887H", "Vertical"],
currentTitle: "good"
};
loopingTitleTextHandler = () => {
const { currentTitle, title } = this.state;
const nextTitleIndex =
title.indexOf(currentTitle) + 1 === title.length
? 0
: title.indexOf(currentTitle) + 1;
this.setState({ currentTitle: title[nextTitleIndex] });
};
render() {
return (
<div className="App">
<ShowTitle
changeTitle={this.loopingTitleTextHandler}
currentTitle={this.state.currentTitle}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
ShowTitle.js
import React from "react";
class ShowTitle extends React.Component {
componentDidMount() {
setInterval(() => {
this.props.changeTitle();
console.log(this.props.currentTitle + " " + new Date().getTime());
}, 3000);
}
render() {
return <div>Title: {this.props.currentTitle}</div>;
}
}
export default ShowTitle;
In the parent component (App.js) we keep track of the currentTitle. When loopingTitleTextHandler() is called, we update our state.currentTitle with the next title in the array. currentTitle gets passed down to ShowTitle component.
In Child component, we use a setInterval() to call loopingTitleTextHandler() every 3 seconds, and display the next title.
You could make use of setInterval in life-cycle hook. this will repeatedly call the function at an interval
loopingTitleTextHandler = () => {
const title = ["good", "cool", "887H", "Vertical"];
for (i = 0; i < 999999; i++) {
var j = i%4;
// loopingTitleTextHandler() // calls itself before return terminates execution
return title[j]; //return 0,1,2,3 in a loop with 3 second delay
}
}
Add setInerval in componentWillMount(){}
componentWillMount(){
setInterval(()=>{
this.loopingTitleTextHandler()
}, 3000) // 3000 milli seconds, that is 3 sec
}

Improve poor progress bar performance

I am trying to pass progress state to other progress bar by using the MobX store.
There are two progress bars. One of them should be changing independently in a short period(about 2secs). I used setTimeOut to increase the current progress and it worked well. Then I tried to save the current progress value into the MobX store to pass to another component. After this, the performance of the progress bar was degraded.
render() {
...
if (tradingProgress.progress > 100) {
this.setState(prevState => ({
tradingProgress: {
...prevState.tradingProgress,
progress: 100,
},
}));
} else if (tradingProgress.isTrading) {
setTimeout(() => {
this.setState(prevState => ({
tradingProgress: {
...prevState.tradingProgress,
progress: prevState.tradingProgress.progress + 5,
},
}));
}, 100);
}
...
// save current progress to mobx store.
// convertProgress, setConvertProgress are in my mobx store.
if (tradingProgress.progress !== convertProgress && tradingProgress.isTrading) {
setConvertProgress(tradingProgress.progress); // in mobx store: this.convertProgress = currentProgress
}
I think using the MobX store frequently is the bottleneck, and I didn't use that store value in other components.
Thanks for spending your golden time for me.
MobX is unlikely to be the bottleneck here. What's more worrying is the use of setState in the render method. This is almost always a bad idea, because setState always causes another render. So you could end up rendering way more often than you actually meant to, which can definitely impact performance. Likewise, you don't want to update your MobX store from inside render, as it's very likely to trigger another render.
Instead, try to move your logic to other parts of the program, and have rendering be more of an 'afterthought': a final consequence of everything else you're doing.
Without really understanding your goal, here's a demonstration with a simple MobX store backing it.
import React from "react";
import ReactDOM from "react-dom";
import { action, decorate, observable } from "mobx";
import { inject, Provider, observer } from "mobx-react";
class UIStore {
convertProgress = 0;
setConvertProgress = progress => {
if (this.convertProgress < 100) {
this.convertProgress = progress;
}
};
}
decorate(UIStore, {
convertProgress: observable,
setConvertProgress: action
});
const store = new UIStore();
class TradingThing extends React.Component {
state = { progress: 0 };
componentDidMount() {
this.setState({ interval: setInterval(this.tick, 100) });
}
componentWillUnmount() {
clearInterval(this.state.interval);
}
tick = () => {
const { convertProgress, setConvertProgress } = this.props.store;
const { progress } = this.state;
setConvertProgress(convertProgress + 1);
if (progress < 100) {
this.setState({ progress: progress + 5 });
}
};
render() {
return (
<>
<div>Progress from component state: {this.state.progress}</div>
<div>Progress from MobX state: {this.props.store.convertProgress}</div>
</>
);
}
}
const TradingProgress = inject("store")(observer(TradingThing));
ReactDOM.render(
<Provider store={store}>
<TradingProgress />
</Provider>,
document.getElementById("root")
);
CodeSandbox
As you can see, the render method is very simple. This is usually a good sign!

React get new data in componentDidUpdate

I have a component which receives a list of news from two or three APIs. the first time the component renders, the apis are called and data is rendered in componentDidMount
something like this:
componentDidMount() {
this.state.platforms.forEach((platform, i) => {
let objToSend = {
phrase: this.props.searchParams.phrase,
// this is the main place when input data changes
...this.props.searchParams.general_params,
...this.props.searchParams.platforms[i].api_params,
stringPath: platform,
apiPath: this.props.searchParams.platforms[i].apiPath,
}
this.props.loadData(objToSend)
// this is the api call using redux which sends data as props to this component
}
new when the phrase change, I want this component to re-render and re-run the componentDidMount, but it's not working because componentDidMount will run once.
So I used componentDidUpdate, but as there are a number of calls so the api is being updated continuesly.
How can I make the component re-render and re-run componentDidMount every time I change the phrase
You can use componentDidUpdate arguments (previousProps, previousState) to check whether some new changes happened.
Example
componentDidUpdate(previousProps, previousState) {
if (previousProps.phrase !== this.props.phrase) {
//Do whatever needs to happen!
}
}
I stopped the infinite loop for my situation this way.
Here's one way to do something() when you re-render.
import React, { Component } from 'react';
import { render } from 'react-dom';
const fakeFetch = (n) => {
console.log(`Doing fake fetch: ${n}`)
return n
}
class App extends Component {
state = {
value: false,
number: 0,
}
componentDidMount() {
const number = fakeFetch(this.state.number + 1);
this.setState({ number })
}
componentDidUpdate(prevProps, prevState) {
if (prevState.value !== this.state.value) {
const number = fakeFetch(this.state.number + 1);
this.setState({ number })
}
}
toggle = () => {
this.setState(({ value }) => ({ value: !value }))
}
render() {
return (
<React.Fragment>
<h1>Number: {this.state.number}</h1>
<button onClick={this.toggle}>re-render</button>
</React.Fragment>
);
}
}
render(<App />, document.getElementById('root'));
Live example here.

React, updating local states when redux reducer is updated

I am making a project using React and Redux. I am updating a reducer with an array and then mapping that array into a component. The component has a state which is used to conditionally render a an image. I want this components state to be reset to its default whenever the reducers state is updated.
Here is the reducer :
const reducer = (state={board : []}, action) => {
switch (action.type) {
case 'MAKE_BOARD':
return Object.assign({}, state, {
board: action.payload
})
default:
return state
}
}
Here is the App.js page which calls the reducer:
import React, { Component } from 'react';
import './App.css';
import Board from '../Board/Board'
import {connect} from 'react-redux'
const mapReduxStateToProps= (reduxState) => ({
reduxState
})
class App extends Component {
state = {
size : '8',
squareArray : []
}
handleChange =(event) => {
this.setState({
...this.state,
size : Number(event.target.value)
})
console.log(this.state)
}
//This function makes a an array of numbers with 1/4 'X's and 3/4 'O's
boardMaker = (number) => {
this.setState({squareArray:[]});
let size = number*number;
let placeHolderArray = []
for(let i=0; i<size; i++){
placeHolderArray.push('O')
}
for(let j=0; j<size/4;j++){
placeHolderArray[Math.floor(Math.random()*size)] = 'X'
}
this.setState({squareArray: placeHolderArray})
console.log(placeHolderArray)
console.log(this.state.squareArray);
this.props.dispatch({type:'MAKE_BOARD', payload: placeHolderArray})
this.props.dispatch({type: 'SET_SIZE', payload : this.state.size})
}
render() {
return (
<div className="App">
<header className="App-header">
<input onChange={this.handleChange} placeholder='Size'/>
<button onClick={()=>this.boardMaker(this.state.size)}>Make Board</button>
<div className='board' style={{width: 40*this.props.reduxState.size.size}}>
{/* {this.state.squareArray.map(space => {
return(
<div className='square'>{space}</div>
)
})} */}
{JSON.stringify(this.props.reduxState)}
{this.props.reduxState.reducer.board.map((space,index) =>
<Board keys={index} id={space}/>
)
}
</div>
</header>
</div>
);
}
}
export default connect(mapReduxStateToProps)(App);
Here is the board.js where the reducer is being mapped:
import React, { Component } from 'react';
import './Board.css'
import { connect } from 'react-redux';
const mapReduxStateToProps = (reduxState) => ({reduxState})
class Board extends Component {
state = {
clicked: false,
displayFlag: false,
counter: 0,
}
imageDisplay= () => {
if(!this.state.clicked && !this.state.displayFlag){
return <img key={this.props.id} src='images/Frog-1.png' alt='Not Clicked'/>
} else if(this.state.displayFlag){
return <img src='images/Yellow.png' alt='None' />
} else {
return this.state.counter;
}
}
handleMouseDown = e => {
document.oncontextmenu = function() {
return false;
}
e = e || window.event;
//console.log(e.which)
console.log(this.state)
switch(e.which) {
case 1 : this.showNumber(); break;
case 2 : break;
case 3 : this.displayFlag(); return false;
default: break;
}
}
displayFlag= () => {
console.log('running')
this.setState({...this.state, displayFlag : !this.state.displayFlag })
return this.state.displayFlag;
}
showNumber= () => {
console.log('run')
let Xcounter = 0;
let edge = Math.sqrt(this.props.reduxState.reducer.board.length)
console.log(edge)
let keys = this.props.keys
let board = this.props.reduxState.reducer.board
let minX = keys%edge === 0 ? 0 : -1;
let maxX = keys%edge === (edge-1) ? 0 : 1;
let minY = Math.floor(keys/edge) == 0 ? 0 : -1;
let maxY = Math.floor(keys/edge) == (edge-1) ? 0 : 1;
for(let x = minX; x <= maxX; x++){
for(let y = minY; y<=maxY; y++){
if(board[keys+x+(y*edge)]=== 'X'){
Xcounter++
}
}
}
if(this.props.id === 'X'){
this.setState({...this.state, clicked: true, counter: 'X'})
return this.state.counter;
}
this.setState({...this.state, clicked: true, counter: Xcounter})
return this.state.counter;
}
render() {
return (
<div className="App">
<div onMouseDown={()=>this.handleMouseDown()} className='square'>{this.imageDisplay()}</div>
</div>
);
}
}
export default connect(mapReduxStateToProps)(Board);
I want the local state on Board to reset when the reducer is updated. I can probably do this be adding properties to the reducer and using them in the component but I am hoping there is a better method.
Looking at your use case - you need to empty the local state on every MAKE_BOARD
action.
Here are two approaches that I had in mind -
You can check for the props that are getting updated and place a condition in your (depending on your react version) lifecycle methods such as componentWillReceiveProps / getDerivedStateFromProps / componentDidUpdate and empty your local state.
You can create a separate action something on the lines of RESET_ACTION_NAME which returns your initial state ( which is empty ) on every update. But for it to function correctly, you'll need to put your local state as an object in the redux store.
PS: If you know that your action to reset the state only has a limited scope, you needn't put your state in the store. But, if you know that this action spans across multiple components, you can consider transforming the local state into a redux store object.
This gets a bit into design opinion land, but I think the most straightforward way to deal with this is to put the board's state in redux as well rather than local state (probably in its own separate reducer in the same store), because (if I understand correctly) you're really saying that you want its state to change (reset) based on the 'MAKE_BOARD' action.
Another alternative would be to include an integer board id in your redux state that you increment each time you remake the board. Then if you use that as the “key” property of your board I believe that will cause it to automatically reset because it will unmount the previous board and create a new one.
If you want more implementation specifics about how to go about this, I recommend getting a version of the code into codepen or codesandbox and sharing that in your question as well (but it's good that you're including most of the relevant code directly in your question text).

Differentiate between single and double click in React

Trying to find out what is the best way of differentiating between single and double click in a React component.
The built-in mouse events (onClick and onDoubleClick) work fine if you don't want apply them on the same DOM element. There is the jQuery dblclick which I don't want to use. There is also a React Timer Mixin which could be used, but I use ES6 classes so then I would need to use something like React Mixin and I just think it's an overkill for doing such a simple thing.
From what I found out people would set the timeout on the click event, so I followed that direction and came with this:
import React, { Component } from 'react'
export default class Click extends Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
this.state = {
clickCount: 0
}
}
handleClick() {
let clickCount = this.state.clickCount
let singleClickTimer
clickCount++
this.setState({ clickCount: this.state.clickCount + 1 })
if (clickCount === 1) {
singleClickTimer = setTimeout(() => {
if (this.state.clickCount === 1) {
clickCount = 0
this.setState({ clickCount: 0 })
console.log('single click')
}
}, 300)
} else if (clickCount === 2) {
clearTimeout(singleClickTimer)
clickCount = 0
this.setState({ clickCount: 0 })
console.log('double click')
}
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
</div>
)
}
}
It works, but I'm trying to find out if there is a better way of doing it.
Edit:
The big problem with this is that setTimeout is async, so I added another check (if (this.state.clickCount === 1) {), but that seems wrong to me.
Another problem with this is the delay, the double click is called immediately, but the single click will be called after whatever timeout was set (around 300ms).
I know this is an old question but I spun my wheels on this problem a bit also, so thought I'd share my solution for those who also end up here.
const DOUBLECLICK_TIMEOUT = 300;
handleImagePress = () => {
this.setState({ pressCount: this.state.pressCount+1 });
setTimeout(() => {
if (this.state.pressCount == 0) { return }
if (this.state.pressCount == 1) {
// Do single press action
} else if (this.state.pressCount == 2) {
// Do double press action
}
this.setState({ pressCount: 0 });
}, DOUBLECLICK_TIMEOUT)
}

Resources