Skip to content

Commit 5fa6c26

Browse files
committed
fix: fix event manager being shared for multiple views
1 parent 76e0968 commit 5fa6c26

File tree

5 files changed

+79
-69
lines changed

5 files changed

+79
-69
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
[![npm][version-badge]][version]
44
[![MIT License][license-badge]][license]
55

6-
Helpers that let you observe the value of an [`Animated.Value`][animated.value] or [`SharedValue`][reanimated.sharedvalue] and convert between them using native events.
6+
Helpers that let you observe the value of an [Animated][animated] or [Reanimated][reanimated] values and convert between them using native events.
77

88
## Use case
99

10-
This library is useful when you have a component that uses [`Animated`](https://reactnative.dev/docs/animated) and accepts animated values or styles, but you use [`Reanimated`](https://docs.swmansion.com/react-native-reanimated/docs/) for your app's animations, or vice versa. You can use this library to convert your values to work with the component.
10+
This library is useful when you have a component that uses [Animated][animated] and accepts animated values or styles, but you use [Reanimated][reanimated] for your app's animations, or vice versa. You can use this library to convert your values to work with the component.
1111

1212
## Installation
1313

@@ -21,19 +21,19 @@ The library exports the following components:
2121

2222
### `AnimatedConverter`
2323

24-
A component that converts between an [`Animated.Value`][animated.value] and a [`SharedValue`][reanimated.sharedvalue] natively.
24+
A component that converts between an [`Animated.Node`][animated.node] and a [`SharedValue`][reanimated.sharedvalue] natively.
2525

2626
It accepts the following props:
2727

2828
- `from`: Value to read and observe changes from.
2929

30-
`Animated.Node` - `Animated.Value` and result of modifications such as interpolation (`Animated.AnimatedInterpolation`), addition (`Animated.AnimatedAddition`), etc.
30+
[`Animated.Node`][animated.node] - [`Animated.Value`][animated.value] or result of modifications such as interpolation (`Animated.AnimatedInterpolation`), addition (`Animated.AnimatedAddition`), etc.
3131

32-
`SharedValue<number>` or `DerivedValue<number>`
32+
[`SharedValue<number>`][reanimated.sharedvalue] or [`DerivedValue<number>`][reanimated.derivedvalue]
3333

3434
- `to`: Value to update when the `from` value changes.
3535

36-
`Animated.Value` or `SharedValue<number>`
36+
[`Animated.Value`][animated.value] or [`SharedValue<number>`][reanimated.sharedvalue]
3737

3838
Usage:
3939

@@ -55,7 +55,7 @@ A component that observes changes in a given value and emits an event when the v
5555

5656
It accepts the following props:
5757

58-
- `value`: The value to observe. It can be a `number`, [`Animated.Value`][animated.value], or [`SharedValue`][reanimated.sharedvalue].
58+
- `value`: The value to observe. It can be a `number`, [`Animated.Node`][animated.node] for [Animated][animated], [`SharedValue`][reanimated.sharedvalue] or [`DerivedValue`][reanimated.derivedvalue] for [Reanimated][reanimated].
5959
- `onValueChange`: A callback function that is called when the observed value changes.
6060

6161
Usage:
@@ -77,7 +77,7 @@ const animatedValue = useRef(new Animated.Value(0)).current;
7777

7878
## How it works
7979

80-
The library renders a native component that receives an [`Animated.Value`][animated.value] or [`SharedValue`][reanimated.sharedvalue] value. When the value changes, the library dispatches an event with the updated value.
80+
The library renders a native component that receives an [`Animated.Node`][animated.node] or [`SharedValue`][reanimated.sharedvalue] value. When the value changes, the library dispatches an event with this value.
8181

8282
This event is then used with [`Animated.event`][animated.event] (with native driver) or [`useEvent`][reanimated.useevent] to update [`Animated.Value`][animated.value] or [`SharedValue`][reanimated.sharedvalue] respectively, depending on the usage.
8383

@@ -93,9 +93,13 @@ MIT
9393

9494
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
9595

96+
[animated]: https://reactnative.dev/docs/animated
97+
[reanimated]: https://docs.swmansion.com/react-native-reanimated/docs/
98+
[animated.node]: https://reactnative.dev/docs/animated#node
9699
[animated.value]: https://reactnative.dev/docs/animated#value
97100
[animated.event]: https://reactnative.dev/docs/animated#event
98101
[reanimated.sharedvalue]: https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/
102+
[reanimated.derivedvalue]: https://docs.swmansion.com/react-native-reanimated/docs/core/useDerivedValue
99103
[reanimated.useevent]: https://docs.swmansion.com/react-native-reanimated/docs/advanced/useEvent
100104
[version-badge]: https://img.shields.io/npm/v/react-native-animated-observer.svg?style=flat-square
101105
[license-badge]: https://img.shields.io/npm/l/react-native-animated-observer.svg?style=flat-square

android/src/main/java/com/animatedobserver/AnimatedObserverEventManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class AnimatedObserverEventManager(private val emitter: (value: Double) -> Unit)
1616
field = value
1717

1818
value?.let {
19-
register(value)
19+
unregister = register(value)
2020
emit()
2121
}
2222
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.animatedobserver
2+
3+
import android.content.Context
4+
import android.util.AttributeSet
5+
import android.view.View
6+
import com.facebook.react.bridge.Arguments
7+
import com.facebook.react.bridge.ReactContext
8+
import com.facebook.react.bridge.WritableMap
9+
import com.facebook.react.uimanager.UIManagerHelper
10+
import com.facebook.react.uimanager.events.Event
11+
12+
class AnimatedObserverView : View {
13+
constructor(context: Context?) : super(context)
14+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
15+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
16+
context,
17+
attrs,
18+
defStyleAttr
19+
)
20+
21+
val manager: AnimatedObserverEventManager = AnimatedObserverEventManager({ value ->
22+
val reactContext = context as ReactContext
23+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
24+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, surfaceId)
25+
?: throw IllegalStateException("$NAME: EventDispatcher is not available for surfaceId: $surfaceId")
26+
27+
eventDispatcher.dispatchEvent(ValueChangeEvent(surfaceId, this.id, value))
28+
})
29+
30+
inner class ValueChangeEvent(
31+
surfaceId: Int, viewId: Int, private val value: Double
32+
) : Event<ValueChangeEvent>(surfaceId, viewId) {
33+
override fun getEventName() = EVENT_ON_CHANGE
34+
35+
override fun getEventData(): WritableMap = Arguments.createMap().apply {
36+
putDouble("value", value)
37+
}
38+
}
39+
40+
companion object {
41+
const val NAME = "AnimatedObserverView"
42+
43+
/**
44+
* On Android, it's necessary to use the `top` prefix for event names
45+
* Otherwise, it doesn't work with `Animated.event` using `useNativeDriver: true`
46+
*/
47+
const val EVENT_ON_CHANGE = "topValueChange"
48+
}
49+
}
Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,53 @@
11
package com.animatedobserver
22

3-
import android.util.Log
4-
import android.view.View
5-
import com.facebook.react.bridge.Arguments
6-
import com.facebook.react.bridge.ReactContext
7-
import com.facebook.react.bridge.WritableMap
83
import com.facebook.react.module.annotations.ReactModule
94
import com.facebook.react.uimanager.SimpleViewManager
105
import com.facebook.react.uimanager.ThemedReactContext
11-
import com.facebook.react.uimanager.UIManagerHelper
126
import com.facebook.react.uimanager.ViewManagerDelegate
137
import com.facebook.react.uimanager.annotations.ReactProp
14-
import com.facebook.react.uimanager.events.Event
158
import com.facebook.react.viewmanagers.AnimatedObserverViewManagerDelegate
169
import com.facebook.react.viewmanagers.AnimatedObserverViewManagerInterface
17-
import java.util.concurrent.ConcurrentHashMap
1810

19-
@ReactModule(name = AnimatedObserverViewManager.NAME)
20-
class AnimatedObserverViewManager : SimpleViewManager<View>(),
21-
AnimatedObserverViewManagerInterface<View> {
22-
private val mDelegate: ViewManagerDelegate<View> =
11+
@ReactModule(name = AnimatedObserverView.NAME)
12+
class AnimatedObserverViewManager : SimpleViewManager<AnimatedObserverView>(),
13+
AnimatedObserverViewManagerInterface<AnimatedObserverView> {
14+
private val mDelegate: ViewManagerDelegate<AnimatedObserverView> =
2315
AnimatedObserverViewManagerDelegate(this)
2416

25-
private var manager: AnimatedObserverEventManager? = null;
26-
27-
override fun createViewInstance(context: ThemedReactContext): View {
28-
val view = View(context)
29-
30-
manager?.cleanup()
31-
manager = AnimatedObserverEventManager({ value ->
32-
val reactContext = view.context as ReactContext
33-
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
34-
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, surfaceId)
35-
?: throw IllegalStateException("$AnimatedObserverViewManager.NAME: EventDispatcher is not available for surfaceId: $surfaceId")
36-
37-
eventDispatcher.dispatchEvent(ValueChangeEvent(surfaceId, view.id, value))
38-
})
17+
override fun createViewInstance(context: ThemedReactContext): AnimatedObserverView {
18+
val view = AnimatedObserverView(context)
3919

4020
return view;
4121
}
4222

43-
override fun onDropViewInstance(view: View) {
44-
manager?.cleanup()
45-
manager = null
23+
override fun onDropViewInstance(view: AnimatedObserverView) {
24+
view.manager.cleanup()
4625

4726
super.onDropViewInstance(view)
4827
}
4928

5029
override fun prepareToRecycleView(
5130
reactContext: ThemedReactContext,
52-
view: View
53-
): View? {
54-
manager?.cleanup()
55-
manager = null
56-
31+
view: AnimatedObserverView
32+
): AnimatedObserverView? {
5733
return super.prepareToRecycleView(reactContext, view)
5834
}
5935

6036
override fun getDelegate() = mDelegate
6137

62-
override fun getName() = NAME
38+
override fun getName() = AnimatedObserverView.NAME
6339

6440
override fun getExportedCustomDirectEventTypeConstants() = mapOf(
65-
EVENT_ON_CHANGE to mapOf("registrationName" to EVENT_ON_CHANGE)
41+
AnimatedObserverView.EVENT_ON_CHANGE to mapOf("registrationName" to AnimatedObserverView.EVENT_ON_CHANGE)
6642
)
6743

6844
@ReactProp(name = "tag")
69-
override fun setTag(view: View, nextTag: String?) {
70-
manager?.tag = nextTag
45+
override fun setTag(view: AnimatedObserverView, nextTag: String?) {
46+
view.manager.tag = nextTag
7147
}
7248

7349
@ReactProp(name = "value")
74-
override fun setValue(view: View, nextValue: Double) {
75-
manager?.value = nextValue
76-
}
77-
78-
inner class ValueChangeEvent(
79-
surfaceId: Int, viewId: Int, private val value: Double
80-
) : Event<ValueChangeEvent>(surfaceId, viewId) {
81-
override fun getEventName() = EVENT_ON_CHANGE
82-
83-
override fun getEventData(): WritableMap = Arguments.createMap().apply {
84-
putDouble("value", value)
85-
}
86-
}
87-
88-
companion object {
89-
const val NAME = "AnimatedObserverView"
90-
91-
/**
92-
* On Android, it's necessary to use `top` prefix for event names
93-
* Otherwise it doesn't work with `Animated.event` using `useNativeDriver: true`
94-
*/
95-
const val EVENT_ON_CHANGE = "topValueChange"
50+
override fun setValue(view: AnimatedObserverView, nextValue: Double) {
51+
view.manager.value = nextValue
9652
}
9753
}

example/src/Examples.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function AnimatedToReanimated() {
3939
<View style={styles.container}>
4040
<AnimatedConverter from={positionAnimated} to={positionReanimated} />
4141
<AnimatedConverter
42+
name="PositionAnimatedInterpolated"
4243
from={positionAnimatedInterpolated}
4344
to={positionReanimatedInterpolated}
4445
/>

0 commit comments

Comments
 (0)