REACT - Generate number automaticaly - javascript

I need help.
I'm trying to generate a random number in this code {number} every given second.
How can i do this?
I tried this, it generates the number randomly but doesn't update it every second.
var number;
(function repeat() {
number = Math.floor((Math.random()*100)+1);
setTimeout(repeat, 1000);
setInterval(repeat, 1000);
})();
import React from 'react'
import styled, { keyframes } from 'styled-components'
class ProgressBar extends React.Component {
render() {
const { text } = this.props
const ProgressContainer = styled.div`
margin-bottom: 25px;
`
const Text = styled.span`
font-size: 17px;
font-family: Poppins;
color: #fff;
`
const Value = styled.span`
font-size: 17px;
font-family: Poppins;
color: #fff;
float: right;
`
const ColorAnimation = keyframes`
0% {background: #04e5e5;}
10% {background: #f37055;}
20% {background: #ef4e7b;}
30% {background: #a166ab;}
40% {background: #5073b8;}
50% {background: #04e5e5;}
60% {background: #07b39b;}
70% {background: #6fba82;}
80% {background: #5073b8;}
90% {background: #1098ad;}
100% {background: #f37055;}
`
const Progress = styled.div`
height: 5px;
border-radius: 2.5px;
margin-top: 10px;
transition: 2s;
animation: ${ColorAnimation} 10s infinite alternate;
`
return(
<ProgressContainer>
<Text>{text}</Text>
<Value>{number}%</Value>
<Progress style={{width: `${number}%`}}></Progress>
</ProgressContainer>
)
}
Thanks

Using the useEffect hook, you can create the needed setInterval and clean up when the component unmounts:
useEffect(() => {
const interval = setInterval(() => {
** code for random number goes here **
}, 1000);
return () => clearInterval(interval);
}, []);
In order to have the component re-render when ever the random number changes, you can utilize the useState hook:
const [randomNumber, setRandomNumber] = useState(null);
Putting it all together:
import React, {useState, useEffect} from "react";
const Container = () => {
const [randomNumber, setRandomNumber] = useState(null)
useEffect(() => {
const interval = setInterval(() => {
setRandomNumber(Math.floor((Math.random()*100)+1))
}, 1000);
return () => clearInterval(interval);
}, []);
return (<div>{randomNumber}</div>)
}
You can see it in action in this JSFiddle.

Related

Bad rendering result of Safari when components including blur & gradient styles

I'm trying to do someting like this:
It's a glowing spot follows the pointer
import { useEffect, useState } from "react";
import "./styles.css";
const CUBE_SIZE = 100;
export default function App() {
const [left, setLeft] = useState(window.innerWidth / 2);
const [top, setTop] = useState(window.innerHeight / 2);
const mouseMoveHandler = (event) => {
setLeft(`${event.clientX - CUBE_SIZE / 2}px`);
setTop(`${event.clientY - CUBE_SIZE / 2}px`);
};
useEffect(() => {
document.addEventListener("mousemove", mouseMoveHandler);
return () => {
document.removeEventListener("mousemove", mouseMoveHandler);
};
}, []);
return (
<div className="App">
<div
style={{ width: `${CUBE_SIZE}px`, height: `${CUBE_SIZE}px`, left, top }}
className="glowing-cube"
/>
</div>
);
}
body {
background-color: black;
}
.App {
font-family: sans-serif;
text-align: center;
}
.glowing-cube {
position: relative;
background: linear-gradient(216.89deg, #dc4b5c 6.73%, #5a53ff 81.32%);
filter: blur(32px);
border-radius: 31px;
transform: rotate(135deg);
}
The codesandbox link is below:
https://7h7cpj.csb.app/
Everything looks perfect in Chrome, while it looks bad on Safari:
I found blur is supported since Safari 6 ref
Then what's the problem?
And, how can I do to improve this?
Is there a feasible solution on Safari?

eventListener "animationend " behave unpredictably, or maybe it's because of setState function

I'm trying to make a toast message component that works like this.
It comes in from outside the right screen, disappears out of the screen after a certain period of time, and the component get deleted after the animation.
It works perfectly when I make a single component. However, when I create multiple toast components, some of them just get removed before the animation starts.
I tried many ways for 3days, but nothing worked... 😭 I would appreciate it if you could check which part is wrong. Help me plz
Toast Contatiner
import React, { useState, useCallback } from 'react';
import ToastItem from './Toast';
import styled from 'styled-components';
type ToastItemInfo = {
content: string | React.ReactNode;
id: string;
};
const ToastContainer = () => {
const [toastItems, setToastItems] = useState<ToastItemInfo[]>([]);
const handleClick = () => {
setToastItems((toastItems) => [
{
content: 'Toasted !',
id: String(Math.random()).substring(2, 8),
},
...toastItems,
]);
};
const removeToast = useCallback((id: string) => {
setToastItems((toastItems) => toastItems.filter((toast) => toast.id !== id));
}, []);
const hasItems = (toastItems: ToastItemInfo[]) => {
return toastItems.length > 0;
};
const mapItems = (toastItems: ToastItemInfo[]) => {
return toastItems.map((toast) => (
<ToastItem key={toast.id} id={toast.id} removeToast={removeToast}>
{toast.content}
</ToastItem>
));
};
return (
<div>
<Button onClick={handleClick}>Toast 🍞</Button>
{hasItems(toastItems) && <Container>{mapItems(toastItems)}</Container>}
</div>
);
};
const Container = styled.div`
position: fixed;
display: flex;
flex-direction: column;
gap: 8px;
top: 0;
right: 0;
margin: 16px;
`;
// .slideout {
// animation: 0.5s linear animate;
// }
// #keyframes animate {
// from {
// transform: translateX(0);
// }
// to {
// transform: translateX(120%);
// }
// }
const Button = styled.button`
width: 132px;
height: 40px;
background: #434253;
color: white;
font-size: 1rem;
border: 2px solid white;
cursor: pointer;
`;
export default ToastContainer;
ToastItem
import React, { memo, useEffect, useRef, useState } from 'react';
import styled, { keyframes } from 'styled-components';
import ProgressBar from './ProgressBar';
interface ToastProps {
id: string;
children: string | React.ReactNode;
removeToast: (id: string) => void;
}
const autoCloseDelay = 3000;
const ToastItem: React.FC<ToastProps> = (props) => {
const { id, removeToast, children } = props;
const [toClose, setToClose] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const removeFn = () => {
removeToast(id);
};
const { current } = ref;
const timeout = setTimeout(() => {
current?.addEventListener('animationend', () => {
removeToast(id);
});
setToClose(true);
}, autoCloseDelay);
return () => {
clearTimeout(timeout);
current?.removeEventListener('animationend', removeFn);
};
}, [id, removeToast]);
return (
<Toast id={id} ref={ref} onClick={() => removeToast(id)} toClose={toClose}>
{children}
<ProgressBar autoCloseDelay={autoCloseDelay} />
</Toast>
);
};
const slideIn = keyframes`
from{
transform: translateX(120%);
}
to{
transform: translateX(0);
}
`;
const slideOut = keyframes`
from{
transform: translateX(0);
}
to{
transform: translateX(120%);
}
`;
const Toast = styled.div<{ toClose: boolean }>`
box-sizing: border-box;
position: relative;
padding: 0 16px;
width: 250px;
height: 58px;
line-height: 58px;
background: #fabb4d;
color: #5f2700;
border-radius: 2px;
cursor: pointer;
animation: 0.5s ease 0s ${(props) => (props.toClose ? slideOut : slideIn)};
&:hover {
transform: scale(1.05);
}
`;
export default memo(ToastItem);
enter image description here

How to make a React component fade in on scroll using IntersectionObserver, but only once?

I am trying to give components a fade-in effect in React when the user scrolls, but I want the fade-in effect to only happen the first time the element moves into the viewport.
Currently, the code I am using causes a fade-in every time the element moves into the viewport, so they are constantly fading in and out.
Here is my fade-in component:
import React, {useState, useRef, useEffect} from 'react';
import './styles/FadeInSection.css';
export default function FadeInSection(props) {
const [isVisible, setVisible] = useState(true);
const domRef = React.useRef();
useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => setVisible(entry.isIntersecting));
});
observer.observe(domRef.current);
return () => observer.unobserve(domRef.current);
}, []);
return (
<div ref={ domRef } className={ `fade-in-section ${ isVisible ? 'is-visible' : '' }` }>
{ props.children }
</div>
)
}
And these are the styles I'm using:
.fade-in-section {
opacity: 0;
transform: translateY(20vh);
isibility: hidden;
transition: opacity 0.2s ease-out, transform 0.6s ease-out;
will-change: opacity, visibility;
}
.fade-in-section.is-visible {
opacity: 1;
transform: none;
visibility: visible;
display: flex;
}
Here is my website, which keeps fading components in and out, offering a terrible experience:
And this is the desired effect:
How can I achieve the desired effect?
Here is a link to the code sandbox to test it: Code sandbox link
You only need to call setVisible if entry.isIntersecting is true, so simply replace:
setVisible(entry.isIntersecting);
With:
entry.isIntersecting && setVisible(true);
This way, once an entry has already been marked as visible, it won't be unmarked, even if you scroll back up, so the element goes out of the viewport, and entry.isIntersecting becomes false again.
Actually, you can even call observer.unobserve at that point, as you don't care anymore.
const FadeInSection = ({
children,
}) => {
const domRef = React.useRef();
const [isVisible, setVisible] = React.useState(false);
React.useEffect(() => {
const observer = new IntersectionObserver(entries => {
// In your case there's only one element to observe:
if (entries[0].isIntersecting) {
// Not possible to set it back to false like this:
setVisible(true);
// No need to keep observing:
observer.unobserve(domRef.current);
}
});
observer.observe(domRef.current);
return () => observer.disconnect();
}, []);
return (<section ref={ domRef } className={ isVisible ? ' is-visible' : '' }>{ children }</section>);
};
const App = () => {
const items = [1, 2, 3, 4, 5, 6, 7, 8].map(number => (
<FadeInSection key={ number }>Section { number }</FadeInSection>
));
return (<main>{ items }</main>);
}
ReactDOM.render(<App />, document.querySelector('#app'));
body {
font-family: monospace;
margin: 0;
}
section {
padding: 16px;
margin: 16px;
box-shadow: 0 0 8px rgba(0, 0, 0, .125);
height: 64px;
opacity: 0;
transform: translate(0, 50%);
visibility: hidden;
transition: opacity 300ms ease-out, transform 300ms ease-out;
will-change: opacity, visibility;
}
.is-visible {
opacity: 1;
transform: none;
visibility: visible;
display: flex;
}
<script src="https://unpkg.com/react#16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.12.0/umd/react-dom.development.js"></script>
<div id="app"></div>

prevent spacing on sides when creating css ticker

I am creating a news ticker but am having issues showing it in a continuous loop. As you can see from the code sandbox here there is gaps on both sides of the ticker at the beginning and at the end. Perhaps it isn't possible to do this in pure css? and maybe I need to use js to manipulate the array. Can anyone give me any guidance on the approach I should take?
I am talking about the blue gap on the right and left at the beginning and end of the animation
import styled, { keyframes } from "styled-components";
const customTicker = keyframes`
0% {
transform: translate3d(1000%, 0, 0);
visibility: visible;
}
100% {
transform: translate3d(-1000%, 0, 0);
}
`;
const CryptosWrapper = styled.div`
background: #123962;
overflow: hidden;
`;
const TickerWrap = styled.div`
display: flex;
overflow: hidden;
`;
const CryptoWrapper = styled.div`
color: white;
flex: 1 0 100px;
animation: ${customTicker} 7s infinite;
animation-timing-function: linear;
`;
const Currency = styled.span`
color: #5d81a6;
`;
const Heading = styled.p`
font-size: 12px;
`;
function App() {
const [cryptos, setCryptos] = React.useState(null);
React.useEffect(() => {
fetch("https://api.coincap.io/v2/assets")
.then(res => {
return res.json();
})
.then(res => {
setCryptos(res.data.slice(0, 10));
});
}, []);
return (
<CryptosWrapper>
<TickerWrap>
{cryptos &&
cryptos.map(crypto => {
return (
<CryptoWrapper key={crypto.id}>
<Heading>{crypto.symbol}/USD</Heading>
<p>
{parseFloat(crypto.priceUsd)
.toFixed(2)
.toLocaleString("en-US")}{" "}
<Currency>USD</Currency>
</p>
</CryptoWrapper>
);
})}
</TickerWrap>
</CryptosWrapper>
);
}
Here's a quick solution, which is somewhat of a hard-coded solution. Imo, there are more robust solutions that will be easier to maintain.
The issue with the current implementation is we don't have an infinite supply of content. We need to have an infinite loop of ticker symbols, so the last ticker symbol is immediately followed by the first ticker symbol.
A quick way to achieve this is to duplicate the ticker symbols. Something like:
setCryptos([...res.data.slice(0, 10), ...res.data.slice(0, 10)]);
This gives us a continuous stream of symbols, like:
A B C D E A B C D E
Now we can tweak the animation loop so it restarts at exactly the moment the 2nd starting symbol A reaches the same position as the 1st A, with a small tweak to the keyframes:
const customTicker = keyframes`
0% {
transform: translate3d(0%, 0, 0);
visibility: visible;
}
100% {
transform: translate3d(-1000%, 0, 0);
}
`;
And here's the result:
const customTicker = window.styled.keyframes`
0% {
transform: translateX(0%);
visibility: visible;
}
100% {
transform: translateX(-1000%);
}
`;
const CryptosWrapper = window.styled.div`
background: #123962;
overflow: hidden;
`;
const TickerWrap = window.styled.div`
display: flex;
overflow: hidden;
`;
const CryptoWrapper = window.styled.div`
color: white;
flex: 1 0 100px;
animation: ${customTicker} 7s infinite;
animation-timing-function: linear;
`;
const Currency = window.styled.span`
color: #5d81a6;
`;
const Heading = window.styled.p`
font-size: 12px;
`;
function App() {
const [cryptos, setCryptos] = React.useState(null);
React.useEffect(() => {
fetch("https://api.coincap.io/v2/assets")
.then(res => {
return res.json();
})
.then(res => {
setCryptos([...res.data.slice(0, 10), ...res.data.slice(0, 10)]);
});
}, []);
return (
<CryptosWrapper>
<TickerWrap>
{cryptos &&
cryptos.map(crypto => {
return (
<CryptoWrapper key={crypto.id}>
<Heading>{crypto.symbol}/USD</Heading>
<p>
{parseFloat(crypto.priceUsd)
.toFixed(2)
.toLocaleString("en-US")}{" "}
<Currency>USD</Currency>
</p>
</CryptoWrapper>
);
})}
</TickerWrap>
</CryptosWrapper>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id="root"></div>
As if superfluous. Why don't you use marquee?

Adding a loader to react component

I'm trying to implement a loader to my react component, when the background image is loading it should display 'loading' and then once it has loaded it should display 'loaded'
I have a setTimeout() on my componentwillMount() to test that the loader functions as expected which it does
I'm struggling to understand how it knows when the image is loaded and to change the loading state
Is it best to put the image into a seperate component with the loader rather than have it on the Hello component?
https://www.webpackbin.com/bins/-KsOEkf9ubvR6iz4bMxG
Update
I have managed to get a simple image loader working using the onload() method attached to the image - https://www.webpackbin.com/bins/-KsNpZPzveUoby1yriFo
Hello.js
import React from 'react'
import Loader from './Loader'
import styled from 'styled-components'
const Card = styled.div`
height: 400px;
width:20%;
background: url('https://www.planwallpaper.com/static/images/Light-Wood-Background-Wallpaper.jpg');
`
export default class Test extends React.Component {
constructor() {
super()
this.state = { loading:true}
}
componentWillMount()
{
setTimeout(() => this.setState({loading: false}), 3000)
console.log("componentDidMount");
}
render() {
return (
<div>
<Card>
<Loader loading={this.state.loading} />
</Card>
</div>
)
}
}
Loader.js
import React, { Component } from 'react'
import styled, { keyframes } from 'styled-components'
import { string } from 'prop-types'
const transition1 = keyframes`
0% { background: #F19939; }
33.33% { background: #F8CA8F; }
66.66% { background: #FBD8AE; }
100% { background: #F19939; }
`
const transition2 = keyframes`
0% { background: #FBD8AE; }
33.33% { background: #F19939; }
66.66% { background: #F8CA8F; }
100% { background: #FBD8AE; }
`
const transition3 = keyframes`
0% { background: #F8CA8F; }
33.33% { background: #FBD8AE; }
66.66% { background: #F19939; }
100% { background: #F8CA8F; }
`
const Box = styled.span`
height: 12px;
width: 12px;
margin: 0 3px 0 3px;
border-radius: 4px;
animation: 0.4s ${transition1 } infinite;
`
const Box2 = styled(Box)`
animation: 0.4s ${transition2 } infinite;
`
const Box3 = styled(Box)`
animation: 0.4s ${transition3 } infinite;
`
const TextWrap = styled.div`
display: flex;
flex: 0 0 100%;
justify-content: center;
color: #fff;
`
const Para = styled.p`
color: #fff
padding: 19px 0 0 0;
`
const ParaLoaded = styled(Para)`
color: #fff;
padding: 22px 0 0 0;
`
export default class Loader extends Component {
render() {
return (
<div >
<div >
<TextWrap>
{
this.props.loading
?
<Para>Loading...</Para>
:
<ParaLoaded>Loaded</ParaLoaded>
}
</TextWrap>
</div>
</div>
)
}
}
You can do it like this: https://www.webpackbin.com/bins/-KsOJpBVfpazfXghJAaF
LoadBackgroundImage.js
const LoadBackgroundImage = (component, imageUrl, seconds, success, failure) => {
let timeoutOccured = false;
const image = new Image();
const timeout = setTimeout(() => {
timeoutOccured = true;
failure();
}, seconds * 1000);
image.onload = () => {
clearTimeout(timeout);
component.style.backgroundImage = `url('${imageUrl}')`;
if (!timeoutOccured) success();
};
image.src = imageUrl;
};
export default LoadBackgroundImage;
Hello.js
import React from 'react'
import Loader from './Loader'
import styled from 'styled-components'
import LoadBackgroundImage from './LoadBackgroundImage'
const Card = styled.div`
height: 400px;
width:20%;
`
export default class Hello extends React.Component {
constructor() {
super()
this.state = { loading:true}
}
componentDidMount() {
LoadBackgroundImage(
this.card,
'https://www.planwallpaper.com/static/images/Light-Wood-Background-Wallpaper.jpg',
5,
() => this.setState({ loading: false }),
() => console.log('The image did not load in 5 seconds')
);
}
render() {
return (
<div>
<Card innerRef={card => this.card = card}>
<Loader loading={this.state.loading} />
</Card>
</div>
)
}
}
In render() you are using innerRef to obtain a reference to the card component and save it in this.card. Then in componentDidMount you are using this reference and LoadBackgroundImage function to load the image and monitor when it's loaded. If the image is loaded in given number of second success callback will be called, otherwise failure callback will be called. The image can still load after 5 seconds, but the success callback will not be called. If you want to be called anyway, you can skip this ckeck: if (!timeoutOccured) in LoadBackgroundImage function.

Categories

Resources