From f34b30422c45b601f047a78267b09704d5d205ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Wed, 28 Aug 2024 18:28:02 +0200 Subject: [PATCH] Add mutable value read/write during render warning (#6310) ## Summary This PR adds warnings shown when the SharedValue `.value` property is accessed (read or written) while the component is being rendered. The initial render is ignored as all reanimated hooks are executed for the first time during the initial render (e.g. `useAnimatedStyle` builds the initial component style). ## Test plan - open the **Invalid read/write during render** example in the example app, - press on the **Re-render** button to trigger re-render, which will access `.value` property of the shared value from the component function body, - see warnings about invalid read and invalid write --------- Co-authored-by: Tomek Zawadzki --- .../examples/InvalidValueAccessExample.tsx | 89 +++++++++++++++++++ apps/common-app/src/examples/index.ts | 6 ++ .../react-native-reanimated/src/mutables.ts | 29 +++++- .../react-native-reanimated/src/reactUtils.ts | 22 +++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 apps/common-app/src/examples/InvalidValueAccessExample.tsx create mode 100644 packages/react-native-reanimated/src/reactUtils.ts diff --git a/apps/common-app/src/examples/InvalidValueAccessExample.tsx b/apps/common-app/src/examples/InvalidValueAccessExample.tsx new file mode 100644 index 00000000000..ff9c5dad966 --- /dev/null +++ b/apps/common-app/src/examples/InvalidValueAccessExample.tsx @@ -0,0 +1,89 @@ +import { Text, StyleSheet, View, Button } from 'react-native'; + +import React, { useEffect } from 'react'; +import Animated, { + configureReanimatedLogger, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + +configureReanimatedLogger({ + // change to `false` or remove the `configureReanimatedLogger` call to + // disable the warning + strict: true, +}); + +export default function InvalidValueAccessExample() { + const [counter, setCounter] = React.useState(0); + const [updateFromUseEffect, setUpdateFromUseEffect] = React.useState(false); + const sv = useSharedValue(0); + + if (!updateFromUseEffect) { + // logs writing to `value`... warning + sv.value = counter; + // logs reading from `value`... warning + console.log('shared value:', sv.value); + } + + useEffect(() => { + if (updateFromUseEffect) { + // no warning is logged + sv.value = counter; + // no warning is logged + console.log('useEffect shared value:', sv.value); + } + }, [sv, counter, updateFromUseEffect]); + + const reRender = () => { + setCounter((prev) => prev + 1); + }; + + const animatedBarStyle = useAnimatedStyle(() => ({ + transform: [{ scaleX: withTiming(Math.sin((sv.value * Math.PI) / 10)) }], + })); + + return ( + + + + Update from:{' '} + + {updateFromUseEffect ? 'useEffect' : 'component'} + + +