[Video] Directly mutating player properties triggers react compiler warnings
Minimal reproducible example
https://github.com/expo/expo
What platform(s) does this occur on?
iOS
Where did you reproduce the issue?
in a standalone app
Summary
Hello !
When using the useVideoPlayer hook, directly mutating properties of the returned player object (like playbackRate) triggers React compiler warnings about mutating values that should not be mutated.
Example component triggering the warning:
import { VideoView, useVideoPlayer } from 'expo-video';
import { Button, View } from 'react-native';
const VideoPlayer = ({ source }: { source: string }) => {
const videoPlayer = useVideoPlayer(source);
const updatePlaybackRate = () => {
// "Mutating a value returned from a function whose return value should not be mutated eslint(react-compiler/react-compiler)"
videoPlayer.playbackRate += 1;
};
return (
<View>
<VideoView player={videoPlayer} {...otherProps} nativeControls={false} />
<Button title="Speed Up" onPress={() => updatePlaybackRate()} />
</View>
);
};
export { VideoPlayer };
Reanimated recently switched from directly accessing animatedValue.value to using getters/setters
Maybe this is related, and a similar solution would solve the issue?
Thanks
Environment
expo-env-info 1.2.2 environment info:
System:
OS: macOS 14.7
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.9.0 - /usr/local/bin/node
Yarn: 4.6.0 - /opt/homebrew/bin/yarn
npm: 10.8.3 - /usr/local/bin/npm
Watchman: 2024.08.12.00 - /opt/homebrew/bin/watchman
Managers:
CocoaPods: 1.15.2 - /Users/jean-baptistelarriviere/.rbenv/shims/pod
SDKs:
iOS SDK:
Platforms: DriverKit 24.2, iOS 18.2, macOS 15.2, tvOS 18.2, visionOS 2.2, watchOS 11.2
Android SDK:
API Levels: 27, 30, 31, 32, 33, 34, 35
Build Tools: 28.0.3, 29.0.2, 30.0.2, 30.0.3, 31.0.0, 33.0.0, 34.0.0, 35.0.0
System Images: android-21 | ARM EABI v7a, android-24 | Google APIs ARM 64 v8a, android-28 | Google ARM64-V8a Play ARM 64 v8a, android-30 | Intel x86 Atom_64, android-30 | Google APIs ARM 64 v8a, android-30 | Google APIs Intel x86 Atom_64, android-31 | ARM 64 v8a, android-31 | Google APIs ARM 64 v8a, android-31 | Google APIs Intel x86 Atom_64, android-31 | Google Play ARM 64 v8a, android-32 | Google APIs ARM 64 v8a, android-33 | Google APIs ARM 64 v8a
IDEs:
Android Studio: 2024.2 AI-242.23726.103.2422.12816248
Xcode: 16.2/16C5032a - /usr/bin/xcodebuild
npmGlobalPackages:
eas-cli: 14.4.1
Expo Workflow: bare
Expo Doctor Diagnostics
13/15 checks passed. 2 checks failed. Possible issues detected:
Use the --verbose flag to see more details about passed checks.
✖ Validate packages against React Native Directory package metadata
The following issues were found when validating your dependencies against React Native Directory:
Unsupported on New Architecture: react-native-otp-verify
Untested on New Architecture: @intercom/intercom-react-native, react-native-fast-shadow
No metadata available: @apollo/client, @brigad/design-system, @brigad/shared, @brigad/shared-mobile, @rosk/design-system, @rosk/react-native-splash, @rosk/shared, @rosk/shared-native, apollo-upload-client, apollo3-cache-persist, its-fine, react-final-form, react-intl, react-native-persona, ts-pattern
Advice:
Use libraries that are actively maintained and support the New Architecture. Find alternative libraries with https://reactnative.directory.
Add packages to expo.doctor.reactNativeDirectoryCheck.exclude in package.json to selectively skip validations, if the warning is not relevant.
Update React Native Directory to include metadata for unknown packages. Alternatively, set expo.doctor.reactNativeDirectoryCheck.listUnknownPackages in package.json to false to skip warnings about packages with no metadata, if the warning is not relevant.
✖ Check that packages match versions required by installed Expo SDK
The following packages should be updated for best compatibility with the installed expo version:
@shopify/[email protected] - expected version: 1.7.6
@shopify/[email protected] - expected version: v2.0.0-next.2
[email protected] - expected version: ~53.0.0-preview.9
[email protected] - expected version: ~0.14.4
[email protected] - expected version: ~16.1.3
[email protected] - expected version: ~7.1.2
[email protected] - expected version: ~17.1.2
[email protected] - expected version: ~13.2.1
[email protected] - expected version: ~14.1.2
[email protected] - expected version: ~2.1.4
[email protected] - expected version: ~13.1.3
[email protected] - expected version: ~14.1.2
[email protected] - expected version: ~7.1.2
[email protected] - expected version: ~16.1.2
[email protected] - expected version: ~5.0.2-preview.4
[email protected] - expected version: ~5.0.4
[email protected] - expected version: ~0.28.5
[email protected] - expected version: ~2.1.3
[email protected] - expected version: ~14.1.4
[email protected] - expected version: 0.79.1
[email protected] - expected version: ~2.24.0
[email protected] - expected version: ~3.17.4
[email protected] - expected version: ~5.8.3
@react-navigation/[email protected] - expected version: ^7.1.6
Your project may not work correctly until you install the expected versions of the packages.
Found outdated dependencies
Advice:
Use 'npx expo install --check' to review and upgrade your dependencies.
2 checks failed, indicating possible issues with the project.
Can confirm this, currently the only thing that prevents me from using the React Compiler.
Thank you for filing this issue! This comment acknowledges we believe this may be a bug and there’s enough information to investigate it. However, we can’t promise any sort of timeline for resolution. We prioritize issues based on severity, breadth of impact, and alignment with our roadmap. If you’d like to help move it more quickly, you can continue to investigate it more deeply and/or you can open a pull request that fixes the cause.
We could add a similar API as Reanimated (.get() and .set()).
The question is whether each property should have its own getter and setter (player.playbackRate.get()), or if the API should be more like player.set('playbackRate', player.get('playbackRate') + 1).
Hi everyone. This is an issue not only with video, but with all of our SharedObjects. It's because react-compiler can't properly interpret the value returned from useVideoPlayer hook. rn-reanimated had a similar issue and they had to add some special cases into the react comiler for them. We believe that it's not a perfect solution for us and will contact Meta to hopefully find something better.
For now I'd suggest disabling the react-compiler with the use no memo flag for components with video 😕
Related, yet not the same: eslint complains about no-param-reassign in useVideoPlayer setup as well, if you follow the basic Usage docs example:
useVideoPlayer(url, (player) => {
player.timeUpdateEventInterval = 1
// ^^ Assignment to property of function parameter 'player'
})