Skip to content

Commit

Permalink
Handle update of value prop in AnimatedNumber (#5974)
Browse files Browse the repository at this point in the history
  • Loading branch information
goplayoutside3 authored Mar 15, 2024
1 parent 85bcd54 commit 875fc77
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 5 deletions.
17 changes: 12 additions & 5 deletions packages/lib-react-components/src/AnimatedNumber/AnimatedNumber.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types'
import { format, interpolate, select } from 'd3'
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'

const initialValue = 0

Expand All @@ -12,6 +12,7 @@ if (isBrowser) {

function AnimatedNumber({ duration = 1000, value }) {
const numRef = useRef(null)
const [animated, setAnimated] = useState(false)

function animateValue() {
select(numRef.current)
Expand All @@ -22,6 +23,7 @@ function AnimatedNumber({ duration = 1000, value }) {
const interpolator = interpolate(initialValue, value)
return t => {
const interpolatedValue = interpolator(t)
if (interpolatedValue === value) setAnimated(true) // animation complete!
const niceValue = formatValue(interpolatedValue)
return niceValue
}
Expand All @@ -45,18 +47,23 @@ function AnimatedNumber({ duration = 1000, value }) {
}

useEffect(() => {
// If we already animated the number once, don't observe intersection
// This could happen if the value prop updates, but page did not refresh
if (animated) return

const numElement = numRef.current

const intersectionObserver = new window.IntersectionObserver(entries => {
// If intersectionRatio is 0, the target is out of view and we do not need to do anything.
if (entries[0].intersectionRatio <= 0) return

// Once target element is in viewport, animate it only once
if (!prefersReducedMotion) {
// Once target element is in viewport, animate it then unobserve
if (!prefersReducedMotion && !animated) {
animateValue()
intersectionObserver.unobserve(numElement)
} else {
lessAnimation()
intersectionObserver.unobserve(numElement)
}
})

Expand All @@ -65,9 +72,9 @@ function AnimatedNumber({ duration = 1000, value }) {
return () => {
intersectionObserver.disconnect()
}
}, [numRef.current])
}, [numRef.current, animated])

return <span ref={numRef}>{formatValue(initialValue)}</span>
return <span ref={numRef}>{!animated ? initialValue : formatValue(value)}</span>
}

AnimatedNumber.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Box } from 'grommet'
import { useState } from 'react'

import AnimatedNumber from './AnimatedNumber'

export default {
Expand All @@ -13,6 +15,22 @@ export const Default = {
}
}

export const UpdateTheValue = () => {
const [value, setValue] = useState(10)

function handleClick() {
const newValue = value + 1
setValue(newValue)
}

return (
<Box width='200px' gap='xsmall'>
<AnimatedNumber value={value} />
<button onClick={handleClick}>Update the value</button>
</Box>
)
}

export const Zero = {
args: {
value: 0
Expand Down

0 comments on commit 875fc77

Please sign in to comment.