ReactNative@v0.63で追加されたPressableが地味にすごい
はじめに
今回はReactNative
のv0.63
で追加されたPressable
についての記事になります。
タップ時とそれ以外とでスタイルや描画内容を変更できるコンポーネントになります。
そこまで目新しいものではないですが、定義部分が面白く参考になったので解説していきます。
Pressableとは
v0.63
で追加されたコンポーネントのひとつです。
タップ操作に関するコンポーネントで、公式には下記の説明があります。
How it works
On an element wrapped by Pressable:
- onPressIn is called when a press is activated.
- onPressOut is called when the press gesture is deactivated.
After pressing onPressIn, one of two things will happen:
- The person will remove their finger, triggering onPressOut followed by onPress.
- If the person leaves their finger longer than 500 milliseconds before removing it, onLongPress is triggered. (onPressOut will still fire when they remove their finger.)
タップしてから離すまでの挙動や、長押しした時のイベント操作についての説明です。
この挙動自体は目新しいものではなくTouchableOpacity
等でも同じようなプロパティが用意されています。
実際「押された時に○○する」といった動作を実装する場合、TouchableOpacity
やButton
を使った方が馴染みがあります。
どこが新しい?
個人的にいいなと思ったのはstyle
とchildren
のプロパティです。
例えば、下記のような見た目のUI
を実装したいとします。
「押される前」と「押している間」で「背景色」と「内部の文字列」が異なります。
これをTouchableOpacity
やButton
で実装する場合「押している間」というのが何気に面倒臭いです。
なぜなら、 クラスコンポーネントのstate
や関数コンポーネントでuseState
を使ってpressing
のように「今押している」という状態を持つ必要がある からです。
これをPressable
で実装すると下記のようになります。
import React from 'react';
import {StyleSheet, View, Text, StatusBar, Pressable} from 'react-native';
const App: () => React.ReactNode = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Pressable
style={({pressed} : PressableStateCallbackType) => [
{
borderRadius: 8,
padding: 6,
backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white',
},
]}>
{({pressed} : PressableStateCallbackType) => <Text>{pressed ? 'Pressed!' : 'Press Me'}</Text>}
</Pressable>
</View>
</>
);
};
export default App;
見てわかる通り、state
を使っていません。
その代わりstyle
とchildren
に該当するPressable
の子要素でpressed
という見慣れない変数を使っています。
このpressed
はコード上で定義されたものではなく、Pressable
側で定義されたプロパティです。
これだけだと挙動がわかりにくいので、定義ファイルを参照していきましょう。
styleの定義
Pressable
のstyle
の定義は以下のようになります。
/**
* Either view styles or a function that receives a boolean reflecting whether
* the component is currently pressed and returns view styles.
*/
style?: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>);
ポイントは StyleProp
を返す関数 を指定できる点です。
ここが通常のコンポーネントのstyle
と異なります。
例えばView
でstyle
を指定する場合、以下のように書くことが多いと思います。
<View style={{ flex: 1, backgroundColor: '#333' }}></View>
オブジェクト指定しています。
これはView
のstyle
が以下のように定義されているためです。
style?: StyleProp<ViewStyle>;
Pressable
のstyle
では上記の定義に加えて、
((state: PressableStateCallbackType) => StyleProp<ViewStyle>)
という形式でもstyle
を指定できるようになっています。
これは PressableStateCallbackType
型の引数を受けてStyleProp<ViewStyle>
を返す関数を指定 できるという意味です。
ちなみにv0.63
時点でPressableStateCallbackType
の型定義は以下のようになります。
export type PressableStateCallbackType = Readonly<{
pressed: boolean;
}>;
ここで先ほどのpressed
が定義されていました。
pressed
はPressable
がタップされているかどうかの状態を表します。
上記を踏まえて、先ほどのコードをstyle
に着目してみると以下のことが分かります。
<Pressable
// ①styleにPressableStateCallbackType型の引数を取り、StyleProp<ViewStyle>を返す関数を指定している
// ②PressableStateCallbackTypeにはPressableの押下状態であるpressedが格納されている
style={({pressed} : PressableStateCallbackType) => [
{
borderRadius: 8,
padding: 6,
// ③pressedの値によってbackgroundColorの値を変更している
backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white',
},
]}>
{({pressed} : PressableStateCallbackType) => <Text>{pressed ? 'Pressed!' : 'Press Me'}</Text>}
</Pressable>
こうすることでstate
に頼ることなく「押している間」のスタイル変更を実現しています。
childrenの定義
style
と同様にchildren
の定義も異なります。
通常のFunctionalComponent
のchildren
は以下のような定義です。
children?: ReactNode;
それに対してPressable
のchildren
は以下のような定義です。
/**
* Either children or a render prop that receives a boolean reflecting whether
* the component is currently pressed.
*/
children?: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode);
こちらもポイントは PressableStateCallbackType
を受け取りReact.ReactNode
を返す関数を指定できる ことです。
こうすることでchildren
にあたる子孫コンポーネントの描画内容を動的に定義することができます。
あたらめてchildren
に該当する部分を見ていきましょう。
style
の時と同じようにpressed
の値で描画する内容を書き分けていますね。
// PressableStateCallbackTypeを受けて、その中のpressedの値で中身が異なるReact.Nodeを返す関数を定義
{({pressed} : PressableStateCallbackType) => <Text>{pressed ? 'Pressed!' : 'Press Me'}</Text>}
まとめ
今回はPressable
について、定義部分に着目しながら特徴を紹介しました。
冒頭でも少し触れましたが、単純に「押した時」にあれこれするだけならTouchableOpacity
やButton
の方が分かりやすいかと思います。
ただ、押している時に細かくUI
を制御したい場合にはPressable
を利用するとオススメです。
style
やchildren
の考え方については、自作のFunctionalComponent
を作成する場合にも応用できるため極力分かりやすく解説しました。
Hooks
が主流となるとFunctionalComponent
を使う機会も増えるので、今後は外部ライブラリでもこういったコンポーネントが増えるかもしれませんね。
Discussion