Skip to content

Commit 161c1ae

Browse files
committed
refactor: merge AnimatedProps into withAnimated
1 parent 32920d6 commit 161c1ae

File tree

5 files changed

+66
-61
lines changed

5 files changed

+66
-61
lines changed

packages/animated/src/AnimatedObject.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export class AnimatedObject extends Animated {
5252
/** Add to a payload set. */
5353
protected _addToPayload(this: Set<AnimatedValue>, source: any) {
5454
const config = getFluidConfig(source)
55-
if (config && TreeContext.current) {
56-
TreeContext.current.dependencies.add(source)
55+
if (config && TreeContext.dependencies) {
56+
TreeContext.dependencies.add(source)
5757
}
5858
const payload = getPayload(source)
5959
if (payload) {

packages/animated/src/AnimatedProps.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.

packages/animated/src/context.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { FluidValue } from '@react-spring/shared'
2-
import { HostConfig } from './createHost'
32

43
export type TreeContext = {
5-
dependencies: Set<FluidValue>
6-
host: HostConfig
4+
/**
5+
* Any animated values found when updating the payload of an `AnimatedObject`
6+
* are also added to this `Set` to be observed by an animated component.
7+
*/
8+
dependencies: Set<FluidValue> | null
79
}
810

9-
export const TreeContext: { current: TreeContext | null } = { current: null }
11+
export const TreeContext: TreeContext = { dependencies: null }

packages/animated/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export * from './AnimatedValue'
33
export * from './AnimatedString'
44
export * from './AnimatedArray'
55
export * from './AnimatedObject'
6-
export * from './AnimatedProps'
76
export * from './getAnimatedType'
87
export * from './createHost'
98
export * from './types'

packages/animated/src/withAnimated.tsx

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import { useLayoutEffect } from 'react-layout-effect'
44
import {
55
is,
66
each,
7+
raf,
78
useForceUpdate,
8-
FluidConfig,
9+
useMemoOne,
910
useOnce,
10-
usePrev,
11+
FluidEvent,
12+
FluidObserver,
13+
FluidValue,
1114
} from '@react-spring/shared'
1215
import { ElementType } from '@react-spring/types'
1316

14-
import { AnimatedProps } from './AnimatedProps'
17+
import { AnimatedObject } from './AnimatedObject'
18+
import { TreeContext } from './context'
1519
import { HostConfig } from './createHost'
1620

1721
export type AnimatableComponent = string | Exclude<ElementType, string>
@@ -37,49 +41,82 @@ export const withAnimated = (Component: any, host: HostConfig) => {
3741
[givenRef]
3842
)
3943

44+
const [props, deps] = getAnimatedState(givenProps, host)
45+
4046
const forceUpdate = useForceUpdate()
41-
const props = new AnimatedProps(() => {
47+
const observer = new PropsObserver(() => {
4248
const instance = instanceRef.current
4349
if (hasInstance && !instance) {
44-
return // The wrapped component forgot to forward its ref.
50+
// Either this component was unmounted before changes could be
51+
// applied, or the wrapped component forgot to forward its ref.
52+
return
4553
}
4654

4755
const didUpdate = instance
48-
? host.applyAnimatedValues(instance, props.getValue(true)!)
56+
? host.applyAnimatedValues(instance, props.getValue(true))
4957
: false
5058

5159
// Re-render the component when native updates fail.
5260
if (didUpdate === false) {
5361
forceUpdate()
5462
}
55-
})
63+
}, deps)
5664

57-
const dependencies = new Set<FluidConfig>()
58-
props.setValue(givenProps, { dependencies, host })
65+
const observerRef = useRef<PropsObserver>()
66+
useLayoutEffect(() => {
67+
const lastObserver = observerRef.current
68+
observerRef.current = observer
5969

60-
const state = [props, dependencies] as const
61-
const stateRef = useRef(state)
62-
const prevState = usePrev(state)
70+
// Observe the latest dependencies.
71+
each(deps, dep => dep.addChild(observer))
6372

64-
useLayoutEffect(() => {
65-
stateRef.current = state
66-
// Attach the new props to our latest dependencies.
67-
each(dependencies, dep => dep.addChild(props))
68-
// Detach the old props from our previous dependencies.
69-
if (prevState) each(prevState[1], dep => dep.removeChild(prevState[0]))
73+
// Stop observing previous dependencies.
74+
if (lastObserver) {
75+
each(lastObserver.deps, dep => dep.removeChild(lastObserver))
76+
raf.cancel(lastObserver.update)
77+
}
7078
})
7179

7280
// Stop observing on unmount.
7381
useOnce(() => () => {
74-
const [props, dependencies] = stateRef.current
75-
each(dependencies, dep => dep.removeChild(props))
82+
const observer = observerRef.current!
83+
each(observer.deps, dep => dep.removeChild(observer))
7684
})
7785

78-
const usedProps = host.getComponentProps(props.getValue()!)
86+
const usedProps = host.getComponentProps(props.getValue())
7987
return <Component {...usedProps} ref={ref} />
8088
})
8189
}
8290

91+
class PropsObserver implements FluidObserver {
92+
constructor(readonly update: () => void, readonly deps: Set<FluidValue>) {}
93+
onParentChange(event: FluidEvent) {
94+
if (event.type == 'change') {
95+
raf.write(this.update)
96+
}
97+
}
98+
}
99+
100+
type AnimatedState = [props: AnimatedObject, dependencies: Set<FluidValue>]
101+
102+
function getAnimatedState(props: any, host: HostConfig): AnimatedState {
103+
const dependencies = new Set<FluidValue>()
104+
TreeContext.dependencies = dependencies
105+
106+
// Search the style for dependencies.
107+
if (props.style)
108+
props = {
109+
...props,
110+
style: host.createAnimatedStyle(props.style),
111+
}
112+
113+
// Search the props for dependencies.
114+
props = new AnimatedObject(props)
115+
116+
TreeContext.dependencies = null
117+
return [props, dependencies]
118+
}
119+
83120
function updateRef<T>(ref: Ref<T>, value: T) {
84121
if (ref) {
85122
if (is.fun(ref)) ref(value)

0 commit comments

Comments
 (0)