Skip to content
React Native Reanimated ガイドブック
Esc
navigateopen⌘Jpreview
On this page

共有値とスタイル

インラインスタイルの限界をきっかけにuseAnimatedStyleを導入し、useSharedValueとuseAnimatedStyleの引数・戻り値・注意点までひととおり押さえます。

前章では共有値をAnimated.Viewstyleに直接渡してアニメーションさせました。この書き方は手軽ですが、少し複雑なことをしようとするとすぐに壁にぶつかります。この章ではその壁を確認し、useAnimatedStyleで乗り越えます。あわせて、useSharedValueuseAnimatedStyleの細かな仕様も整理しておきます。

インラインスタイルの限界

共有値をインラインのスタイルに渡す書き方には、大きな弱点があります。共有値に入っている値を読み取って加工できません。

const width = useSharedValue(100);

<Animated.View style={{ width }} />; // これはできる
<Animated.View style={{ width: width * 5 }} />; // これはできない

widthは共有値そのものであって、ただの数値ではありません。だから* 5のような計算を挟もうとしても期待どおりには動きません。条件分岐やループを絡めた、もう少し凝ったアニメーションを組みたくなると、インラインスタイルだけでは足りなくなります。

useAnimatedStyleでスタイルを組み立てる

ここで登場するのがuseAnimatedStyleです。StyleSheetのスタイルによく似たスタイルオブジェクトを返すフックで、その中では共有値の.valueを読み取って自由に計算できます。返したスタイルをAnimatedコンポーネントのstyleに渡すと、共有値やReactのstateが変わるたびに自動で更新されます。

次のデモは、ボタンを押すたびに四角形が右へ動きます。translateXの値を2倍してからwithSpringに渡している点に注目してください。共有値の.valueにアクセスできるからこそできる書き方です。

デモを読み込み中…

「コード」タブを見ると、アニメーションに関わるロジックがuseAnimatedStyleの中に一か所へまとまっているのが分かります。値の加工も、アニメーション関数の適用も、すべてこのフックの中で完結します。これがインラインスタイルにはない利点です。

useSharedValueをくわしく

ここで共有値の仕様を整理しておきます。useSharedValueは初期値を1つ受け取り、共有値を返します。

const sv = useSharedValue(100);

// 読み取り
console.log(sv.value);

// 書き換え
sv.value += 50;

初期値には数値・文字列・真偽値のほか、配列やオブジェクトも渡せます。値の読み書きは.valueプロパティ、またはget/setメソッドで行います。React Compilerを使っている場合は、.valueの直接操作ではなくget/setを使うのが推奨です。

共有値を扱ううえで、いくつか気をつけたい点があります。

  • レンダリング中に.valueを読み書きしないでください。レンダー中の副作用はReactのルール違反です。読み書きはuseAnimatedStyleuseEffectなど、レンダー中に実行されないコールバックの中で行います
  • sv.valueを書き換えても、通常のReact再レンダーは起きません。共有値はただのJavaScriptオブジェクトだからです
  • JSスレッドで.valueを書き換えても、その直後のログには古い値が出ます。UIスレッド側では同期的に、JSスレッド側では非同期に更新が反映されるためです

分割代入は共有値の反応性を壊すので避けてください。

let { value } = sv; // これはダメ

value += 50; // スタイルは更新されない

オブジェクトや配列を入れているときは、プロパティを個別に書き換えるのではなく、まるごと再代入します。大きなデータを部分的に変えたいときは.modifyメソッドが使えます。

const sv = useSharedValue({ x: 0, y: 0 });

sv.value.x = 50; // 反応性が失われる
sv.value = { x: 50, y: 0 }; // これでよい

useAnimatedStyleの細かい挙動

useAnimatedStyleは第1引数にスタイルを返す関数(updater)を取り、第2引数に依存配列を取ります。戻り値はAnimatedコンポーネントのstyleに渡すためのスタイルオブジェクトです。

第2引数の依存配列は、web向けにBabelプラグインを使わない構成のときにだけ関係します。本書のデモはまさにその構成で動いているため、updaterの中で読む共有値やstateを依存配列に明示しています。デモの「コード」タブに依存配列が並んでいるのはこのためです。

そのほかに押さえておきたい挙動をまとめます。

  • アニメーションスタイルは静的なスタイルより優先されます。style={[styles.box, animatedStyle]}のように配列で渡し、動的な部分だけをuseAnimatedStyleで書くと、不要な再計算を避けられます
  • useAnimatedStyleが返す値はAnimatedコンポーネントにしか渡せません。通常のコンポーネントに渡すとエラーになります
  • updaterの中で共有値を書き換えるのは未定義の動作で、無限ループを招くことがあります

プロパティをアニメーションさせる

widthtransformのようにstyleに渡す値だけでなく、コンポーネントのpropsをアニメーションさせたい場面もあります。代表例がSVGです。SVGの円は半径などをpropsとして受け取ります。

<Circle cx="50" cy="50" r="10" fill="blue" />

こうしたコンポーネントのpropsをアニメーション可能にするには、createAnimatedComponentでラップし、useAnimatedPropspropsを組み立てます。

import Animated, {
  useAnimatedProps,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";
import { Svg, Circle } from "react-native-svg";

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

function App() {
  const r = useSharedValue(20);

  const animatedProps = useAnimatedProps(() => ({
    r: withTiming(r.value),
  }));

  return (
    <Svg>
      <AnimatedCircle cx="50%" cy="50%" fill="#b58df1" animatedProps={animatedProps} />
    </Svg>
  );
}

useAnimatedPropsはupdaterがアニメーションさせたいpropsのオブジェクトを返し、その戻り値をコンポーネントのanimatedPropsに渡します。使い方はuseAnimatedStyleとよく似ていて、こちらも.valueにアクセスしてロジックをまとめられます。

このSVGの例はreact-native-svgに依存します。本書のデモ環境にはreact-native-svgを入れていないため、動くデモではなくコード例として示しました。useAnimatedPropsそのものの詳しい使いどころは第5章でもう一度取り上げます。

この章のまとめ

  • インラインスタイルは手軽ですが、共有値の値を読み取って加工できません
  • useAnimatedStyleを使うと.valueにアクセスでき、計算やアニメーション関数をひとまとめにできます
  • スタイルはpropsではなくstyleに、propsuseAnimatedPropsで扱います
  • 共有値の読み書きはレンダー中を避け、分割代入やオブジェクトの部分更新には注意します

次章では、withTimingwithSpringの設定に踏み込み、動きの表情を自分で調整できるようにします。

この章のもとになった公式ドキュメント