Skip to content

Commit 98e843a

Browse files
committed
fix: disconnect AnimatedProps before attaching a new native view
1 parent cb5ea03 commit 98e843a

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

‎packages/react-native/Libraries/Animated/nodes/AnimatedProps.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ export default class AnimatedProps extends AnimatedNode {
268268
if (this._target?.instance === instance) {
269269
return;
270270
}
271+
if (this.__isNative && this._target != null) {
272+
this.#disconnectAnimatedView(this._target);
273+
}
271274
const target = (this._target = {instance, connectedViewTag: null});
272275
if (this.__isNative) {
273276
this.#connectAnimatedView(target);

‎packages/react-native/src/private/animated/__tests__/AnimatedNative-test.js‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,94 @@ describe('Native Animated', () => {
16191619
);
16201620
});
16211621

1622+
it('disconnects the previous native view before connecting a replacement', () => {
1623+
const findNodeHandle = jest
1624+
.fn()
1625+
.mockReturnValueOnce(11)
1626+
.mockReturnValueOnce(22);
1627+
jest.setMock('../../../../Libraries/ReactNative/RendererProxy', {
1628+
findNodeHandle,
1629+
});
1630+
const {Animated} = importModules();
1631+
const AnimatedProps =
1632+
require('../../../../Libraries/Animated/nodes/AnimatedProps').default;
1633+
const firstView = {};
1634+
const secondView = {};
1635+
const opacity = new Animated.Value(1);
1636+
const propsNode = new AnimatedProps({style: {opacity}}, jest.fn());
1637+
propsNode.__attach();
1638+
propsNode.setNativeView(firstView);
1639+
propsNode.__makeNative();
1640+
const propsTag = propsNode.__getNativeTag();
1641+
1642+
propsNode.setNativeView(secondView);
1643+
1644+
expect(
1645+
NativeAnimatedModule.connectAnimatedNodeToView,
1646+
).toHaveBeenNthCalledWith(1, propsTag, 11);
1647+
expect(
1648+
NativeAnimatedModule.disconnectAnimatedNodeFromView,
1649+
).toHaveBeenCalledWith(propsTag, 11);
1650+
expect(
1651+
NativeAnimatedModule.connectAnimatedNodeToView,
1652+
).toHaveBeenNthCalledWith(2, propsTag, 22);
1653+
expect(
1654+
// $FlowFixMe[prop-missing]
1655+
NativeAnimatedModule.disconnectAnimatedNodeFromView.mock
1656+
.invocationCallOrder[0],
1657+
).toBeLessThan(
1658+
// $FlowFixMe[prop-missing]
1659+
NativeAnimatedModule.connectAnimatedNodeToView.mock
1660+
.invocationCallOrder[1],
1661+
);
1662+
1663+
propsNode.setNativeView(secondView);
1664+
expect(
1665+
NativeAnimatedModule.connectAnimatedNodeToView,
1666+
).toHaveBeenCalledTimes(2);
1667+
expect(
1668+
NativeAnimatedModule.disconnectAnimatedNodeFromView,
1669+
).toHaveBeenCalledTimes(1);
1670+
1671+
propsNode.__detach();
1672+
expect(
1673+
NativeAnimatedModule.disconnectAnimatedNodeFromView,
1674+
).toHaveBeenNthCalledWith(2, propsTag, 22);
1675+
expect(findNodeHandle).toHaveBeenCalledTimes(2);
1676+
});
1677+
1678+
it('only connects the latest view when switching to the native driver', () => {
1679+
jest.setMock('../../../../Libraries/ReactNative/RendererProxy', {
1680+
findNodeHandle: () => 22,
1681+
});
1682+
const {Animated} = importModules();
1683+
const AnimatedProps =
1684+
require('../../../../Libraries/Animated/nodes/AnimatedProps').default;
1685+
const opacity = new Animated.Value(1);
1686+
const propsNode = new AnimatedProps({style: {opacity}}, jest.fn());
1687+
propsNode.__attach();
1688+
propsNode.setNativeView({});
1689+
propsNode.setNativeView({});
1690+
expect(
1691+
NativeAnimatedModule.connectAnimatedNodeToView,
1692+
).not.toHaveBeenCalled();
1693+
expect(
1694+
NativeAnimatedModule.disconnectAnimatedNodeFromView,
1695+
).not.toHaveBeenCalled();
1696+
1697+
propsNode.__makeNative();
1698+
expect(
1699+
NativeAnimatedModule.connectAnimatedNodeToView,
1700+
).toHaveBeenCalledTimes(1);
1701+
expect(
1702+
NativeAnimatedModule.connectAnimatedNodeToView,
1703+
).toHaveBeenCalledWith(propsNode.__getNativeTag(), 22);
1704+
expect(
1705+
NativeAnimatedModule.disconnectAnimatedNodeFromView,
1706+
).not.toHaveBeenCalled();
1707+
propsNode.__detach();
1708+
});
1709+
16221710
it('creates new props, style and transform nodes at update', async () => {
16231711
const {Animated} = importModules();
16241712

0 commit comments

Comments
 (0)