Skip to content

Commit

Permalink
Remove ref in render antipattern in animated docs
Browse files Browse the repository at this point in the history
Reading or writing to a ref in render is a [rule of React violation](https://react.dev/reference/react/useRef). This PR updates the docs for animated to use the `useAnimatedValue` hook instead, which does use a ref under the hood but isolates the rule of React violation to just that hook.

This allows users that follow code examples for animated to have their
components and hooks be compilable by React Compiler.

This hook was added in
[0.71](https://github.com/facebook/react-native/blob/main/CHANGELOG.md#:~:text=Introduce%20useAnimatedValue%20hook%20to%20make%20it%20easier%20working%20with%20Animated.Values%20in%20function%20components.%20(e22217fe8b%20by%20%40fabriziocucci))
so I'm also updating versioned docs from 0.71 onwards.
  • Loading branch information
poteto committed Oct 30, 2024
1 parent e20b097 commit f6728c4
Show file tree
Hide file tree
Showing 40 changed files with 198 additions and 152 deletions.
17 changes: 12 additions & 5 deletions docs/animated.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ The core workflow for creating an animation is to create an `Animated.Value`, ho

The following example contains a `View` which will fade in and fade out based on the animated value `fadeAnim`

```SnackPlayer name=Animated%20Example
import React, {useRef} from 'react';
import {Animated, Text, View, StyleSheet, Button} from 'react-native';
```SnackPlayer name=Animated%20Example&supportedPlatforms=ios,android
import React from 'react';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
import {
Animated,
Text,
View,
StyleSheet,
Button,
useAnimatedValue,
} from 'react-native';
const App = () => {
// fadeAnim will be used as the value for opacity. Initial Value: 0
const fadeAnim = useRef(new Animated.Value(0)).current;
const fadeAnim = useAnimatedValue(0);
const fadeIn = () => {
// Will change fadeAnim value to 1 in 5 seconds
Expand Down Expand Up @@ -495,7 +502,7 @@ Stops any running animation and resets the value to its original.

### `Value`

Standard value class for driving animations. Typically initialized with `new Animated.Value(0);`
Standard value class for driving animations. Typically initialized with `useAnimatedValue(0);` or `new Animated.Value(0);` in class components.

You can read more about `Animated.Value` API on the separate [page](animatedvalue).

Expand Down
2 changes: 1 addition & 1 deletion docs/animatedvalue.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: Animated.Value

Standard value for driving animations. One `Animated.Value` can drive multiple properties in a synchronized fashion, but can only be driven by one mechanism at a time. Using a new mechanism (e.g. starting a new animation, or calling `setValue`) will stop any previous ones.

Typically initialized with `new Animated.Value(0);`
Typically initialized with `useAnimatedValue(0);` or `new Animated.Value(0);` in class components.

---

Expand Down
17 changes: 9 additions & 8 deletions docs/animations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ For example, a container view that fades in when it is mounted may look like thi
<TabItem value="javascript">

```SnackPlayer ext=js
import React, {useRef, useEffect} from 'react';
import {Animated, Text, View} from 'react-native';
import React, {useEffect} from 'react';
import {Animated, Text, View, useAnimatedValue} from 'react-native';
const FadeInView = props => {
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
const fadeAnim = useAnimatedValue(0); // Initial value for opacity: 0
useEffect(() => {
Animated.timing(fadeAnim, {
Expand Down Expand Up @@ -74,15 +74,15 @@ export default () => {
<TabItem value="typescript">

```SnackPlayer ext=tsx
import React, {useRef, useEffect} from 'react';
import {Animated, Text, View} from 'react-native';
import React, {useEffect} from 'react';
import {Animated, Text, View, useAnimatedValue} from 'react-native';
import type {PropsWithChildren} from 'react';
import type {ViewStyle} from 'react-native';
type FadeInViewProps = PropsWithChildren<{style: ViewStyle}>;
const FadeInView: React.FC<FadeInViewProps> = props => {
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
const fadeAnim = useAnimatedValue(0); // Initial value for opacity: 0
useEffect(() => {
Animated.timing(fadeAnim, {
Expand Down Expand Up @@ -311,7 +311,7 @@ The following example implements a horizontal scrolling carousel where the scrol
#### ScrollView with Animated Event Example

```SnackPlayer name=Animated&supportedPlatforms=ios,android
import React, {useRef} from 'react';
import React from 'react';
import {
ScrollView,
Text,
Expand All @@ -320,6 +320,7 @@ import {
ImageBackground,
Animated,
useWindowDimensions,
useAnimatedValue,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';

Expand All @@ -328,7 +329,7 @@ const images = new Array(6).fill(
);

const App = () => {
const scrollX = useRef(new Animated.Value(0)).current;
const scrollX = useAnimatedValue(0);

const {width: windowWidth} = useWindowDimensions();

Expand Down
8 changes: 5 additions & 3 deletions docs/easing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The following helpers are used to modify other easing functions.
<Tabs groupId="language" queryString defaultValue={constants.defaultSnackLanguage} values={constants.snackLanguages}>
<TabItem value="javascript">

```SnackPlayer name=Easing%20Demo&ext=js
```SnackPlayer name=Easing%20Demo&ext=js&supportedPlatforms=ios,android
import React from 'react';
import {
Animated,
Expand All @@ -59,11 +59,12 @@ import {
Text,
TouchableOpacity,
View,
useAnimatedValue,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
const App = () => {
let opacity = new Animated.Value(0);
const opacity = useAnimatedValue(0);
const animate = easing => {
opacity.setValue(0);
Expand Down Expand Up @@ -219,12 +220,13 @@ import {
Text,
TouchableOpacity,
View,
useAnimatedValue,
type EasingFunction,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
const App = () => {
let opacity = new Animated.Value(0);
const opacity = useAnimatedValue(0);
const animate = (easing: EasingFunction) => {
opacity.setValue(0);
Expand Down
10 changes: 6 additions & 4 deletions docs/interactionmanager.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ By default, queued tasks are executed together in a loop in one `setImmediate` b
<TabItem value="javascript">

```SnackPlayer name=InteractionManager%20Function%20Component%20Basic%20Example&supportedPlatforms=ios,android&ext=js
import React, {useState, useEffect} from 'react';
import React, {useEffect} from 'react';
import {
Alert,
Animated,
InteractionManager,
Platform,
StyleSheet,
Text,
useAnimatedValue,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
Expand All @@ -66,7 +67,7 @@ const instructions = Platform.select({
});
const useFadeIn = (duration = 5000) => {
const [opacity] = useState(new Animated.Value(0));
const opacity = useAnimatedValue(0);
// Running the animation when the component is mounted
useEffect(() => {
Expand Down Expand Up @@ -128,14 +129,15 @@ export default App;
<TabItem value="typescript">

```SnackPlayer name=InteractionManager%20Function%20Component%20Basic%20Example&supportedPlatforms=ios,android&ext=tsx
import React, {useState, useEffect} from 'react';
import React, {useEffect} from 'react';
import {
Alert,
Animated,
InteractionManager,
Platform,
StyleSheet,
Text,
useAnimatedValue,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
Expand All @@ -147,7 +149,7 @@ const instructions = Platform.select({
});
const useFadeIn = (duration = 5000) => {
const [opacity] = useState(new Animated.Value(0));
const opacity = useAnimatedValue(0);
// Running the animation when the component is mounted
useEffect(() => {
Expand Down
14 changes: 10 additions & 4 deletions docs/transforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,19 @@ The `transformOrigin` property sets the origin for a view's transformations. The

# Example

```SnackPlayer name=TransformOrigin%20Example
import React, {useRef, useEffect} from 'react';
import {Animated, View, StyleSheet, Easing} from 'react-native';
```SnackPlayer name=TransformOrigin%20Example&supportedPlatforms=ios,android
import React, {useEffect} from 'react';
import {
Animated,
View,
StyleSheet,
Easing,
useAnimatedValue,
} from 'react-native';
import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context';
const App = () => {
const rotateAnim = useRef(new Animated.Value(0)).current;
const rotateAnim = useAnimatedValue(0);
useEffect(() => {
Animated.loop(
Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-0.70/easing.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The following helpers are used to modify other easing functions.

## Example

```SnackPlayer name=Easing%20Demo
```SnackPlayer name=Easing%20Demo&supportedPlatforms=ios,android
import React from "react";
import { Animated, Easing, SectionList, StatusBar, StyleSheet, Text, TouchableOpacity, View } from "react-native";
Expand Down
5 changes: 3 additions & 2 deletions website/versioned_docs/version-0.70/interactionmanager.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ By default, queued tasks are executed together in a loop in one `setImmediate` b
### Basic

```SnackPlayer name=InteractionManager%20Function%20Component%20Basic%20Example&supportedPlatforms=ios,android
import React, { useState, useEffect } from "react";
import React, { useEffect } from "react";
import {
Alert,
Animated,
Expand All @@ -51,6 +51,7 @@ import {
StyleSheet,
Text,
View,
useAnimatedValue,
} from "react-native";
const instructions = Platform.select({
Expand All @@ -63,7 +64,7 @@ const instructions = Platform.select({
const useMount = func => useEffect(() => func(), []);
const useFadeIn = (duration = 5000) => {
const [opacity] = useState(new Animated.Value(0));
const opacity = useAnimatedValue(0);
// Running the animation when the component is mounted
useMount(() => {
Expand Down
9 changes: 5 additions & 4 deletions website/versioned_docs/version-0.71/animated.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,21 @@ The following example contains a `View` which will fade in and fade out based on
<Tabs groupId="syntax" queryString defaultValue={constants.defaultSyntax} values={constants.syntax}>
<TabItem value="functional">

```SnackPlayer name=Animated
import React, {useRef} from 'react';
```SnackPlayer name=Animate&supportedPlatforms=ios,android
import React from 'react';
import {
Animated,
Text,
View,
StyleSheet,
Button,
SafeAreaView,
useAnimatedValue,
} from 'react-native';
const App = () => {
// fadeAnim will be used as the value for opacity. Initial Value: 0
const fadeAnim = useRef(new Animated.Value(0)).current;
const fadeAnim = useAnimatedValue(0);
const fadeIn = () => {
// Will change fadeAnim value to 1 in 5 seconds
Expand Down Expand Up @@ -601,7 +602,7 @@ Stops any running animation and resets the value to its original.

### `Value`

Standard value class for driving animations. Typically initialized with `new Animated.Value(0);`
Standard value class for driving animations. Typically initialized with `useAnimatedValue(0)` or `new Animated.Value(0);` in class components.

You can read more about `Animated.Value` API on the separate [page](animatedvalue).

Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-0.71/animatedvalue.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: Animated.Value

Standard value for driving animations. One `Animated.Value` can drive multiple properties in a synchronized fashion, but can only be driven by one mechanism at a time. Using a new mechanism (e.g. starting a new animation, or calling `setValue`) will stop any previous ones.

Typically initialized with `new Animated.Value(0);`
Typically initialized with `useAnimatedValue(0) or `new Animated.Value(0);` in class components.

---

Expand Down
15 changes: 8 additions & 7 deletions website/versioned_docs/version-0.71/animations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ For example, a container view that fades in when it is mounted may look like thi
<TabItem value="javascript">

```SnackPlayer ext=js
import React, {useRef, useEffect} from 'react';
import React, {useEffect, useAnimatedValue} from 'react';
import {Animated, Text, View} from 'react-native';
const FadeInView = props => {
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
const fadeAnim = useAnimatedValue(0); // Initial value for opacity: 0
useEffect(() => {
Animated.timing(fadeAnim, {
Expand Down Expand Up @@ -74,15 +74,15 @@ export default () => {
<TabItem value="typescript">

```SnackPlayer ext=tsx
import React, {useRef, useEffect} from 'react';
import {Animated, Text, View} from 'react-native';
import React, {useEffect} from 'react';
import {Animated, Text, View, useAnimatedValue} from 'react-native';
import type {PropsWithChildren} from 'react';
import type {ViewStyle} from 'react-native';
type FadeInViewProps = PropsWithChildren<{style: ViewStyle}>;
const FadeInView: React.FC<FadeInViewProps> = props => {
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
const fadeAnim = useAnimatedValue(0); // Initial value for opacity: 0
useEffect(() => {
Animated.timing(fadeAnim, {
Expand Down Expand Up @@ -314,7 +314,7 @@ The following example implements a horizontal scrolling carousel where the scrol
<TabItem value="functional">

```SnackPlayer name=Animated&supportedPlatforms=ios,android
import React, {useRef} from 'react';
import React from 'react';
import {
SafeAreaView,
ScrollView,
Expand All @@ -324,14 +324,15 @@ import {
ImageBackground,
Animated,
useWindowDimensions,
useAnimatedValue,
} from 'react-native';

const images = new Array(6).fill(
'https://images.unsplash.com/photo-1556740749-887f6717d7e4',
);

const App = () => {
const scrollX = useRef(new Animated.Value(0)).current;
const scrollX = useAnimatedValue(0);

const {width: windowWidth} = useWindowDimensions();

Expand Down
8 changes: 5 additions & 3 deletions website/versioned_docs/version-0.71/easing.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ import {
Text,
TouchableOpacity,
View,
useAnimatedValue,
} from 'react-native';
const App = () => {
let opacity = new Animated.Value(0);
let opacity = useAnimatedValue(0);
const animate = easing => {
opacity.setValue(0);
Expand Down Expand Up @@ -203,7 +204,7 @@ export default App;
</TabItem>
<TabItem value="typescript">

```SnackPlayer name=Easing%20Demo&ext=tsx
```SnackPlayer name=Easing%20Demo&ext=tsx&supportedPlatforms=ios,android
import React from 'react';
import {
Animated,
Expand All @@ -214,11 +215,12 @@ import {
Text,
TouchableOpacity,
View,
useAnimatedValue,
} from 'react-native';
import type {EasingFunction} from 'react-native';
const App = () => {
let opacity = new Animated.Value(0);
let opacity = useAnimatedValue(0);
const animate = (easing: EasingFunction) => {
opacity.setValue(0);
Expand Down
Loading

0 comments on commit f6728c4

Please sign in to comment.