Skip to content

Commit

Permalink
feat: add deps arrays, docs, & close #120
Browse files Browse the repository at this point in the history
  • Loading branch information
nandorojo committed Nov 18, 2021
1 parent 7b1e6a4 commit 96c01ec
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 18 deletions.
12 changes: 11 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// generated by @nandorojo/lint-expo
{ "extends": ["nando"] }
{
"extends": ["nando"],
"rules": {
"react-hooks/exhaustive-deps": [
"warn",
{
"additionalHooks": "(useDerivedValue|useAnimatedStyle|useAnimatedProps)"
}
]
}
}
24 changes: 24 additions & 0 deletions docs/docs/interactions/eslint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
id: eslint
title: ESLint usage
---

It is recommended to use a dependency array with all Moti Interaction hooks, just like `useMemo` and `useCallback`.

To enforce this with your ESLint plugin, you can use the `additionalHooks` field in your ESLint config:

```json
{
"rules": {
// ...
"react-hooks/exhaustive-deps": [
"error",
{
"additionalHooks": "(useMotiPressableTransition|useMotiPressable|useMotiPressables|useMotiPressableAnimatedProps|useInterpolateMotiPressable)"
}
]
}
}
```

This assumes you've already installed the `react-hooks` eslint [plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks).
1 change: 1 addition & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
'interactions/use-pressable-transition',
'interactions/use-pressable-interpolate',
'interactions/merge',
'interactions/eslint',
],
Components: ['skeleton'],
Web: ['web', 'next'],
Expand Down
19 changes: 18 additions & 1 deletion packages/core/src/use-map-animate-to-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,24 @@ export default function useMapAnimateToStyle<Animate>({
// }

return final
})
}, [
animateProp,
custom,
defaultDelay,
disableInitialAnimation,
exitProp,
exitTransitionProp,
fromProp,
hasExitStyle,
isMounted.value,
isPresent,
onDidAnimate,
reanimatedOnDidAnimated,
reanimatedSafeToUnmount,
state,
stylePriority,
transitionProp,
])

useEffect(() => {
isMounted.value = true
Expand Down
21 changes: 16 additions & 5 deletions packages/interactions/src/pressable/pressable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const MotiPressable = forwardRef<View, MotiPressableProps>(
exit,
children,
exitTransition,
transition,
transition: transitionProp,
style,
onPressOut,
onPressIn,
Expand All @@ -51,10 +51,21 @@ export const MotiPressable = forwardRef<View, MotiPressableProps>(
const hovered = hoveredValue || _hovered
const pressed = pressedValue || _pressed

const interaction = useDerivedValue<MotiPressableInteractionState>(() => ({
hovered: hovered.value,
pressed: pressed.value,
}))
const interaction = useDerivedValue<MotiPressableInteractionState>(
() => ({
hovered: hovered.value,
pressed: pressed.value,
}),
[hovered, pressed]
)

const transition = useDerivedValue(() => {
if (typeof transitionProp === 'function') {
return transitionProp(interaction.value)
}

return transitionProp || {}
}, [transitionProp, interaction])

const __state = useDerivedValue(() => {
if (typeof animate === 'function') {
Expand Down
46 changes: 37 additions & 9 deletions packages/interactions/src/pressable/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ComponentProps } from 'react'
import type { MotiView } from '@motify/components'
import type { ViewStyle, Insets } from 'react-native'
import type { MotiAnimationProp } from '@motify/core'
import type { MotiAnimationProp, MotiTransition } from '@motify/core'
import type Animated from 'react-native-reanimated'

export type MotiPressableInteractionState = {
Expand All @@ -11,23 +11,51 @@ export type MotiPressableInteractionState = {

export type AnimateProp = MotiAnimationProp<ViewStyle>

export type MotiPressableInteractionProp = (
type Interactable<T> = (
interaction: MotiPressableInteractionState
) => NonNullable<AnimateProp>
) => NonNullable<T>

export type MotiPressableProp = AnimateProp | MotiPressableInteractionProp
type InteractableProp<T> = Interactable<T> | T

export type MotiPressableInteractionProp = Interactable<AnimateProp>

export type MotiPressableTransitionProp = InteractableProp<MotiTransition>

export type MotiPressableProp = InteractableProp<AnimateProp>

export type MotiPressableProps = {
/*
* Worklet that returns the `transition` prop. Or, just a normal `transition` prop, similar to `MotiView`.
*
* It's recomended that you memoize this prop with `useMemo`.
*
* ```tsx
* <MotiPressable
* transition={useMemo(() => ({ hovered, pressed }) => {
* 'worklet'
* return {
* delay: hovered || pressed ? 0 : 200
* }
* }, [])}
* />
* ```
*
* @worklet
*/
transition?: MotiPressableTransitionProp
/*
* Worklet that returns the `animated` prop. Or, just a normal `animate` prop, similar to `MotiView`.
*
* It's recomended that you memoize this prop with `useCallback`.
* It's recomended that you memoize this prop with `useMemo`.
*
* ```tsx
* <MotiPressable
* animate={useCallback(({ hovered }) => ({
* opacity: hovered ? 0.8 : 1
* }), [])}
* animate={useMemo(() => ({ hovered, pressed }) => {
* 'worklet'
* return {
* opacity: hovered ? 0.8 : 1
* }
* }, [])}
* />
* ```
*
Expand Down Expand Up @@ -68,5 +96,5 @@ export type MotiPressableProps = {
hoveredValue?: Animated.SharedValue<boolean>
} & Pick<
ComponentProps<typeof MotiView>,
'children' | 'exit' | 'from' | 'transition' | 'exitTransition' | 'style'
'children' | 'exit' | 'from' | 'exitTransition' | 'style'
>
12 changes: 10 additions & 2 deletions packages/interactions/src/pressable/use-pressables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,23 @@ export function useMotiPressables(
* Function that receives the interaction state from all parent containers and returns a style object/
* @worklet
*/
factory: Factory
factory: Factory,
deps: readonly any[] = []
) {
const context = useMotiPressableContext()

if (!deps) {
console.warn(
'[@motify/interactions] useMotiPressables is missing a dependency array as the second argument. https://moti.fyi/interactions/use-pressables. You can use this hook to your ESLint plugin for hooks using the additionalHooks field: https://www.npmjs.com/package/eslint-plugin-react-hooks'
)
}

const __state = useDerivedValue(() => {
const animatedResult = factory(context.containers)

return animatedResult
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [context.containers, ...deps])

const state = useMemo(() => ({ __state }), [__state])

Expand Down

0 comments on commit 96c01ec

Please sign in to comment.