react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
145 lines (142 loc) • 5.34 kB
JavaScript
'use strict';
import { Component } from 'react';
import { Platform, StyleSheet } from 'react-native';
import { getViewInfo } from "../../createAnimatedComponent/getViewInfo.js";
import { getShadowNodeWrapperFromRef } from '../../fabricUtils';
import { findHostInstance } from '../../platform-specific/findHostInstance';
import { assignRef } from "../../reactUtils.js";
import { markNodeAsRemovable, unmarkNodeAsRemovable } from "../native/index.js";
import { CSSManager } from '../platform';
import { filterCSSProps } from "./utils.js";
import { jsx as _jsx } from "react/jsx-runtime";
// TODO - change these ugly underscore prefixed methods and properties to real
// private/protected ones when possible (when changes from this repo are merged
// to the main one)
export default class AnimatedComponent extends Component {
_cssStyle = {}; // RN style object with Reanimated CSS properties
_componentRef = null;
_componentDOMRef = null;
_willUnmount = false;
constructor(ChildComponent, props) {
super(props);
this.ChildComponent = ChildComponent;
}
getComponentViewTag() {
return this._getViewInfo().viewTag;
}
_onSetLocalRef() {
// noop - can be overridden in subclasses
}
_getViewInfo() {
if (this._viewInfo !== undefined) {
return this._viewInfo;
}
const hostInstance = findHostInstance(this);
if (!hostInstance) {
/*
findHostInstance can return null for a component that doesn't render anything
(render function returns null). Example:
svg Stop: https://github.com/react-native-svg/react-native-svg/blob/develop/src/elements/Stop.tsx
*/
throw new Error('[Reanimated] Cannot find host instance for this component. Maybe it renders nothing?');
}
const viewInfo = getViewInfo(hostInstance);
const viewTag = viewInfo.viewTag ?? -1;
const reactViewName = viewInfo.reactViewName;
const shadowNodeWrapper = getShadowNodeWrapperFromRef(this, hostInstance);
this._viewInfo = {
viewTag,
shadowNodeWrapper,
reactViewName
};
return this._viewInfo;
}
_setComponentRef = ref => {
// Forward to user ref prop (if one has been specified)
const forwardedRef = this.props.forwardedRef;
if (!ref) {
// component has been unmounted
if (this._forwardedRefCleanup) {
this._forwardedRefCleanup();
this._forwardedRefCleanup = undefined;
} else {
assignRef(forwardedRef, null);
}
return;
}
this._forwardedRefCleanup = assignRef(forwardedRef, ref);
if (ref !== this._componentRef) {
this._componentRef = this._resolveComponentRef(ref);
// if ref is changed, reset viewInfo
this._viewInfo = undefined;
}
this._onSetLocalRef();
};
_resolveComponentRef = ref => {
const componentRef = ref;
// Component can specify ref which should be animated when animated version of the component is created.
// Otherwise, we animate the component itself.
if (componentRef && componentRef.getAnimatableRef) {
return componentRef.getAnimatableRef();
}
return componentRef;
};
_updateStyles(props) {
this._cssStyle = StyleSheet.flatten(props.style) ?? {};
}
componentDidMount() {
this._updateStyles(this.props);
const viewTag = this._viewInfo?.viewTag;
if (this._willUnmount && typeof viewTag === 'number') {
unmarkNodeAsRemovable(viewTag);
}
this._CSSManager ??= new CSSManager(this._getViewInfo(),
// `react-native-svg`'s web classes don't set `static displayName`
// (only the native side does), so fall back to the class `name` which
// matches the React `displayName` pattern used elsewhere.
this.ChildComponent.displayName ?? this.ChildComponent.name);
this._CSSManager?.update(this._cssStyle, this.props);
this._willUnmount = false;
}
componentWillUnmount() {
if (this._CSSManager) {
this._CSSManager.unmountCleanup();
}
const wrapper = this._viewInfo?.shadowNodeWrapper;
if (wrapper) {
// Mark node as removable on the native (C++) side, but only actually remove it
// when it no longer exists in the Shadow Tree. This ensures proper cleanup of
// animations/transitions/props while handling cases where the node might be
// remounted (e.g., when frozen) after componentWillUnmount is called.
markNodeAsRemovable(wrapper);
}
this._willUnmount = true;
}
shouldComponentUpdate(nextProps) {
this._updateStyles(nextProps);
if (this._CSSManager) {
this._CSSManager.update(this._cssStyle, nextProps);
}
// TODO - maybe check if the render is necessary instead of always returning true
return true;
}
render(props) {
const {
ChildComponent
} = this;
const platformProps = Platform.select({
web: {},
default: {
collapsable: false
}
});
return /*#__PURE__*/_jsx(ChildComponent, {
...filterCSSProps(props ?? this.props),
...platformProps,
// Casting is used here, because ref can be null - in that case it cannot be assigned to HTMLElement.
// After spending some time trying to figure out what to do with this problem, we decided to leave it this way
ref: this._setComponentRef
});
}
}
//# sourceMappingURL=AnimatedComponent.native.js.map