react-native-shared-element
react-native-shared-element copied to clipboard
Error while updating property 'endNode' of a view managed by: RNSharedElementTranstion
I'm using react-navigation-shared-element@next react-native-shared-element and @react-navigation/native@^5.0.9 @react-navigation/stack@^5.1.1
I have that error when execute more then 2 times an shared element animation.
Thanks <3
Duplicate of #30
Which version of react-native and react-native-shared-element are you using? And on what Android phone (API) are you seeing this?
Same here with a Xioami Mi A1 and
Library | Version |
---|---|
react-native | 0.61.5 |
react-navigation | 4.3.9 |
react-navigation-shared-element | 2.3.0 |
react-navigation-stack | 2.5.1 |
react-native-shared-element | 0.7.0 |
Which version of react-native and react-native-shared-element are you using? And on what Android phone (API) are you seeing this?
Library | Version |
---|---|
react-native | 0.62.2 |
react-navigation-shared-element@next | ^5.0.0-alpha1 |
react-native-shared-element | ^0.7.0 |
I'm seeing this in Note 10+ API Level 29 and API level 28
I have noticed that I have this problem when I use a SharedElement
inside a ListHeaderComponent
of a FlatList
. Only android.
@jorgemasta Thanks for sharing that, that might help 👍
I would be awesome if perhaps someone can add a reproduction case to the ./test-app
, that would be really helpful to me and would speed fixing this up 👍
i ran into this issue as well, and found it was not the child view that was missing but instead it was the ancestor; after setting collaspable={false}
on the ancestor view where my ref was, this issue went away.
I've encountered this problem when the shared element is in a list thats in a tabNavigator thats in a stack navigator
<Stack_Navigator>
<ViewItemPage>
</ViewItemPage>
<TabNavigator>
<Screen1>
<List>
<MySharedElement />
</List>
</Screen1>
<Screen2>
</Screen2>
</TabNavigator
</Stack_Navigator>
debugged the call method and saw that otherRoute from createSharedStackNavigator configuration is from TabNavigator page and not the Screen1 page (where is should be called from). the error may be thrown because after the target page is mounted, it cannot find the sharedElement to navigate back to because its not on the TabNavigator page but in the list.
when the sharedElement is not in a nested tabNavigator, works fine
I've encountered a similar error, even when I removed the elements out of the nested navigator
+1 i met the same issue when try it with react navigation v5.
hi @ironforward , i am using react-navigation-share-element 5.0.0-alpha1 and met this issue, where i can add collaspable={false} to fix the issue. Thanks
Hi, is there any news about this? Unfortunately I met the same issue with 5.0.0-alpha1 and collapsable={false}
on the ancestor View doesn't solve it. Thanks!
bump.. also experiencing this issue on production
Any info about this still happening... on 5.0.0-alpha1
any updates on this?
Maybe this will fix the isssue, I extended createSharedElementStackNavigator.tsx
from react-navigation-shared-element
v5.0.0-alpha1:
import * as React from 'react';
import {
useNavigationBuilder,
createNavigatorFactory,
StackRouter,
DefaultNavigatorOptions,
RouteConfig,
StackRouterOptions,
StackNavigationState,
} from '@react-navigation/native';
import {
CardAnimationContext,
StackView,
StackNavigationOptions,
} from '@react-navigation/stack';
import {SharedElementRendererProxy} from 'react-navigation-shared-element/lib/module/SharedElementRendererProxy';
import SharedElementRendererContext from 'react-navigation-shared-element/lib/module/SharedElementRendererContext';
import SharedElementRendererView from 'react-navigation-shared-element/lib/module/SharedElementRendererView';
import SharedElementRendererData from 'react-navigation-shared-element/lib/module/SharedElementRendererData';
import createSharedElementScene from 'react-navigation-shared-element/lib/module/createSharedElementScene';
import {
SharedElementSceneComponent,
SharedElementsComponentConfig,
} from 'react-navigation-shared-element/lib/module/types';
import {
StackNavigationConfig,
StackNavigationEventMap,
} from '@react-navigation/stack/lib/typescript/src/types';
import { Route } from '@react-navigation/native';
let _navigatorId = 1;
export default function createSharedElementStackNavigator<
ParamList extends Record<string, object | undefined>
>(options?: { name?: string }) {
const navigatorId =
options && options.name ? options.name : `stack${_navigatorId}`;
_navigatorId++;
const rendererDataProxy = new SharedElementRendererProxy();
type Props = DefaultNavigatorOptions<StackNavigationOptions> &
StackRouterOptions &
StackNavigationConfig;
function SharedElementStackNavigator({
initialRouteName,
children,
screenOptions,
...rest
}: Props) {
const { state, descriptors, navigation } = useNavigationBuilder<
StackNavigationState,
StackRouterOptions,
StackNavigationOptions,
StackNavigationEventMap
>(StackRouter, {
initialRouteName,
children,
screenOptions,
});
const rendererDataRef = React.useRef<SharedElementRendererData | null>(
null
);
const [shouldShowTransition, setShouldShowTransition] = React.useState(false);
React.useEffect(() => {
setShouldShowTransition(true);
}, [state]);
function handleTransitionEnd({ route }: { route: Route<string> }, closing) {
setShouldShowTransition(false);
navigation.emit({
type: 'transitionEnd',
data: {closing},
target: route.key,
});
};
return (
<SharedElementRendererContext.Consumer>
{rendererData => {
// In case a renderer is already present higher up in the chain
// then don't bother creating a renderer here, but use that one instead
if (!rendererData) {
rendererDataRef.current =
rendererDataRef.current || new SharedElementRendererData();
rendererDataProxy.source = rendererDataRef.current;
} else {
rendererDataProxy.source = rendererData;
}
return (
<SharedElementRendererContext.Provider value={rendererDataProxy}>
<StackView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
onTransitionEnd={handleTransitionEnd}
/>
{rendererDataRef.current && shouldShowTransition ? (
<SharedElementRendererView
rendererData={rendererDataRef.current}
/>
) : (
undefined
)}
</SharedElementRendererContext.Provider>
);
}}
</SharedElementRendererContext.Consumer>
);
}
const navigatorFactory = createNavigatorFactory<
StackNavigationState,
StackNavigationOptions,
StackNavigationEventMap,
typeof SharedElementStackNavigator
>(SharedElementStackNavigator);
const { Navigator, Screen } = navigatorFactory<ParamList>();
type ScreenProps<RouteName extends keyof ParamList> = Omit<
RouteConfig<
ParamList,
RouteName,
StackNavigationState,
StackNavigationOptions,
StackNavigationEventMap
>,
'component' | 'children'
> & {
component: SharedElementSceneComponent;
sharedElementsConfig?: SharedElementsComponentConfig;
};
function wrapComponent(component: SharedElementSceneComponent) {
return createSharedElementScene(
component,
rendererDataProxy,
CardAnimationContext,
navigatorId
);
}
// Wrapping Screen to explicitly statically type a "Shared Element" Screen.
function wrapScreen<RouteName extends keyof ParamList>(
_: ScreenProps<RouteName>
) {
return null;
}
type NavigatorProps = React.ComponentProps<typeof Navigator>;
function getSharedElementsChildrenProps(children: React.ReactNode) {
return React.Children.toArray(children).reduce<any[]>((acc, child) => {
if (React.isValidElement(child)) {
if (child.type === wrapScreen) {
acc.push(child.props);
}
if (child.type === React.Fragment) {
acc.push(...getSharedElementsChildrenProps(child.props.children));
}
}
return acc;
}, []);
}
// react-navigation only allows the Screen component as direct children
// of Navigator, this is why we need to wrap the Navigator
function WrapNavigator(props: NavigatorProps) {
const { children, ...rest } = props;
const componentMapRef = React.useRef<Map<any, any>>(new Map());
const screenChildrenProps = getSharedElementsChildrenProps(children);
return (
<Navigator {...rest}>
{screenChildrenProps.map(
({ component, sharedElementsConfig, ...childrenProps }) => {
if (sharedElementsConfig)
component.sharedElements = sharedElementsConfig;
if (!componentMapRef.current.has(component)) {
componentMapRef.current.set(component, wrapComponent(component));
}
const wrappedComponent = componentMapRef.current.get(component);
return (
<Screen
key={childrenProps.name}
{...childrenProps}
component={wrappedComponent}
/>
);
}
)}
</Navigator>
);
}
return {
Navigator: WrapNavigator,
Screen: wrapScreen,
};
}
I added code below to createSharedElementStackNavigator.tsx
:
import { Route } from '@react-navigation/native';
...
const [shouldShowTransition, setShouldShowTransition] = React.useState(false);
React.useEffect(() => {
setShouldShowTransition(true);
}, [state]);
function handleTransitionEnd({ route }: { route: Route<string> }, closing) {
setShouldShowTransition(false);
navigation.emit({
type: 'transitionEnd',
data: {closing},
target: route.key,
});
};
...
<SharedElementRendererContext.Provider value={rendererDataProxy}>
<StackView
{...rest}
state={state}
navigation={navigation}
descriptors={descriptors}
onTransitionEnd={handleTransitionEnd}
/>
{rendererDataRef.current && shouldShowTransition ? (
<SharedElementRendererView
rendererData={rendererDataRef.current}
/>
) : (
undefined
)}
</SharedElementRendererContext.Provider>
Basically, I tried to make sure the node is already created when RNSharedElementTranstion
trying to resolve the node or view. I do this by mounting SharedElementRendererView
(which contains SharedElementTransition
) after react navigation state is changed, and then unmount it after screen transition has ended, so on the next state change, it in unmounted state. It seems fix the issue, I already tested it on Samsung Galaxy Tab A 8.0 (2019) with android version 9.
Same issue here in debug mode on Android. Is not happening always.
Hello fellow android users, I too experienced crashes etc. HOWEVER, I don't think it was due to this library. This library works fine. The problem is when you use this library with components that can't manage their states properly on the native side lol. For example, most of you use Reanimated2's Animated.View, try replacing that Animated.View with View from the react-native library and watch as your crashes disappear. In fact, for animations in components that use SharedElement you can just use the good ol' Animated component from the react-native library as well. This is what I have done. Having said that, I still use Reaniamted2 for everything else, it's just when I'm using SharedElement, I'll avoid it.
Hope this helps, bye.
Thank you @gigadeplex for your comment. Given how long this issue has been inactive I'm closing it as stale.