UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

145 lines (142 loc) 5.34 kB
'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