#
Moti Interactions#
Snippet#
VideoFirst, import:
Next, animate your interactions:
#
AboutThe MotiPressable
component lets you animate based on pressed
and hovered
interactions, without triggering any re-renders.
The usage is very similar to Pressable
from react-native
.
The two differences are 1) you use the animate
prop rather than style
, and 2) the function you pass must be a worklet
, resulting in faster performance and a better developer experience.
Like always, MotiPressable
relies on the native thread when tracking interactions.
#
ExamplesCheck out these tweets from Fernando Rojo for more context:
- From Moti's API to MotiPressable
- Remaking BeatGig's web dropdown menu with moti interactions
- Animating children of a Pressable component without re-renders
#
InstallationVersions before 0.17.0 required you to install @motify/interactions
. This is no longer the case. See the PR.
Interactions now comes bundled with moti
. You can import it like so:
#
Peer dependenciesAs of moti@0.17.0
, moti/interactions
uses react-native-gesture-handler
v2. Upgrading may not be required, but it is recommended. This is the version included in Expo SDK 44.
You'll need to wrap your app with GestureHandlerRootView
from react-native-gesture-handler
. Please see their docs for installation instructions.
#
Animating childrenA common use-case of a component like Pressable
is styling children based on the animation state.
Before moti
, you might do this with the React Native Pressable
component.
Moti takes a different approach to improve performance and composition.
Rather than using React state to track the interaction, Moti uses Reanimated shared values with React Native Gesture Handler.
As a result, Moti's interactions trigger zero re-renders, and all animations are handled on the native thread.
#
The Moti wayLet's see what the above example might look like with Moti.
First, change Pressable
to MotiPressable
. Next, remove the function child, and instead pass a component directly. Let's call it Child
.
Then, in the Child
component, we can access the parent's interaction state with useMotiPressable
#
Access a specific parentuseMotiPressable
also optionally takes a unique id
as its first argument. This lets you access a specific parent pressable's interaction.
For example, say you have a list
component at the root, and you want to access its state:
Notice that the MotiPressable
component now has an id="list"
prop. This tells all of its children that it can be uniquely referred to as list
.
By default, the useMotiPressable
would access the interaction state of its closest MotiPressable
parent. That default behavior doesn't work for this case, since we want to animate based on the top-level list component.
In the Child
component, pass list
as the first argument to useMotiPressable
.
That's it. Now, useMotiPressable
will return animate based on the interaction state of the outer-most MotiPressable
.
#
Access multiple parentsIn the previous section, we saw how to access a unique parent's interaction state. But what if we want to combine the interaction states of multiple components for more complex animations?
That's easy too. Our previous example looked like this:
Let's add a unique id
prop to each MotiPressable
item that's rendered in the list, called item-${id}
.
Let's also pass the id
as a prop to the Child
.
Now, the Child
component can call useMotiPressables
instead of useMotiPressable
.
Say we want to make all items fade away when you hover over the list, except for the actual item you're hovering.
#
Animated propsLet's say you want to update a child component's props based on a parent's interaction state.
For example, you have a dropdown menu whose pointerEvents
should be none
when its container isn't hovered.
You can also pass a TypeScript generic to useMotiPressableAnimatedProps
:
useMotiPressableAnimatedProps
relies on useAnimatedProps
under the hood.
animatedProps
cannot be used withanimate
on the same prop on Web. If you need to do both, please split your usage into two components; one that receives theanimate
prop, and another that receivesanimateProps
. This is a reanimated limitation.
#
Interpolate interaction stateA rare but available use-case is useInterpolateMotiPressable
.
As the name implies, this lets you access the shared value state of a parent pressable.
Example:
If you're passing a unique id
prop to your pressable, you can also isolate this hook to that pressable.
Say the parent pressable has id="list"
, and you want to isolate this hook to the list
pressable:
Then, in the Item
component:
It returns an Animated.DerivedValue
. You can also type it with a generic:
Just like any derived value, you can read the value it returns with .value
:
#
PerformanceBy default, this component should have better performance than the native Pressable
. It triggers zero re-renders, and all animations are run on the native thread.
If your component re-renders often, consider wrapping your hook with useCallback
to improve performance:
For all the hooks provided, you can also pass a dependency array to improve performance. It works just like the dependency array for useMemo
, and it's always the last argument for any of the pressable hooks.
#
Web SupportMotiPressable
provides first-class support for Web, including hovered
and pressed
interactions.
Please note that Reanimated 2 uses JS animations on Web. That said, MotiPressable
still doesn't trigger re-renders on web.