Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

moti requires expo-linear-gradient when im using react native cli only without expo @nandorojo #329

Open
2 of 3 tasks
husam868maher opened this issue Dec 18, 2023 · 14 comments

Comments

@husam868maher
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Do you want this issue prioritized?

  • Yes, I have sponsored
  • Not urgent

Current Behavior

getting this weird error while trying to use moti with react native only

my package json contains these packages

"react-native-reanimated": "^3.6.1",
"react-native": "0.72.7",
"moti": "^0.27.2",
Screenshot 2023-12-18 at 7 29 38 PM

Expected Behavior

getting this weird error while trying to use moti with react native only

my package json contains these packages

"react-native-reanimated": "^3.6.1",
"react-native": "0.72.7",
"moti": "^0.27.2",
Screenshot 2023-12-18 at 7 29 38 PM

Steps To Reproduce

create new react native project with npx react-native init project
install moti package
instal reanimated package
use in in the code

Versions

"react-native-reanimated": "^3.6.1",
    "react-native": "0.72.7",
    "moti": "^0.27.2",

Screenshots

Screenshot 2023-12-18 at 7 29 38 PM Screenshot 2023-12-18 at 7 32 35 PM

Reproduction

npx create-react-native-app -t with-moti

@yamak-app
Copy link

@husam868maher @nandorojo same here any fix for this ?

@nandorojo
Copy link
Owner

Did you use the non-expo approach from the skeleton on docs?

https://moti.fyi/skeleton#non-expo-users

@VladyslavMartynov10
Copy link

Hey @nandorojo!

Recently I've also tried to use Skeleton component with react-native-cli & react-native-lineal-gradient but got the same error as @yamak-app

@nandorojo
Copy link
Owner

Seems like your Babel plugin isn't working

@nandorojo
Copy link
Owner

You added the module resolver one from the docs?

@VladyslavMartynov10
Copy link

Hi @nandorojo !

Thanks again for such great package for animations. I've resolved
the problem for react-native cli by using this small patch changes:

moti+0.27.2.patch

@nandorojo
Copy link
Owner

Can you please send the code inline?

@VladyslavMartynov10
Copy link

expo.tsx file:

import { LinearGradient } from 'react-native-linear-gradient'
import React from 'react'

import SkeletonNative from './skeleton-new'
import { MotiSkeletonProps } from './types'

export default function SkeletonExpo(
  props: Omit<MotiSkeletonProps, 'Gradient'>
) {
  return <SkeletonNative {...props} Gradient={LinearGradient as any} />
}

SkeletonExpo.Group = SkeletonNative.Group

skeleton.tsx:


import { LinearGradient } from 'react-native-linear-gradient'
import React, { useState, createContext, useContext } from 'react'
import { View, StyleSheet } from 'react-native'

import { View as MotiView } from '../components'
import { AnimatePresence, MotiTransitionProp } from '../core'
import {
  DEFAULT_SKELETON_SIZE as DEFAULT_SIZE,
  defaultDarkColors,
  defaultLightColors,
  baseColors,
} from './shared'
import { MotiSkeletonProps } from './types'

export default function Skeleton(props: MotiSkeletonProps) {
  const skeletonGroupContext = useContext(SkeletonGroupContext)
  const {
    radius = 8,
    children,
    show = skeletonGroupContext ?? !children,
    width,
    height = children ? undefined : DEFAULT_SIZE,
    boxHeight,
    colorMode = 'dark',
    colors = colorMode === 'dark' ? defaultDarkColors : defaultLightColors,
    backgroundColor = colors[0] ??
      colors[1] ??
      baseColors[colorMode]?.secondary,
    backgroundSize = 6,
    disableExitAnimation,
    transition,
  } = props

  const [measuredWidth, setMeasuredWidth] = useState(0)

  const getBorderRadius = () => {
    if (radius === 'square') {
      return 0
    }
    if (radius === 'round') {
      return 99999
    }
    return radius
  }

  const borderRadius = getBorderRadius()

  const getOuterHeight = () => {
    if (boxHeight != null) return boxHeight
    if (show && !children) {
      return height
    }
    return undefined
  }

  const outerHeight = getOuterHeight()

  return (
    <View
      style={{
        height: outerHeight,
        minHeight: height,
        minWidth: width ?? (children ? undefined : DEFAULT_SIZE),
      }}
    >
      {children}
      <AnimatePresence>
        {show && (
          <MotiView
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              borderRadius,
              width: width ?? (children ? '100%' : DEFAULT_SIZE),
              height: height ?? '100%',
              overflow: 'hidden',
            }}
            animate={{
              backgroundColor,
              opacity: 1,
            }}
            transition={{
              type: 'timing',
            }}
            exit={
              !disableExitAnimation && {
                opacity: 0,
              }
            }
            onLayout={({ nativeEvent }) => {
              if (measuredWidth === nativeEvent.layout.width) return

              setMeasuredWidth(nativeEvent.layout.width)
            }}
            pointerEvents="none"
          >
            <AnimatedGradient
              // force a key change to make the loop animation re-mount
              key={`${JSON.stringify(colors)}-${measuredWidth}-${JSON.stringify(
                transition || null
              )}`}
              colors={colors}
              backgroundSize={backgroundSize}
              measuredWidth={measuredWidth}
              transition={transition}
            />
          </MotiView>
        )}
      </AnimatePresence>
    </View>
  )
}

const AnimatedGradient = React.memo(
  function AnimatedGradient({
    measuredWidth,
    colors,
    backgroundSize,
    transition = {},
  }: {
    measuredWidth: number
    colors: string[]
    backgroundSize: number
    transition?: MotiTransitionProp
  }) {
    return (
      <MotiView
        style={StyleSheet.absoluteFillObject}
        from={{ opacity: 0 }}
        transition={{
          type: 'timing',
          duration: 200,
        }}
        animate={
          measuredWidth
            ? {
                opacity: 1,
              }
            : undefined
        }
      >
        <MotiView
          style={[
            StyleSheet.absoluteFillObject,
            {
              width: measuredWidth * backgroundSize,
            },
          ]}
          from={{
            translateX: 0,
          }}
          animate={
            measuredWidth
              ? {
                  translateX: -measuredWidth * (backgroundSize - 1),
                }
              : undefined
          }
          transition={{
            loop: true,
            delay: 200,
            type: 'timing',
            duration: 3000,
            ...(transition as any),
          }}
        >
          <LinearGradient
            colors={colors}
            start={{
              x: 0.1,
              y: 1,
            }}
            end={{
              x: 1,
              y: 1,
            }}
            style={StyleSheet.absoluteFillObject}
          />
        </MotiView>
      </MotiView>
    )
  },
  function propsAreEqual(prev, next) {
    if (prev.measuredWidth !== next.measuredWidth) return false

    if (prev.backgroundSize !== next.backgroundSize) return false

    const didColorsChange = prev.colors.some((color, index) => {
      return color !== next.colors[index]
    })

    if (didColorsChange) return false

    // transition changes will not be respected, but it'll be in the key
    return true
  }
)

const SkeletonGroupContext = createContext<boolean | undefined>(undefined)

function SkeletonGroup({
  children,
  show,
}: {
  children: React.ReactNode
  /**
   * If `true`, all `Skeleton` children components will be shown.
   *
   * If `false`, the `Skeleton` children will be hidden.
   */
  show: boolean
}) {
  return (
    <SkeletonGroupContext.Provider value={show}>
      {children}
    </SkeletonGroupContext.Provider>
  )
}

Skeleton.Group = SkeletonGroup

The only important change here is this new import { LinearGradient } from 'react-native-linear-gradient' instead of expo-linear-gradient.

babel.config.js:

module.exports = function (api) {
  api.cache(true);

 return {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    'react-native-reanimated/plugin',
    [
      'babel-plugin-module-resolver',
      {
        root: ['./src/'],
        alias: [
          { 'moti/skeleton': 'moti/skeleton/react-native-linear-gradient' },
        ],
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
      },
    ],
  ],
  };
};

@VladyslavMartynov10
Copy link

I didn't properly tested it with other RN versions. In my case it's working like a charm for both Android/iOS "react-native": "0.72.7"

@VladyslavMartynov10
Copy link

@nandorojo Let me know if it's working on your side?

By the way, maybe it's better to override this react-native-linear-gradient/expo-linear-gradient dependency directly in json with "overrides" or something similar to it. What do you think?

@nandorojo
Copy link
Owner

nandorojo commented Jan 25, 2024

Please send the patch file inline

What's wrong with the current docs for the skeleton using the Babel plugin? Doesn't it do this for you?

@VladyslavMartynov10
Copy link

diff --git a/node_modules/moti/src/skeleton/expo.tsx b/node_modules/moti/src/skeleton/expo.tsx
index 1674b89..083824c 100644
--- a/node_modules/moti/src/skeleton/expo.tsx
+++ b/node_modules/moti/src/skeleton/expo.tsx
@@ -1,4 +1,4 @@
-import { LinearGradient } from 'expo-linear-gradient'
+import { LinearGradient } from 'react-native-linear-gradient'
 import React from 'react'
 
 import SkeletonNative from './skeleton-new'
diff --git a/node_modules/moti/src/skeleton/skeleton.tsx b/node_modules/moti/src/skeleton/skeleton.tsx
index 3ef5dd1..0e3bed3 100644
--- a/node_modules/moti/src/skeleton/skeleton.tsx
+++ b/node_modules/moti/src/skeleton/skeleton.tsx
@@ -1,4 +1,4 @@
-import { LinearGradient } from 'expo-linear-gradient'
+import { LinearGradient } from 'react-native-linear-gradient'
 import React, { useState, createContext, useContext } from 'react'
 import { View, StyleSheet } from 'react-native'
 
diff --git a/node_modules/moti/src/skeleton/types.ts b/node_modules/moti/src/skeleton/types.ts
index 7fa9d78..176f224 100644
--- a/node_modules/moti/src/skeleton/types.ts
+++ b/node_modules/moti/src/skeleton/types.ts
@@ -1,4 +1,4 @@
-import { LinearGradient } from 'expo-linear-gradient'
+import { LinearGradient } from 'react-native-linear-gradient'
 import { MotiTransitionProp } from '../core'
 import { baseColors } from './shared'

babel-plugin is working as expected now. Before, I had some troubles with changing babel.config.js.

@falegreg
Copy link

Same problem here, I tried with the same patch but it's still not working. Any updates?

@Mghendi-Pato
Copy link

This solution worked for me using React Native CLI:

Install react-native-linear-gradient:
npm install react-native-linear-gradient

Replace all instances of:
import { LinearGradient } from 'expo-linear-gradient'
with:
import { LinearGradient } from 'react-native-linear-gradient'

Files where the change is needed:

  • node_modules/moti/src/skeleton/expo.tsx
  • node_modules/moti/src/skeleton/skeleton.tsx
  • node_modules/moti/src/skeleton/types.ts
  • node_modules/moti/build/skeleton/expo.js

This should resolve the issue when using React Native CLI instead of Expo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants