React Timer + Color Change - reactjs

i have a simple div element in react, now i would like to be able to preset a time somewhere and for example after 10 minutes. The div would change color.
Does anyone know if that is possible to achieven?

Use the componentDidMount API to init timer, and don't forget to remove it at componentWillUnmount.
class App extends Component {
constructor() {
super()
this.state = {
color: 'blue'
}
}
handleChangeColor = (newColor) => {
this.setState({
color: newColor
})
}
componentDidMount() {
this.timer = setTimeout(
() => this.handleChangeColor('red'),
1000*3 // in milliseconds, 3s for fast show
)
}
componentWillUnmount() {
clearTimeout(this.timer)
}
render() {
return (
<div style={ { background: this.state.color} }>
Color Div
</div>
)
}
}
For full code, check here.

Related

React why my clearInterval cannot stop setInterval?

I am totally new to React. What I try to implement is a timer. When click on the hours, minute or second the timer will stop and for the selected one turns into an input field when enter button has been click it should not show any more input fields and start back the clock.
how it looks like
I try to stop passing new props to the child component when I click the flexbox container. I wrote a handleClick function and setInterval() or clearInterval() base on update variable in the state.
What I want is when I click any of the hour/minute/second, the select filed will change to the input filed, and the timer stop. Once I hit enter it will back to the timer.
class Timer extends React.Component{
constructor(props){
super (props);
this.timerRef = React.createRef();
this.state = {
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
update:true,
};
}
tick() {
this.setState({
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
})
}
componentDidMount(){
this.intervalId = setInterval(
() => this.tick(), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
handleClick(){
this.setState(state => ({update: !state.update}));
console.log("1",this.state.update);
if (this.state.update){
this.intervalId = setInterval(
() => this.tick(), 1000
);
console.log("2 set interval",this.intervalId);
}
else{
clearInterval(this.intervalId);
console.log("2 clear interval");
}
}
render() {
const { hh, mm, ss, suffix } = this.state;
return (
<div className="box" > London Clock
<div className="flexbox-container" onClick={() => this.handleClick()}>
<Content time={hh}></Content>
<div><p>:</p></div>
<Content time={mm}></Content>
<div><p>:</p></div>
<Content time={ss}></Content>
<div className="suffix"><p>{suffix}</p></div>
</div>
</div>
);
}
}
class Content extends React.Component {
state = {
editMode: false,
time: ""
};
componentDidMount(){
this.setState({
time: this.props.time,
})
}
handleKeyDown(event){
console.log(event,event.key === 'Enter');
if (event.key === 'Enter'){
this.setState({
editMode: false,
})
}
}
render() {
const {editMode} = this.state;
return (
<div>
{editMode? (
<p>
<input
defaultValue={this.props.time}
onKeyPress={e => this.handleKeyDown(e)}
/>
</p>
) : (
<p onClick={() => this.setState({ editMode: true })}>{this.props.time}</p>
)}
</div>
);
}
}
ReactDOM.render(
<Timer/>,
document.body
);
.flexbox-container {
display: flex;
flex-direction: row;
}
.suffix{
padding-left: 20px;
}
.box{
border-style: solid;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
When your component is mounted, it will start an interval and assign it to intervalId.
Your clickhandler modifies the state and then immediately attemtps to look at the state, without waiting for it to be updated. The state likely is not updated at this point, so it reassigns the interval, resulting in a zombie-updater.
Either pass a callback along to setState(updater, [callback]) or move your interval-logic to componentDidUpdate, which would allow you to de-duplicate interval logic
Try like below instead of setting to state and bind your tick function.
componentDidMount(){
this.intervalId = setInterval(
() => this.tick.bind(this), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
I think you can use React's Ref instead of the state.
constructor(props) {
this.timerRef = React.createRef();
}
componentDidMount() {
this.timerRef.current = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.timerRef.current);
}

Show a Simple Animation on setState in react

I display a data from JSON file to the DIV in my component.
I Set timeout for few seconds and after that the data displays.
I want to show a simple animation when the state changes to true.
Here is a sample of my Code Structure:
import someData from "../Data";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = { contentLoad: false }
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
})
}, 2500)
}
render() {
return (
<div>
{someData.map((someData) => {
return (
<div> {this.state.contentLoad && someData.name}</div>
)
})}
</div>
)
}
}
I read about react transition group, but cant understand cause i'm new to react. someone please use this as a template and provide me a codepen or codesandbox link for solution.
I agree with #JohnRuddell that Pose is a heavy library if all you need is a simple fade in. However I would look it if you are doing multiple animations that are more complex.
Sample code:
import React from "react";
import ReactDOM from "react-dom";
import posed from "react-pose";
import "./styles.css";
const AnimatedDiv = posed.div({
hidden: { opacity: 0 },
visible: { opacity: 1 }
});
class Example2 extends React.Component {
constructor(props) {
super(props);
this.state = { contentLoad: false };
}
componentDidMount() {
setTimeout(() => {
this.setState({
contentLoad: true
});
}, 2500);
}
someData = ["hello", "hi", "test"];
render() {
return (
<AnimatedDiv pose={this.state.contentLoad ? "visible" : "hidden"}>
{this.someData.map(t => {
return <div>{t}</div>;
})}
</AnimatedDiv>
);
}
}
ReactDOM.render(<Example2 />, document.getElementById("root"));
Sandbox link

Event listeners and refs in React 16

I have an element whose width I want to set equal to the parent both when the element is rendered and when the parent is resized. I'm using the new React.createRef API to achieve this and currently have the following:
class Footer extends Component {
constructor(props) {
super(props);
this.footerRef = React.createRef();
this.state = { width: 0 };
}
updateWidth() {
const width = this.footerRef.current.parentNode.clientWidth;
this.setState({ width });
}
componentDidMount() {
this.updateWidth();
this.footerRef.current.addEventListener("resize", this.updateWidth);
}
componentWillUnmount() {
this.footerRef.current.removeEventListener("resize", this.updateWidth);
}
render() {
const { light, setEqualToParentWidth, className, ...props } = this.props;
const style = setEqualToParentWidth
? { ...props.style, width: this.state.width }
: { ...props.style };
return (
<footer
{...props}
ref={this.footerRef}
style={style}
data-ut="footer"
/>
);
}
}
This seems to compile without any errors and accurately sizes itself on mount. However, once it has mounted, changing the viewport width does not change the width of the footer. Am I attaching the event listener incorrectly?
I also initially tried attaching the event listener to window, but that resulted in TypeError: Cannot read property 'current' of undefined on the first line of updateWidth when I attempted to resize my screen.
How can I fix this?
You need to use the window resize event. When you assign the event listener you need to bind to the proper scope inside your constructor this.updateWidth = this.updateWidth.bind(this);
This should also be debounced.
Try this:
class FooterBase extends Component {
constructor(props) {
super(props);
this.footerRef = React.createRef();
this.updateWidth = this.updateWidth.bind(this);
this.state = { width: 0 };
}
updateWidth() {
const width = this.footerRef.current.parentNode.clientWidth;
this.setState({ width });
}
componentDidMount() {
this.updateWidth();
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
render() {
const { light, setEqualToParentWidth, className, ...props } = this.props;
const style = setEqualToParentWidth
? { ...props.style, width: this.state.width }
: { ...props.style };
return (
<footer
{...props}
ref={this.footerRef}
style={style}
data-ut="footer"
></footer>
);
}
}
DEMO

ReactJS. Infinity loop

I'am getting props from child in getCount function. And set it prop into state. Than i try set it in component and get infinity loop. How can i fix that?
There is code of parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: count
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
child:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = { h: 0 };
this.counter = 0;
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
render() {
this.counter++;
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
The problem lies in your Message component.
You are using getCount() inside your componentDidUpdate() method. This causes your parent to re-render, and in turn your Message component to re-render. Each re-render triggers another re-render and the loop never stops.
You probably want to add a check to only run the function if the props have changed. Something like:
componentDidUpdate(prevProps) {
if(prevProps.color !== this.props.color) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
}
This will keep the functionality you need, but prevent, not only the infinity-loop, but also unnecessary updates.

Render Clappr player in ReactJS

I'm using Clappr player with ReactJS.
I want Clappr player component appear and destroy when I click to a toggle button. But it seems like when Clappr player is created, the entire page has reload (the toggle button dissapear and appear in a blink). So here is my code:
ClapprComponent.js
import React, { Component } from 'react'
import Clappr from 'clappr'
class ClapprComponent extends Component {
shouldComponentUpdate(nextProps) {
let changed = (nextProps.source != this.props.source)
if (changed) {
this.change(nextProps.source)
}
return false
}
componentDidMount() {
this.change(this.props.source)
}
componentWillUnmount() {
this.destroyPlayer()
}
destroyPlayer = () => {
if (this.player) {
this.player.destroy()
}
this.player = null
}
change = source => {
if (this.player) {
this.player.load(source)
return
}
const { id, width, height } = this.props
this.player = new Clappr.Player({
baseUrl: "/assets/clappr",
parent: `#${id}`,
source: source,
autoPlay: true,
width: width,
height: height
})
}
render() {
const { id } = this.props
return (
<div id={id}></div>
)
}
}
export default ClapprComponent
Video.js
import React, { Component } from 'react'
import { Clappr } from '../components'
class VideoList extends Component {
constructor() {
super()
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
const boxStyle = {
width: "640",
height: "360",
border: "2px solid",
margin: "0 auto"
}
return (
<div>
<div style={boxStyle}>
{this.state.isActive ?
<Clappr
id="video"
source="http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"
width="640"
height="360" />
: ''}
</div>
<button class="btn btn-primary" onClick={this.toggle.bind(this)}>Toggle</button>
</div>
)
}
}
export default VideoList
Anyone can explain why? And how to fix this problem?
Edit 1: I kind of understand why the button is reload. It's because in index.html <head>, I load some css. When the page is re-render, it load the css first, and then execute my app.min.js. The button doesn't reload in a blink if I move the css tags under the <script src="app.min.js"></script>.
But it doesn't solve my problem yet. Because the css files have to put in <head> tags. Any help? :(
Here you have a running (jsbin link) example. I simplified a little bit and it still shows your main requirement:
class ClapprComponent extends React.Component {
componentDidMount(){
const { id, source } = this.props;
this.clappr_player = new Clappr.Player({
parent: `#${id}`,
source: source
});
}
componentWillUnmount() {
this.clappr_player.destroy();
this.clappr_player = null;
}
render() {
const { id } = this.props;
return (
<div>
<p>Active</p>
<p id={id}></p>
</div>
);
}
}
class NotActive extends React.Component {
render() {
return (
<p>Not Active</p>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
return (
<div>
<h1>Clappr React Demo</h1>
{ this.state.isActive ?
<ClapprComponent
id="video"
source="http://www.html5videoplayer.net/videos/toystory.mp4"
/> :
<NotActive />}
<button onClick={this.toggle}>Toggle</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Also make sure to rename the button class property to className.
Maybe you can work from here to find your exact problem? Hope that helps.
In Clappr's documentation I found a like about how to use clappr with reactjs

Resources