I don't really know why this issue is happening.
It is suppose to update 90 deg every second.
Sometimes it goes crazy and update more 90 degrees sometimes.
CodePen Link
const updateValue = _=>{
console.log(this.state.value + 90);
this.setState(s=>({
value: s.value + 90
}))
}
this.timerId = setInterval(updateValue, 1000)
"setInterval" and "setTimeOut" out of sync when tab is not focused
you must change your async doThis same as follows:
async doThis () {
const updateValue = _=>{
if (!document.hidden){
console.log(this.state.value + 90);
this.setState(s=>({value: s.value + 90}))
}
}
this.timerId = setInterval(updateValue, 1000)
}
I did this and I think your problem solved ... plase use this code:
const {useState} = React;
class App extends React.Component{
constructor(){
super();
this.state = {
value: 0
}
this.doThis = this.doThis.bind(this);
}
componentDidMount() {
this.doThis();
}
componentWillUnmount() {
clearInterval(this.timerId);
}
async doThis () {
const updateValue = _=>{
if (!document.hidden){
console.log(this.state.value + 90);
this.setState(s=>({value: s.value + 90}))
}
}
this.timerId = setInterval(updateValue, 1000)
}
render() {
return <Box value={this.state.value}/>;
}
}
function Box(props) {
const angle = props.value;
let style = {
transform: `rotateZ(${angle}deg)`,
transformOrigin: 'bottom'
}
let boxStyle = {
display: 'flex',
justifyContent: 'center'
}
return(
<div>
<div className='box' style={boxStyle}>
<div style={style} className="needle"></div>
</div>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'));
Related
The following is most likely a fundamental question. I have the following class. The value of this.isRunning is updated to true in start() but after running start() at stop() the value is still false. What is the reason for that? This happens when the class is imported into a React component and instantiated.
The latest version of Stopwatch as used in a React component can be seen below:
import moment from "moment";
export default class Stopwatch {
constructor() {
console.log("run");
this.isRunning = false;
this.lastUpdateTime = 0;
this.totalDuration = 0;
this.interval = null;
}
start() {
if (this.isRunning) {
return console.error("Stopwatch is already started");
}
console.log("Stopwatch started");
this.lastUpdateTime = window.performance.now();
this.isRunning = true;
console.log(this.isRunning);
this.interval = setInterval(() => {
this.totalDuration += window.performance.now() - this.lastUpdateTime;
this.lastUpdateTime = window.performance.now();
}, 100); // update about every 100 ms
}
stop() {
console.log(this.isRunning)
if (!this.isRunning) {
return console.error("Stopwatch is already stopped");
}
console.log("Stopwatch stopped");
console.log("Stopwatch duration:", this.getFormattedDuration());
this.isRunning = false;
clearInterval(this.interval);
}
reset() {
console.log("Stopwatch reset");
this.isRunning = false;
this.lastUpdateTime = 0;
this.totalDuration = 0;
clearInterval(this.interval);
}
formatTime = (ms) => moment(ms).format("mm:ss.S");
getFormattedDuration() {
if (this.lastUpdateTime === 0) {
return "00:00.000";
}
return this.formatTime(this.totalDuration);
}
}
First, the stopwatch is started. Then it is stopped, and finally, reset.
The console output is the following:
Here is a stripped-down version of the original React component where the issue still occurs:
import React from 'react';
import { Button, Row, Col } from 'antd';
import { I18n } from 'aws-amplify';
import Stopwatch from '../../../../../utils/Stopwatch';
export default function TenMeter(props) {
const stopwatch = new Stopwatch();
const startTrial = async (index) => {
stopwatch.start();
}
const completeTrial = async (index) => {
stopwatch.stop();
resetParameters();
}
const resetTrial = async (index) => {
resetParameters();
}
const resetTest = (index) => {
resetParameters();
}
/**
* Reset parameter values.
*/
const resetParameters = () => {
stopwatch.reset();
}
function getTrialsButtonGroup(index) {
return (
<Row key={index} gutter={[16, 16]}>
<Col xs={{ span: 24, offset: 0 }} lg={{ span: 24, offset: 0 }}>
<span>{I18n.get('Trial')} {index + 1}</span>
<Button style={{ marginLeft: "10px" }} onClick={() => startTrial(index)}>{I18n.get('Start')}</Button>
<Button style={{ marginLeft: "10px" }} onClick={() => resetTrial(index)}>{I18n.get('Reset')}</Button>
<Button style={{ marginLeft: "10px" }} onClick={() => completeTrial(index)}>{I18n.get('Stop')}</Button>
</Col>
</Row>
)
};
return (
<div>
{getTrialsButtonGroup(0)}
</div>
);
}
When used in the above React component, Stopwatch has several instances initialized as it can be seen that the constructor is run several times.
In this JSFiddle snippet the constructor runs only once but the same issue occurs when stopwatch.stop() is triggered using the console.
I am trying to be able to use cntrl+s while focus within a textarea using react-hotkeys.
this.keyMap = {
KEY: "ctrl+s"
};
this.handlers = {
KEY: (e) => {
e.preventDefault();
this.saveBtn(c);
}
};
<HotKeys keyMap={this.keyMap} handlers={this.handlers}>
<textarea/>
</HotKeys>
You need to use Control+s, not ctrl+s.
You need to call configure like that so it won't ignore textareas:
import { configure } from "react-hotkeys";
configure({
ignoreTags: []
});
Following is not solution it's work around but it fulfills the requirement...
[Please Note] Basically I have restricted access to Ctrl key in browser and then it
works fine though.
import { HotKeys } from 'react-hotkeys';
import React, { PureComponent, Component } from 'react';
import { configure } from 'react-hotkeys';
const COLORS = ['green', 'purple', 'orange', 'grey', 'pink'];
const ACTION_KEY_MAP = {
KEY: 'Control+s',
};
class Login extends Component {
constructor(props, context) {
super(props, context);
this.changeColor = this.changeColor.bind(this);
configure({
ignoreTags: ['div']
});
this.state = {
colorNumber: 0
};
}
changeColor(e) {
e.preventDefault();
this.setState(({ colorNumber }) => ({ colorNumber: colorNumber === COLORS.length - 1 ? 0 : colorNumber + 1 }));
}
KeyDown(e){
if(e.ctrlKey) e.preventDefault();
}
render() {
const handlers = {
KEY: this.changeColor
};
const { colorNumber } = this.state;
const style = {
width: 200,
height: 60,
left: 20,
top: 20,
opacity: 1,
background: COLORS[colorNumber],
};
return (
<HotKeys
keyMap={ACTION_KEY_MAP}
handlers={handlers}
>
<textarea
style={style}
className="node"
tabIndex="0"
onKeyDown={this.KeyDown}
></textarea>
</HotKeys>
);
}
}
export default Login;
Every element of the array should be displayed for some time and the time for which each element is displayed should be determined by a value in each element.
let array=[{display:"a",time:10},{display:"b",time:15},{display:"c",time:22}]
class App extends React.Component{
state={stateDisplay:"",
stateTime:""
}
componentWillMount(){
var i=0;
let handle=setInterval(()=>{
var element= array[i]
this.setState({
stateDisplay:element.display,
stateTime:element.time,
})
i=i+1;
if(i===array.length){
clearInterval(handle)
}
},10000)
}
render(){
return(
<div> {this.state.stateDisplay} </div>
)}}
i have done something like this but using setinterval the delay can only be set for a constant time,here 10s.
I want the first element to display for 10s and then the next element for 15s, third for 22s which is the time value for each element of the array.
I know i cant do that using setinterval is there a way to do this using Settimeout?
This was almost like a little challenge, heres what i managed to come up with, its in typescript, if you need js, just remove interfaces and type annotations
/* eslint-disable #typescript-eslint/no-explicit-any */
/* eslint-disable prettier/prettier */
/* eslint-disable no-shadow */
/* eslint-disable no-console */
import React, { FC, useState, useEffect, useCallback } from 'react';
import { View, Button, Text } from 'react-native';
interface Data {
duration: number;
bgColor: string;
}
const dataArr: Data[] = [
{ duration: 3, bgColor: 'tomato' },
{ duration: 6, bgColor: 'skyblue' },
{ duration: 9, bgColor: 'gray' },
];
const Parent = () => {
const [currentIdx, setCurrentIdx] = useState<number>(0);
const [elementData, setElementData] = useState<Data>(dataArr[currentIdx]);
useEffect(() => {
console.log('idx', currentIdx);
if (currentIdx > dataArr.length) return;
setElementData({ ...dataArr[currentIdx] });
}, [currentIdx]);
const pushNext = () => {
setCurrentIdx(currentIdx + 1);
};
const handleRestart = () => {
setCurrentIdx(0);
setElementData({ ...dataArr[0] });
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Timer
data={elementData}
onCountDownComplete={pushNext}
restart={handleRestart}
/>
</View>
);
};
interface Props {
data: Data;
onCountDownComplete: () => void;
restart: () => void;
}
const Timer: FC<Props> = ({ data, onCountDownComplete, restart }) => {
const [seconds, setSeconds] = useState<number>(data.duration);
// update on data change
useEffect(() => {
setSeconds(data.duration);
}, [data]);
const callback = useCallback(() => {
onCountDownComplete();
}, [onCountDownComplete]);
useEffect(() => {
let interval: any = null;
if (seconds > -1) {
interval = setInterval(() => {
if (seconds - 1 === -1) {
callback();
} else {
setSeconds(seconds - 1);
}
}, 1000);
} else {
return;
}
return () => {
clearInterval(interval);
};
}, [seconds, callback]);
return (
<View
style={{ backgroundColor: data.bgColor, padding: 16, borderRadius: 10 }}
>
<Text style={{ marginBottom: 24 }}>{seconds}</Text>
<Button title="restart" onPress={restart} />
</View>
);
};
I have two pens, and I'm trying to use a React component I defined in one pen inside another, but I'm not clear on how Codepen actually handles React imports between pens. I went to the destination pen and added the source pen's address to the Javascript references, but I don't know how to proceed from there. I can get this to work in a local node project using traditional export, but the Codepen element is giving me trouble. Here's the code:
SOURCE (https://codepen.io/ejpg/pen/LmOVoR):
export default class Wheel extends React.Component // Export default causes error
{
constructor(props){
super(props);
this.state = {
spin : false,
value: 0
};
this.spin = this.spin.bind(this);
}
spin(e){
var val = this.state.value + 720 + (Math.floor(Math.random() * 24) * 15);
console.log((this.state.value % 360) / 15);
e.target.style.webkitTransform = 'rotate(' + -val + 'deg)';
e.target.style.webkitTransition = '-webkit-transform 4s ease-out';
this.setState({value: val});
}
render(){
const wheelVals = [800, "BANKRUPT", 200, 300, 350, 250, 400, 300, 200, 250, 500, 350, 250,
"BANKRUPT", 200, 300, 400, 250, 600, "LOSE A TURN", 200, 300, 250, 200];
return (<div><img width="400" height="400" src="https://orig00.deviantart.net/0a38/f/2010/242/f/6/singapore_wheel_of_fortune_by_wheelgenius-d2xmb9v.jpg" onClick={(e) => this.spin(e)}/><br/><br/>{wheelVals[(this.state.value % 360) / 15]}
</div>);
}
}
DESTINATION (https://codepen.io/ejpg/pen/bMgWpN):
let { Grid, Row, Col, ButtonToolbar, Button } = ReactBootstrap;
// How do I import the class?
class CustomButton extends React.Component {
onHandleClick = () => {
this.props.onClick();
};
render(){
return <Button bsStyle={this.props.bsStyle} onClick={this.onHandleClick}><strong>{this.props.text}</strong></Button>;
}
}
class Letter extends React.Component {
onHandleClick = () => {
this.props.onClick(this.props.letter);
};
render () {
const style = { border: '1px solid black',
display: 'inline-block',
fontSize: '3.5vw',
width: '4vw',
height: '10vh',
textAlign: 'center',
whiteSpace: 'no-wrap',
overflow: 'hidden'};
if (this.props.letter === ' ') style.border = '';
return (
<div
style={style}
key={this.props.key}
onClick={this.onHandleClick} // Have to pass onClick to div
>
{this.props.letter}
</div>
);
}
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
var blanks = '';
for (var i = 0; i < this.props.answer.length; ++i)
{
this.props.answer[i] === ' ' ?
blanks += ' ': blanks += '-';
}
this.state = {
phrase: blanks,
alpha: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
bonus: false,
revealed: false
};
this.callLetter = this.callLetter.bind(this);
this.bonusRound = this.bonusRound.bind(this);
this.complete = this.complete.bind(this);
}
replaceAt(str, index, replacement) {
return str.substr(0, index) + replacement + str.substr(index + replacement.length);
}
complete(){
if (this.state.revealed === false)
{
this.setState({phrase: this.props.answer, revealed: true});
}
}
checkForLetter(letter, phr)
{
this.setState((prevState, props) => {
var prephrase = prevState.phrase;
var index = phr.indexOf(letter);
while( index !== -1)
{
prephrase = this.replaceAt(prephrase, index, letter);
index = phr.indexOf(letter, index + 1);
}
return ({phrase: prephrase});
});
}
callLetter(letter) {
this.setState((prevState, props) => {
var alphaclone = prevState.alpha;
var letterindex = alphaclone.indexOf(letter);
alphaclone = alphaclone.slice(0, letterindex) + alphaclone.slice(letterindex + 1);
return ({alpha: alphaclone});
});
this.checkForLetter(letter, this.props.answer);
}
bonusRound(){
if (this.state.bonus === false)
{
this.callLetter('R');
this.callLetter('S');
this.callLetter('T');
this.callLetter('L');
this.callLetter('N');
this.callLetter('E');
this.setState({bonus: true});
}
}
render() {
return (
<Grid>
<Row className="show-grid" >
{
this.state.phrase.split(' ').map((item, j) =>
(
<div style = {{display:'inline-block'}}>
<Letter letter = {' '}/>
{item.split('').map((item, i) =>
(
<Letter letter= {item}/>
)) }
</div>
))
}
</Row>
<Row className="show-grid" style={{margin: '3vh'}}>
{
this.state.alpha.split('').map((item, i) =>
(
<Letter letter={item} key={i} onClick={this.callLetter}/>
))
}
</Row>
<Row className="show-grid" style={{margin: '3vh'}}>
<ButtonToolbar>
<CustomButton bsStyle = {"primary"} text= {"BONUS ROUND"} onClick = {this.bonusRound}/>
<CustomButton bsStyle = {"danger"} text= {"REVEAL ALL"} onClick = {this.complete}/>
</ButtonToolbar>
</Row>
</Grid>
);
}
}
ReactDOM.render(
<MyComponent answer='A VERY VERY EXCESSIVELY LONG TEST'/>,
document.getElementsByClassName('container-fluid')[0]
);
Any help is greatly appreciated.
EDIT: I can't believe I actually have to explicitly state that I don't want to copy and paste it.
For that you have to make your pen containing the component as a module. You can do this by going to Settings > Javascript and checking the Add type="module" checkbox.
Then you can import the component in another pen using the URL of your pen:
import MyComponent from 'https://codepen.io/user/pen/xyz.js';
The entire doc regarding this may be found here: https://blog.codepen.io/2017/12/26/adding-typemodule-scripts-pens/.
Hope this helps :)
I have the following page in React:
#connect((store) => {
return {
all_orders_data: store.trends.all_orders_data,
...etc...
};
})
class OrdersController extends MyComponent {
constructor(props){
let custom_methods = ['refreshData', 'runDispatch'];
super(props, custom_methods);
this.state = {last_updated_at: new Date()};
}
componentWillMount() {
this.runDispatch();
// this.props.dispatch(fetchOrderData());
}
componentDidMount() {
this.refreshTimerID = setInterval(
() => this.refreshData(),
300 * 1000 // 5 minutes
);
}
refreshData() {
console.log('running refresh');
this.runDispatch(true);
this.setState({last_updated_at: new Date()});
}
runDispatch(refresh=false) {
this.props.dispatch(fetchOrderTrendingAllOrdersData(refresh));
}
render() {
return (
<div>
<h1>Order Trending</h1>
{last_updated_at}
<br/>
<div className="card col-md-12">
<h2 className="style-1">All Orders</h2>
<LineChart
data={all_orders_data}
fetched={fetched_all_orders_data}
error={error_in_fetching_all_orders_data}
/>
</div>
</div>
)
}
In the render I unpack the props, the last_updated header, and render a line chart.
I want to be able to click buttons to toggle lines on the chart. To do this, I have to keep track of the keys for the data variable to show as lines.
I can't put these line options in the constructor because the runDispatch isn't fired off until componentWillMount.
I can't put it in componentWillMount because I don't know when runDispatch will return for data until it does return.
I can get the keys for the all_orders_data in the reducer and pass it to OrdersController as a prop, but props can't be changed and I want this page of our app to control the current lines showing.
I don't want to put it on the chart component because it get's new props every time to refresh runs, and I don't know if it will maintain the proper state after refresh.
The toggle setting doesn't need to be retained later, only while the controller is active (if they pick a new link I don't care if it resets).
My gut is to put state on the line chart since it doesn't have to be permanent.
Where is the correct place to keep the state of these line keys, like:
{
all_orders: ['Online orders', 'Web orders', ...]
}
to let the user toggle what lines he wants to see on graph? It can be on the LineChart, the controller, a new redux prop, etc.
I decided to put it on the LineChart itself, since the buttons toggle lines on it. It wasn't working, but you can protect the state in componentWillReceiveProps:
import React from 'react';
import ReactLoading from 'react-loading';
import {
VictoryChart,
VictoryLine,
VictoryTheme,
VictoryAxis,
VictoryTooltip,
VictoryBar,
VictoryVoronoiContainer
} from 'victory';
import _ from 'underscore';
import MyComponent from '../main/MyComponent';
const COLOR_OPTIONS = [
'#c43a31', // dark red
'blue',
'green',
'yellow',
'purple',
'teal',
'orange'
];
function getTimestringFromUnixTimestamp(timestamp) {
// just default it to AM for now
let period = 'AM'
let date = new Date(timestamp);
let hours = date.getHours();
let minutes = date.getMinutes();
if (hours >= 12) {
period = 'PM';
}
if (hours == 0) {
hours += 12;
} else if (hours >= 13) {
hours -= 12;
}
hours = "0" + hours;
minutes = "0" + minutes;
// Will display time in 10:30 AM format
let formattedTime = `${hours.substr(-2)}:${minutes.substr(-2)} ${period}`;
return formattedTime
}
function displayTooltips(data) {
// x is the unix timestamp, y is the order count
let { x, y } = data;
let formattedTime = getTimestringFromUnixTimestamp(x);
// https://stackoverflow.com/questions/847185/convert-a-unix-timestamp-to-time-in-javascript
return `Time - ${formattedTime}\nOrder Count - ${y}`
}
export default class LineChart extends MyComponent {
constructor(props) {
let custom_methods = [
'generateVictoryLines',
'generateToggleButtons',
'toggleItemOnclick',
'toggleAllLines',
];
super(props, custom_methods);
this.state = {
active_line_keys: [],
available_line_keys: []
};
}
componentWillReceiveProps(nextProps) {
console.log('\n\ncomponentWillReceiveProps:');
let data = nextProps.data;
console.log(data);
if (data) {
let line_keys = Object.keys(data);
console.log('line_keys:');
console.log(line_keys);
console.log('this.state.available_line_keys:');
console.log( this.state.available_line_keys);
let is_equal = _.isEqual(_.sortBy(line_keys), _.sortBy(this.state.available_line_keys));
if (!is_equal) {
console.log('line keys are diff; need to update state');
this.setState({
available_line_keys: line_keys,
active_line_keys: line_keys
});
}
}
}
generateVictoryLines() {
return this.state.active_line_keys.map((key, index) => {
let this_keys_permanent_index = this.state.available_line_keys.indexOf(key);
let color = COLOR_OPTIONS[this_keys_permanent_index];
return (
<VictoryLine
labels={displayTooltips}
labelComponent={<VictoryTooltip/>}
style={{
data: { stroke: `${color}` },
parent: { border: `1px solid #ccc`}
}}
data={this.props.data[key]}
/>
)
});
}
generateToggleButtons() {
return this.state.available_line_keys.map((key, index) => {
let this_keys_permanent_index = this.state.available_line_keys.indexOf(key);
let color = COLOR_OPTIONS[this_keys_permanent_index];
console.log(key);
return (
<button onClick={this.toggleItemOnclick.bind(null, key)} style={ {color: color}}>{key}</button>
);
})
}
toggleItemOnclick(name) {
console.log('\ntoggleItemOnclick:');
console.log(name);
console.log(this.state);
let is_in_active_line_keys = this.state.active_line_keys.indexOf(name) != -1;
console.log(is_in_active_line_keys);
let new_active_line_keys;
if (is_in_active_line_keys) {
new_active_line_keys = this.state.active_line_keys.filter(e => e !== name); // e is each item in the list; filter
// this.setState({active_line_keys: new_active_line_keys});
} else {
new_active_line_keys = this.state.active_line_keys.slice();
new_active_line_keys.push(name);
}
console.log(new_active_line_keys);
this.setState({active_line_keys: new_active_line_keys});
// arr = arr.filter(e => e !== el);
}
toggleAllLines() {
if (this.state.active_line_keys.length < this.state.available_line_keys.length) {
this.setState({active_line_keys: this.state.available_line_keys});
} else {
this.setState({active_line_keys: []});
}
}
// addAllLines() {
// this.setState({active_line_keys: this.state.available_line_keys});
// }
render() {
let graph_body;
let { data, error, fetched } = this.props; // we don't need data here
let victory_lines = this.generateVictoryLines();
let buttons = this.generateToggleButtons();
if (error) {
// alert(error);
console.log('\n\nerror:');
console.log(error);
graph_body = (<h2 className="centered">Error retrieving data</h2>);
} else if (fetched == false) {
graph_body = (
<div className="card col-md-12">
<span><h2 style={{fontWeight: "bold", fontSize: "25px", color: "#F96302"}}>Just a moment...Your request is being processed.</h2>
<ReactLoading type="cylon" color="#F96302" /></span>
</div>
)
} else {
try {
// in the victoryLine, interpolation="natural" will smooth graph out
graph_body = (
<div>
<VictoryChart
theme={VictoryTheme.material}
scale={{x: "time", y: "linear"}}
animate={{duration: 650}}
containerComponent={<VictoryVoronoiContainer/>}
width={1500}
height={600}
>
<VictoryAxis style={ { tickLabels: {fontSize: '20px'} } }/>
<VictoryAxis
dependentAxis
tickValues={[20, 40, 60, 80, 100]}
style={ { tickLabels: {fontSize: '20px'} } }
/>
{victory_lines}
</VictoryChart>
<button onClick={this.toggleAllLines}>Toggle lines</button>
{buttons}
</div>
)
} catch(err) {
graph_body = (<h2 className="centered">Error using response: {`${err}`}</h2>);
}
}
return (
<div>
{graph_body}
</div>
)
}
}