首页 > web前端 > js教程 > 如何使用 @shopify/restyle 在 React Native 中构建类型强制的 UI 组件

如何使用 @shopify/restyle 在 React Native 中构建类型强制的 UI 组件

Mary-Kate Olsen
发布: 2024-12-02 09:05:15
原创
195 人浏览过

自从我在博客上写一篇技术文章以来已经有一段时间了,这是一篇关于使用 @shopify/restyle 和 expo 在 React Native 中构建类型强制 UI 组件的新文章。

@shopify/restyle 是一个强大的 React Native 样式库,可为您的 UI 组件带来类型安全性和一致性。与传统的样式方法不同,Restyle 允许您创建集中式主题配置,在整个应用程序中强制执行设计系统原则。

入门

项目设置

  • 使用 expo 设置您的 React Native 项目
npx create-expo-app@latest
登录后复制
登录后复制
  • 转到您的项目目录并使用 expo 安装 @shopify/restyle 包
cd /path/to/project
npx expo install @shopify/restyle
登录后复制

创建你的主题

创建 theme.tsx 文件来定义您的设计系统:

touch theme.tsx
登录后复制
  • 复制并粘贴默认主题配置
import {createTheme} from '@shopify/restyle';

const palette = {
  purpleLight: '#8C6FF7',
  purplePrimary: '#5A31F4',
  purpleDark: '#3F22AB',

  greenLight: '#56DCBA',
  greenPrimary: '#0ECD9D',
  greenDark: '#0A906E',

  black: '#0B0B0B',
  white: '#F0F2F3',
};

const theme = createTheme({
  colors: {
    mainBackground: palette.white,
    cardPrimaryBackground: palette.purplePrimary,
  },
  spacing: {
    s: 8,
    m: 16,
    l: 24,
    xl: 40,
  },
  textVariants: {
    header: {
      fontWeight: 'bold',
      fontSize: 34,
    },
    body: {
      fontSize: 16,
      lineHeight: 24,
    },
    defaults: {
      // We can define a default text variant here.
    },
  },
});

export type Theme = typeof theme;
export default theme;
登录后复制

实施主题提供者

更新您的 app/_layout.tsx:

import { DarkTheme, DefaultTheme } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import { StatusBar } from "expo-status-bar";
import { useEffect } from "react";
import "react-native-reanimated";

import { ThemeProvider } from "@shopify/restyle";
import theme from "@/theme";

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

export default function RootLayout() {
  const [loaded] = useFonts({
    SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
  });

  useEffect(() => {
    if (loaded) {
      SplashScreen.hideAsync();
    }
  }, [loaded]);

  if (!loaded) {
    return null;
  }

  return (
    <ThemeProvider theme={theme}>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
      <StatusBar>



<h2>
  
  
  Creating Reusable Components
</h2>

<h3>
  
  
  Text Component
</h3>



<pre class="brush:php;toolbar:false">touch components/Text.tsx
登录后复制
// In components/Text.tsx

import {createText} from '@shopify/restyle';
import {Theme} from '../theme';

export const Text = createText<Theme>();

登录后复制

让我们在主屏幕上使用它

import { Text } from "@/components/Text";
import { SafeAreaView } from "react-native-safe-area-context";

export default function HomeScreen() {
  return (
    <SafeAreaView>
      <Text margin="m" variant="header">
        This is the Home screen. Built using @shopify/restyle.
      </Text>
    </SafeAreaView>
  );
}

登录后复制

正如您在上面的代码中看到的,我们将边距作为“m”而不是数字传递。我们从 theme.tsxfile 中获取值。

// ./theme.tsx

const theme = createTheme({
  spacing: {
    s: 8,
    m: 16, // margin="m"
    l: 24,
    xl: 40,
  },
  textVariants: {
    header: { // our text header variant
      fontWeight: 'bold',
      fontSize: 34,
    },
    body: {
      fontSize: 16,
      lineHeight: 24,
    },
  },
    // ...rest of code
  },
});
登录后复制

这就是我们的主页视图的外观

How to build type-enforced UI components in React Native using @shopify/restyle

骨架装载机组件

让我们构建一张骨架装载机卡

touch components/SkeletonLoader.tsx
登录后复制
// components/SkeletonLoader.tsx

import {
  BackgroundColorProps,
  createBox,
  createRestyleComponent,
  createVariant,
  spacing,
  SpacingProps,
  VariantProps,
} from "@shopify/restyle";
import { Theme } from "@/theme";
import { View } from "react-native";

const Box = createBox<Theme>();

type Props = SpacingProps<Theme> &
  VariantProps<Theme, "cardVariants"> &
  BackgroundColorProps<Theme> &
  React.ComponentProps<typeof View>;

const CardSkeleton = createRestyleComponent<Props, Theme>([
  spacing,
  createVariant({ themeKey: "cardVariants" }),
]);

const SkeletonLoader = () => {
  return (
    <CardSkeleton variant="elevated">
      <Box
        backgroundColor="cardPrimaryBackground"
        height={20}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      >
      </Box>

      <Box
        backgroundColor="cardPrimaryBackground"
        height={100}
        marginBottom="s"
        width="90%"
        overflow="hidden"
        borderRadius={"m"}
      >
      </Box>
      <Box
        backgroundColor="cardPrimaryBackground"
        height={50}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      >
      </Box>
    </CardSkeleton>
  );
};

export default SkeletonLoader;

登录后复制
  • 我们从 @shopify/restyle 包中创建一个新框作为预定义组件,这将是我们创建骨架框的方式
const Box = createBox<Theme>();
登录后复制
  • 使用createStyleComponent创建一个新的CardSkeleton组件来创建自定义组件,我们传递了必须在theme.tsx文件中定义的间距和cardVariants的道具
type Props = SpacingProps<Theme> &
  VariantProps<Theme, "cardVariants"> &
  BackgroundColorProps<Theme> &
  React.ComponentProps<typeof View>;

const CardSkeleton = createRestyleComponent<Props, Theme>([
  spacing,
  createVariant({ themeKey: "cardVariants" }),
]);
登录后复制
  • 创建一个 SkeletonLoader 组件来渲染我们的 Skelton Card 组件
// components/SkeletonLoader.tsx

export const SkeletonLoader = () => {
  return (
    <CardSkeleton variant="elevated">
      <Box
        backgroundColor="cardPrimaryBackground"
        height={20}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      ></Box>

      <Box
        backgroundColor="cardPrimaryBackground"
        height={100}
        marginBottom="s"
        width="90%"
        overflow="hidden"
        borderRadius={"m"}
      ></Box>
      <Box
        backgroundColor="cardPrimaryBackground"
        height={50}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      ></Box>
    </CardSkeleton>
  );
};

登录后复制

我们还剩下一件事要使其正常工作,更新 theme.tsx 文件以拥有 cardVariants

const theme = createTheme({
  colors: {
    // Add Black Color to use it later on
    black: palette.black,
  },
  // Add Border Radius Variants
  borderRadii: {
    s: 4,
    m: 10,
    l: 25,
    xl: 75,
  },
  // Add Card Variants
  cardVariants: {
    elevated: {
      shadowColor: "black",
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.1,
      shadowRadius: 4,
      elevation: 3,
      borderRadius: "m",
    },
    defaults: {
      padding: "m",
      borderRadius: "m",
    },
  },
});

登录后复制

太棒了,但是让我们为我们的组件添加动画

// components/SkeletonLoader.tsx

const ShimmerAnimation = () => {
  const shimmerTranslate = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.loop(
      Animated.timing(shimmerTranslate, {
        toValue: 1,
        duration: 1500,
        useNativeDriver: true,
      })
    ).start();
  }, [shimmerTranslate]);

  const translateX = shimmerTranslate.interpolate({
    inputRange: [0, 1],
    outputRange: [-300, 300],
  });

  return (
    <Animated.View
     >



<p>and let’s use it in our Skeleton Loader Component<br>
</p>

<pre class="brush:php;toolbar:false">// components/SkeletonLoader.tsx

export const SkeletonLoader = () => {
  return (
    <CardSkeleton variant="elevated">
      <Box
        backgroundColor="cardPrimaryBackground"
        height={20}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      >
        <ShimmerAnimation />
      </Box>

      <Box
        backgroundColor="cardPrimaryBackground"
        height={100}
        marginBottom="s"
        width="90%"
        overflow="hidden"
        borderRadius={"m"}
      >
        <ShimmerAnimation />
      </Box>
      <Box
        backgroundColor="cardPrimaryBackground"
        height={50}
        marginBottom="s"
        width="70%"
        overflow="hidden"
        borderRadius={"m"}
      >
        <ShimmerAnimation />
      </Box>
    </CardSkeleton>
  );
};

登录后复制

这是完整的组件代码:

// components/SkeletonLoader.tsx

import { useEffect, useRef } from "react";
import { Animated } from "react-native";
import {
  BackgroundColorProps,
  createBox,
  createRestyleComponent,
  createVariant,
  spacing,
  SpacingProps,
  VariantProps,
} from "@shopify/restyle";
import { Theme } from "@/theme";
import { View } from "react-native";

const Box = createBox<Theme>();

const ShimmerAnimation = () => {
  const shimmerTranslate = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.loop(
      Animated.timing(shimmerTranslate, {
        toValue: 1,
        duration: 1500,
        useNativeDriver: true,
      })
    ).start();
  }, [shimmerTranslate]);

  const translateX = shimmerTranslate.interpolate({
    inputRange: [0, 1],
    outputRange: [-300, 300],
  });

  return (
    



Et voila, we made a skeleton loader card using @shopify/restyle using

How to build type-enforced UI components in React Native using @shopify/restyle

Support for dark mode

Let’s start with adding dark theme configuration, in your theme.tsxfile

// theme.tsx

export const darkTheme: Theme = {
  ...theme,
  colors: {
    ...theme.colors,
    mainBackground: palette.white,
    cardPrimaryBackground: palette.purpleDark,
    greenPrimary: palette.purpleLight,
  },
  textVariants: {
    ...theme.textVariants,
    defaults: {
      ...theme.textVariants.header,
      color: palette.purpleDark,
    },
  },
登录后复制

通过将深色主题配置添加到我们的layout.tsx 文件中来将其添加到应用程序布局中

npx create-expo-app@latest
登录后复制
登录后复制
  • 基于颜色模式,使用默认的浅色主题或在深色模式下使用 theme.tsx 文件中定义的 darkTheme 配置
 // app/_layout.tsx

 从“@/theme”导入主题,{ darkTheme };

 //...其余代码

    <ThemeProvider主题={colorSchema ===“深色”?黑暗主题:主题}>
      <堆栈>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> >
        <Stack.Screen name="未找到" />
      </堆栈>
      



<p>这是深色和浅色模式。</p>

<p><img src="https://img.php.cn/upload/article/000/000/000/173310152340178.jpg" alt="How to build type-enforced UI components in React Native using @shopify/restyle"></p>

<p><img src="https://img.php.cn/upload/article/000/000/000/173310152557660.jpg" alt="How to build type-enforced UI components in React Native using @shopify/restyle"></p>

<p>瞧,我们成功地使用 @shopify/restyle 包创建了类型强制的 UI 组件</p>

<p>谢谢你:)</p>


          

            
        
登录后复制

以上是如何使用 @shopify/restyle 在 React Native 中构建类型强制的 UI 组件的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板