How to create timer in React-hooks - javascript

How i can create animated timer using React-hooks
Here is complete code what i had tried
Basically i was trying Displays the progress of time remaining as an animated ring.
But somehow i am getting failed in it
I just followed this blog for creating animated timer https://css-tricks.com/how-to-create-an-animated-countdown-timer-with-html-css-and-javascript/
function setRemainingPathColor(timeLeft) {
const { alert, warning, info } = COLOR_CODES;
console.log(dataFromDiv);
if (timeLeft <= alert.threshold) {
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.remove(warning.color);
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.add(alert.color);
} else if (timeLeft <= warning.threshold) {
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.remove(info.color);
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.add(warning.color);
}
}
React.useEffect(() => {
let timer;
let timePassed = 0;
let timeLeft;
timer = counter > 0 && setTimeout(() => setCounter(counter - 1), 1000);
timePassed = timePassed += 1;
timeLeft = counter - timePassed;
setRemainingPathColor(timeLeft);
return () => {
if (timer) {
clearTimeout(timer);
}
};
}, [counter]);

The error you were getting is because dataFromDiv.current.querySelectorAll(...) was always returning undefined because dataFromDiv.current was a reference to div#base-timer-path-remaining which is the element you wanted to modify. So, your code would work fine by just removing .querySelectorAll(...).
However, there are some better ways to structure your code:
Instead of doing direct dom manipulations, it's easier in this case to just figure out which color you want using useMemo to set up derived data based on the counter value.
You can also use an interval instead of a timer as it's easier to work with and a little bit cleaner. This also uses the updater function form of setCounter so that the effect doesn't need to have counter in the dependencies.
I also added a reset button to my example below so you don't have to re-run it every time.
const pathColor = React.useMemo(() => {
const { alert, warning, info } = COLOR_CODES;
if (counter <= alert.threshold) {
return alert.color;
} else if (counter <= warning.threshold) {
return warning.color;
} else {
return info.color;
}
}, [counter]);
React.useEffect(() => {
const timerId = setInterval(() => {
setCounter(counter => {
if (counter <= 0) {
clearInterval(timerId);
return counter;
}
return counter - 1;
});
}, 1000);
return () => {
clearInterval(timerId);
};
}, [timerReset]); // this timerReset is to make sure that the interval starts off again whenever the reset button is pressed.
This line is simply a way to force a re-render. The reducer function x=>x+1 increments the timerReset value whenever dispatch (renamed to resetTimer) is called. And then I use timerReset to force the effect to re-run in order to start the interval again (if it stopped)
const [timerReset, resetTimer] = React.useReducer(x => x + 1, 0);
const padTime = time => {
return String(time).length === 1 ? `0${time}` : `${time}`;
};
const format = time => {
const minutes = Math.floor(time / 60);
const seconds = time % 60;
return `${minutes}:${padTime(seconds)}`;
};
const WARNING_THRESHOLD = 10;
const ALERT_THRESHOLD = 5;
const COLOR_CODES = {
info: {
color: "green"
},
warning: {
color: "orange",
threshold: WARNING_THRESHOLD
},
alert: {
color: "red",
threshold: ALERT_THRESHOLD
}
};
function App() {
const [counter, setCounter] = React.useState(20);
const [timerReset, resetTimer] = React.useReducer(x => x + 1, 0);
const pathColor = React.useMemo(() => {
const { alert, warning, info } = COLOR_CODES;
if (counter <= alert.threshold) {
return alert.color;
} else if (counter <= warning.threshold) {
return warning.color;
} else {
return info.color;
}
}, [counter]);
React.useEffect(() => {
const timerId = setInterval(() => {
setCounter(counter => {
if (counter <= 0) {
clearInterval(timerId);
return counter;
}
return counter - 1;
});
}, 1000);
return () => {
clearInterval(timerId);
};
}, [timerReset]);
return (
<div className="App">
<div className="base-timer">
<svg
className="base-timer__svg"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<g className="base-timer__circle">
<circle
className="base-timer__path-elapsed"
cx="50"
cy="50"
r="45"
/>
<path
id="base-timer-path-remaining"
className={`base-timer__path-remaining ${pathColor}`}
d="
M 50, 50
m -45, 0
a 45,45 0 1,0 90,0
a 45,45 0 1,0 -90,0
"
/>
</g>
</svg>
<span id="base-timer-label" className="base-timer__label">
{format(counter)}
</span>
</div>
<button
onClick={() => {
setCounter(20);
resetTimer();
}}
>
reset timer
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
/* Sets the containers height and width */
.base-timer {
position: relative;
height: 300px;
width: 300px;
}
/* Removes SVG styling that would hide the time label */
.base-timer__circle {
fill: none;
stroke: none;
}
/* The SVG path that displays the timer's progress */
.base-timer__path-elapsed {
stroke-width: 7px;
stroke: grey;
}
.base-timer__path-remaining {
stroke-width: 7px;
stroke-linecap: round;
transform: rotate(90deg);
transform-origin: center;
transition: 1s linear all;
fill-rule: nonzero;
stroke: currentColor;
}
.base-timer__path-remaining.green {
color: rgb(65, 184, 131);
}
.base-timer__path-remaining.orange {
color: orange;
}
.base-timer__path-remaining.red {
color: red;
}
.base-timer__label {
position: absolute;
width: 300px;
height: 300px;
top: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

How do you show a link after scrolling 300px down a page in JS?

I am trying to make a link that's anchored to a heading appear after scrolling down 300px on my website, but my code doesn't seem to work. Does anyone know why?
NOTE-
I am using Bootstrap5 on my website.
I have altered my code based on the replies I got but I'm still facing the issue. This is how my code looks now-
Here is my code -
<a href="#header-title-1" id="customID" class="bottom-0 end-0 quick-anchor-top hide"> <i
class="fa-solid fa-arrow-up"></i></a>
.quick-anchor-top {
font-size: 25px;
padding: 15px 25px 15px 25px;
border-radius: 50px;
color: rgb(0, 0, 0);
background-color: rgba(182, 20, 20, 0.800);
transition: all 0.4s ease;
margin: 20px;
position: fixed;
z-index: 1;
}
.quick-anchor-top:hover {
transition-duration: 0.4s;
color: white;
background-color: rgba(0, 0, 0, 0.800);
}
.quick-anchor-top.show {
display: block;
}
.quick-anchor-top.hide {
display: none;
}
const myID = document.getElementById("customID");
// Reset timeout after each call
const debounce = function (func, duration = 250){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, duration);
};
}
// Call only once per duration
function throttle(func, duration = 250) {
let shouldWait = false
return function (...args) {
if (!shouldWait) {
func.apply(this, args)
shouldWait = true
setTimeout(function () {
shouldWait = false
}, duration)
}
}
}
// Handle scroll Event
const scrollHandler = function() {
const { scrollY } = window;
if ( scrollY >= 300) {
myID.classList.add('show');
myID.classList.remove('hide');
} else {
myID.classList.add('hide');
myID.classList.remove('show');
}
};
window.addEventListener("scroll", throttle(() => scrollHandler()) );
The JavaScript code works properly: the show and hide CSS classes names are appearing. The problem is in the CSS. So, to fix it try the following:
.quick-anchor-top {
font-size: 25px;
padding: 15px 25px 15px 25px;
border-radius: 50px;
color: rgb(0, 0, 0);
background-color: rgba(182, 20, 20, 0.800);
transition: all 0.4s ease;
margin: 20px;
position: fixed;
z-index: 1;
display: none;
}
.quick-anchor-top.show {
display: block;
}
.quick-anchor-top.hide {
display: none;
}
.quick-anchor-top:hover {
transition-duration: 0.4s;
color: white;
background-color: rgba(0, 0, 0, 0.800);
}
When page just loaded
You don't need to set
class="bottom-0 end-0 quick-anchor-top hide"
change a tag to
<a href="#header-title-1" id="customID" > <i
class="fa-solid fa-arrow-up"></i></a>
change your if else to
if (y >= 300) {
myID.className = "quick-anchor-top"
} else {
myID.className = ""
}
That is not the correct way to add or remove classes. Also I would recommend using a debounce or throttle depending on how you need to handle events because a scroll event can run several hundred times in a second.
const myID = document.getElementById("customID");
// Reset timeout after each call
const debounce = function (func, duration = 250){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, duration);
};
}
// Call only once per duration
function throttle(func, duration = 250) {
let shouldWait = false
return function (...args) {
if (!shouldWait) {
func.apply(this, args)
shouldWait = true
setTimeout(function () {
shouldWait = false
}, duration)
}
}
}
// Handle scroll Event
const scrollHandler = function() {
const { scrollY } = window;
if ( scrollY >= 300) {
myID.classList.add('show');
myID.classList.remove('hide');
} else {
myID.classList.add('hide');
myID.classList.remove('show');
}
};
window.addEventListener("scroll", throttle(() => scrollHandler()) );

After resetting stopwatch stopwatch is beginning at the previous time

When I want to reset my stopwatch and start it again the timer begins at the stop-point of the previous timer execution but I want that the timer begins at zero. I tried different ways to solve this problem but my tries did not work. What is my failure? The considered area in my JavaScript is marked up.
window.onload = function () {
//global variables
let interval = null;
let timerId = null;
let y = 3.90;
let reversal = 20
const output = document.querySelector('output');
let maintime = document.getElementById('maintime');
const start = document.getElementById('actioner');
const clear = document.getElementById('reseter');
let [milliseconds, seconds, minutes, hours] = [0, 0, 0, 0];
//If start is clicked
start.addEventListener('click', () => {
buttonAndTimer();
startDrive();
}); // end of func
function buttonAndTimer() {
start.innerText = 'Stop';
if (!interval) {
interval = setInterval(() => {
run();
}, 10);
} else {
clearInterval(interval)
start.innerText = 'Resume';
interval = null;
};
}
function run() {
milliseconds += 10;
if (milliseconds == 1000) { //note: 1000 milliseconds are 1 seconds
milliseconds = 0;
seconds++;
};
if (seconds == 60) {
seconds = 0;
minutes++;
};
if (minutes == 60) {
minutes == 0
hours++;
};
h = hours < 10 ? '0' + hours : hours;
m = minutes < 10 ? '0' + minutes : minutes;
s = seconds < 10 ? '0' + seconds : seconds;
ms = milliseconds < 100 ? '00' + milliseconds : milliseconds;
//Template Literals
maintime.innerHTML = `${h} : ${m} : ${s} : ${ms} `
};
//calculating price
function startDrive() {
if (start.innerText != 'Resume') {
output.innerHTML = y.toFixed(2) + '€';
timerId = setInterval(() => {
if (y < reversal) {
y += 0.14375;
} else if (y > reversal) {
y += 0.103125;
}
output.innerHTML = y.toFixed(2) + "€";
}, 5000);
}
/*Considered area */
if (start.innerText == 'Resume') {
clearInterval(timerId);
}
} //end of func
// considered area
clear.addEventListener('click', () => {
clearInterval(interval);
interval = null;
maintime.innerHTML = '00:00:00:000';
start.innerText = 'Start'
clearInterval(timerId);
timerId = 0;
output.innerHTML = "";
})
} //end of window.load
#box {
display: flex;
justify-content: center;
align-items:center;
flex-direction: column;
gap: 5px;
}
span, #maintime{
color:#74bde0;
width:15vh;
text-align: center;
max-width:20vh;
}
.button {
border:none;
border-radius: 30px;
cursor: pointer;
color:#74bde0;
box-shadow: 1px 1px 1px;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
}
output {
border: 1px solid;
border-color:#74bde0 ;
border-radius: 5px;
height: 10vh;
width: 30vh;
text-align: center;
color:#74bde0;
line-height: 500%;
box-shadow: rgba(17, 17, 26, 0.1) 0px 4px 16px, rgba(17, 17, 26, 0.1) 0px 8px 24px, rgba(17, 17, 26, 0.1) 0px 16px 56px;
}
#card {
background-color: #2f2f2f;
width: 80vh;
height: 10vh;
border:1px solid;
border-color:blueviolet;
border-radius: 30px;
}
<body>
<div id="box">
<button class='button' id='actioner'>Start</button>
<output></output>
<button class='button' id='reseter'>Reset</button>
<div id='mainstopwatch'>
<div id='maintime'>
<span class='span' id="mainhour">00:</span>
<span class='span' id="mainminute">00:</span>
<span class='span' id="mainsecond">00:</span>
<span class='span' id="milliseconds">000</span>
</div>
</div>
</body>
Fixed it.. I hope this is acceptable answer..
window.onload = function () {
//global variables
let interval = null;
let timerId = null;
let y = 3.90;
let reversal = 20
const output = document.querySelector('output');
let maintime = document.getElementById('maintime');
const start = document.getElementById('actioner');
const clear = document.getElementById('reseter');
let [milliseconds, seconds, minutes, hours] = [0, 0, 0, 0];
//If start is clicked
start.addEventListener('click', () => {
console.log("start clicked.. ")
buttonAndTimer();
startDrive();
}); // end of func
function buttonAndTimer() {
start.innerText = 'Stop';
if (!interval) {
interval = setInterval(() => {
run();
}, 10);
} else {
clearInterval(interval)
start.innerText = 'Resume';
interval = null;
};
}
function run() {
milliseconds += 10;
if (milliseconds == 1000) { //note: 1000 milliseconds are 1 seconds
milliseconds = 0;
seconds++;
};
if (seconds == 60) {
seconds = 0;
minutes++;
};
if (minutes == 60) {
minutes == 0
hours++;
};
h = hours < 10 ? '0' + hours : hours;
m = minutes < 10 ? '0' + minutes : minutes;
s = seconds < 10 ? '0' + seconds : seconds;
ms = milliseconds < 100 ? '00' + milliseconds : milliseconds;
//Template Literals
maintime.innerHTML = `${h} : ${m} : ${s} : ${ms} `
};
//calculating price
function startDrive() {
if (start.innerText != 'Resume') {
output.innerHTML = y.toFixed(2) + '€';
timerId = setInterval(() => {
if (y < reversal) {
y += 0.14375;
} else if (y > reversal) {
y += 0.103125;
}
output.innerHTML = y.toFixed(2) + "€";
}, 5000);
}
/*Considered area */
if (start.innerText == 'Resume') {
clearInterval(timerId);
}
} //end of func
// considered area
clear.addEventListener('click', () => {
console.log("clear clicked.. ")
clearInterval(interval);
interval = null;
maintime.innerHTML = '00:00:00:000';
start.innerText = 'Start'
clearInterval(timerId);
timerId = 0;
output.innerHTML = "";
milliseconds = 0
seconds = 0
minutes = 0
hours = 0
})
} //end of

How to run a function when scroll reaches a certain component?

There are some components stacked on top of each other and the last component has a timer. I want the timer to start only when that component is visible on screen or when scroll is reached to that component. [REPL]
let count_val = 80;
let count = 0;
function startTimer() {
let count_interval = setInterval(() => {
count += 1;
if(count >= count_val) {
clearInterval(count_interval);
}
}, 100);
}
// check if scroll reached to component and run below function.
startTimer();
How do I achieve this?
Like commented this can be achieved using Intersection Observer and an action
REPL
<script>
let count_val = 80;
let count = 0;
function timer(node) {
let interval
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
interval = setInterval(() => {
count += 1;
if(count === count_val) {
clearInterval(interval);
}
}, 100);
} else {
clearInterval(interval)
count = 0
}
})
})
observer.observe(node)
return {
destroy: () => observer.disconnect()
}
}
</script>
<div use:timer>
<p>
Counter - {count}
</p>
</div>
<style>
div {
height: 100vh;
display: grid;
place-items: center;
background-color: teal;
color: #0a0a0a;
font-size: 4rem;
}
</style>

Function firing multiple times

could someone help me out with this piece of Javascript?
I am trying to make some sort of "whack-a-mole" game, and this is what I came up with; I set up a way to keep track of the score by adding 1 (score++) every time the user clicks on the picture that pops up. My problem is that the code runs the function more times than needed—for example, if I click on the first image that pops up, the function to add +1 to the score fires once, if I click on the second, the function fires twice, threee times on the third, etc...
What am I doing wrong?
//gid
const grid = document.querySelector('.grid');
//score display value
const scoreValue = document.querySelector('#scoreValue');
//score
let score = 0;
const timer = setInterval(() => {
//output random number
let output = Math.floor(Math.random() * 16);
//select hole
let hole = document.getElementById(output);
hole.innerHTML = '<img src="img/kiseki.png" alt=""></img>';
setTimeout(() => {
hole.innerHTML = '';
}, 2000);
grid.addEventListener('click', e => {
if (e.target.tagName === "IMG") {
score++;
scoreValue.textContent = score;
console.log(score);
hole.innerHTML = '';
}
});
}, 4000);
Since you're ading a new eventListener every time the interval runs, so in order to solve your problem, just add it once, before starting the setInterval that pops your moles.
Example code:
const grid = document.querySelector('.grid');
const scoreValue = document.querySelector('#scoreValue');
const newMoleTimer = 4000;
const moleTimeout = 2000
let score = 0;
let hole;
grid.addEventListener('click', e => {
if (e.target.tagName === "IMG") {
score++;
scoreValue.textContent = score;
if(hole) hole.innerHTML = '';
}
});
const timer = setInterval(() => {
let output = Math.floor(Math.random() * 16);
hole = document.getElementById(output);
hole.innerHTML = '<img src="img/kiseki.png" alt=""></img>';
setTimeout(() => {
hole.innerHTML = '';
}, moleTimeout);
}, newMoleTimer);
*updated code according to #Meika commentary
You need to separate the eventlistener from the settimer function.
In this example I created div elements with a color. Only blue color score and can only score one point pr. timer.
//gid
const grid = document.querySelector('#grid');
//score display value
const scoreValue = document.querySelector('#scoreValue');
//score
let score = 0;
grid.addEventListener('click', e => {
if (e.target.score) {
score++;
scoreValue.textContent = score;
e.target.score = false;
}
});
const timer = setInterval(() => {
//output random number
let output = 1 + Math.floor(Math.random() * 3);
//select hole
let hole = document.querySelector(`div.box:nth-child(${output})`)
hole.classList.add('blue');
hole.score = true;
setTimeout(() => {
hole.classList.remove('blue');
hole.score = false;
}, 1000);
}, 2000);
div#grid {
display: flex;
}
div.box {
width: 100px;
height: 100px;
border: thin solid black;
background-color: red;
}
div.blue {
background-color: blue;
}
<div id="grid">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div id="scoreValue"></div>
Rewrite, a mole is a DOM element, attach the click event to it on load then, in the game timer you only need to pick a random mole and toggle a class, within the click event you can check for that class, if it is there then the mole must be showing, add a score.
For example:
const moles = document.querySelectorAll('.grid .mole')
const hitScore = document.querySelector('.score .hit')
const missScore = document.querySelector('.score .miss')
const gameOver = document.querySelector('.gameover')
let score = {
hit: 0,
miss: 0
}
// assign clicks to all moles
moles.forEach((elm) => {
elm.addEventListener('click', e => {
if (e.target.classList.contains('show')) {
hitScore.textContent = ++score.hit
e.target.classList.remove('show')
}
})
})
// game timer
const timer = setInterval(() => {
// get random mole element
const randMole = moles[Math.floor(Math.random() * moles.length)]
// check if has class, i.e miss
if (randMole.classList.contains('show')) {
missScore.textContent = ++score.miss
}
// toggle show
randMole.classList.toggle('show')
// 5 misses and game over
if (score.miss >= 5) {
clearInterval(timer)
gameOver.style.display = 'block'
}
}, 1000)
.grid {
width: 310px;
height: 310px;
background-image: url(https://i.imgur.com/s6lUgud.png);
position: relative
}
.mole {
position: absolute;
width: 100px;
height: 100px
}
.mole.show {
background-image: url(https://i.imgur.com/uScpWV4.png);
background-repeat: no-repeat;
background-size: 48px 51px;
background-position: center
}
.mole:nth-of-type(1) {
top: 0;
left: 0
}
.mole:nth-of-type(2) {
top: 0;
left: 108px
}
.mole:nth-of-type(3) {
top: 0;
left: 214px
}
.mole:nth-of-type(4) {
top: 100px;
left: 0
}
.mole:nth-of-type(5) {
top: 100px;
left: 108px
}
.mole:nth-of-type(6) {
top: 100px;
left: 214px
}
.mole:nth-of-type(7) {
top: 200px;
left: 0px
}
.mole:nth-of-type(8) {
top: 200px;
left: 107px
}
.mole:nth-of-type(9) {
top: 200px;
left: 214px
}
.gameover {
display: none;
color: red
}
<div class="score">
<strong>Score:</strong> Hit:
<span class="hit">0</span> Miss:
<span class="miss">0</span>
</div>
<div class="gameover">Game Over</div>
<div class="grid">
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
<div class="mole"></div>
</div>

Updating attribute value in Loop - Reactjs

I am trying to make a image slide from left to write and right to left infinitely in a loop in my react component.
I am not able to change the left property of the style attribute.
Here is the relevant piece of code
let lastRight = 0;
let multiplier = 1;
const images = [One, Two, Three, Four];
class ImageStream extends Component {
state = {
position: [0, 640, 1280, 1920]
};
changeLeft() {
if (lastRight > 2000) {
multiplier = -1;
} else if (lastRight < -600) {
multiplier = 1;
}
for (let i = 0; i < this.state.position.length; i++) {
this.state.position[i] = (this.state.position[i] + (multiplier * 1));
lastRight = this.state.position[i];
}
}
componentDidMount() {
this.intervalID = setInterval(this.changeLeft.bind(this), 200);
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.intervalId);
}
renderImage(imageUrl, index) {
return (
<img
src={imageUrl}
style={{ left: this.state.position[index] }}
key={index + "_image"}
/>
);
}
render() {
return (
<div id="image-scroll" className="mt-4">
{images.map((imageUrl, index) => this.renderImage(imageUrl, index))}
</div>
);
}
}
export default ImageStream;
What I am expecting is that the left property of the style attribute of the image changes because the position arrays is changing regularly.
I am fairly new to React and would love to know if I am doing this wrong/right.
Thank you in advance for looking into it.
You were not using setState to change the state, also tweaked a few numbers and CSS to get the things running. Do check it out
let lastRight = 0;
let multiplier = 1;
const images = ["One", "Two", "Three", "Four"];
class ImageStream extends React.Component {
state = {
position: [0, 640, 1280, 1920]
};
changeLeft() {
if (lastRight > 2000) {
multiplier = -1;
} else if (lastRight < -600) {
multiplier = 1;
}
let posArr = [...this.state.position]
for (let i = 0; i < posArr.length; i++) {
let pos = posArr[i]
, newPos = pos + (multiplier * 10)
posArr[i] = newPos
this.setState({ position: posArr })
lastRight = newPos
}
}
componentDidMount() {
this.changeLeft = this.changeLeft.bind(this)
this.intervalID = setInterval(this.changeLeft, 10);
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.intervalId);
}
renderImage(imageUrl, index) {
return (
<img
src={imageUrl}
style={{ left: this.state.position[index] }}
key={index + "_image"}
/>
);
}
render() {
return (
<div id="image-scroll" className="mt-4">
{images.map((imageUrl, index) => this.renderImage(imageUrl, index))}
</div>
);
}
}
// Render it
ReactDOM.render(
<ImageStream />,
document.getElementById("react")
);
img{ position: relative; width: 100px; height: 100px; outline: 1px solid blue;}
<div id="react"></div>
<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>

Categories

Resources