rive-react-native
rive-react-native copied to clipboard
React Native app crashes when calling Rive Ref Methods
Description
When calling the ref functions (.play()
, .reset()
, etc.), it causes a crash. The crash consistently happened whether I was calling these functions from inside a useEffect
, a handler function, or wherever else in the React lifecycle. It seems like there's something funky with the ref happening where the underlying Swift code is trying to do an operation on a null object or something.
The specific error is:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
and it gets thrown by this line:
https://github.com/rive-app/rive-react-native/blob/f6432c82384aab7b4e397f819f12ff51c0c4540b/ios/RiveReactNativeView.swift#L132
Provide a Repro
Here's a code snippet that was consistently crashing the app when switching between dark and light mode in the iOS Simulator:
const isDarkMode = useColorScheme() === 'dark'
const animationRef = useRef<RiveRef>(null)
useEffect(() => {
animationRef.current?.play("Intro", LoopMode.Loop)
}, [isDarkMode])
return (
<Rive
ref={animationRef}
alignment={Alignment.TopCenter}
animationName="Intro"
artboardName="Unified"
fit={Fit.FitHeight}
resourceName={isDarkMode ? 'OnboardingDark' : 'OnboardingLight'}
stateMachineName="State Machine 1"
style={animationStyles}
/>
)
it would also crash consistently when I would navigate away from the app and back again multiple times in a row.
I tried some combination of:
- cleanup function in useEffect to
.stop()
or.reset()
the animation -
.stop()
or.reset()
the animation each time theuseEffect
runs - calling
.stop()
or.reset()
when the screen has focus
and eventually settled on just unmounting the component when this screen lost focus and remounting it when it gained focus again.
Source .riv
/.rev
file
Unfortunately I can't share the .riv file as it's from a private project. But I'm almost certain it's not specific to the Rive file since we've seen the same behavior with many different files.
Expected behavior
Rive would run the ref function(s) I call on it without crashing the app.
I am experiencing the same issue. In my case I have more than one
<Rive
...
/>
component however I do not think this is contributing to the issue.
We're experiencing the same issue when calling setInputState on the RiveRef. If we wrap the calls in a setTimeout (without a delay) to await pending JS runtime executions it works. In general it would be desirable to instead declaratively define the states provided for an animation since it would better fit React's paradigm.
Completely agreed with @maylukas . However it looks like Rive always use imperative functions in other repositories too (rive-ios, rive-android) instead of declarative code, so I am not sure it will change anytime soon. For those coming here for the crash issue reported by OP, here is basically what proposes @maylukas :
const [initialized, setInitialized] = useState(false);
const riveRef = React.useRef<RiveRef>(null);
useEffect(() => {
const fnToExecute = () => {
// Your function logic containing riveRef
// riveRef.current.setInputState(...)
};
let timer;
if (!initialized) {
timer = setTimeout(() => {
fnToExecute();
setInitialized(true);
}, 100);
} else {
fnToExecute();
}
return () => {
clearTimeout(timer);
};
}, [initialized]);
Had same issue when tried to call setInputState
inside useEffect
. iOS was crashing. Resolved this by using InteractionManager
Before
useEffect(() => {
riveRef?.current?.setInputState('hero', 'health', healthValue);
}, [healthValue]);
After
const canUseRive = useRef(false);
useEffect(() => {
InteractionManager.runAfterInteractions(() => {
canUseRive.current = true;
});
}, []);
useEffect(() => {
if (canUseRive.current) {
riveRef?.current?.setInputState('hero', 'health', healthValue);
}
}, [healthValue]);