alexZajac / react-native-skeleton-content

A customizable skeleton-like loading placeholder for react native projects using expo.
https://www.npmjs.com/package/react-native-skeleton-content
MIT License
614 stars 134 forks source link

Feature: Support nested child layout, extract skeleton layout into components to reuse. #49

Open hoangvu12 opened 3 years ago

hoangvu12 commented 3 years ago

Is your feature request related to a problem? Please describe. I want to reuse skeleton layout cause I don't want to repeat the loader in all of my project.

Describe the solution you'd like Support nested child layout, allow us to extract skeleton layout into components, then reuse it.

Describe alternatives you've considered No.

Additional context For example I have a global loader file, name `Loader.tsx. It contains

import React from "react";
import { ViewStyle } from "react-native";
import SkeletonContent from "react-native-skeleton-content";

type LoaderProps = {
  children: React.ReactNode;
  containerStyle?: ViewStyle;
};

export default function Loader(props: LoaderProps) {
  const { children, containerStyle } = props;

  return (
    <SkeletonContent
      containerStyle={containerStyle}
      boneColor="#121212"
      highlightColor="#333333"
      animationType="pulse"
      isLoading={true}
    >
      {children}
    </SkeletonContent>
  );
}

Then I'll have a layout name VideoCardLayout.tsx, it contains:

import React from "react";
import { StyleSheet, View } from "react-native";
import {
  CardPaddingRight,
  CardWidth,
  ImageHeight,
  ImageRatio,
  StudiosFontSize,
  TitleFontSize,
} from "../components/VideoCard";

const VideoCardLayout = () => (
  <View style={styles.container}>
    <View style={styles.thumbnail} />
    <View style={styles.title} />
    <View style={styles.studios} />
  </View>
);

export default VideoCardLayout;

const styles = StyleSheet.create({
  container: {
    width: CardWidth,
    marginRight: CardPaddingRight,
    marginBottom: 5,
  },
  thumbnail: {
    width: CardWidth,
    height: ImageHeight * ImageRatio,
    marginBottom: 5,
  },
  title: {
    height: TitleFontSize,
    width: CardWidth * 0.7,
    marginBottom: 5,
  },
  studios: {
    height: StudiosFontSize,
    width: CardWidth * 0.4,
  },
});

Then if I want to use the layout, I just do this:

export default function HomeScreen() {
  const { data, isLoading, isError } = useHomePage();

  if (isLoading) {
    return (
      <Loader containerStyle={{ flex: 1, padding: 10 }}>
        <VideoCardLoader />
      </Loader>
    );
  }

  return (
    <ScrollView style={{ flex: 1, padding: 10 }}>
      {data.map((category: CategoryProps) => (
        <Category {...category} key={category.title} />
      ))}
    </ScrollView>
  );
}

Also, if I want to use VideoCardLayout to another layout, let say CategoryLayout, I'll do

import React from "react";

import { moderateScale } from "../utils/scale";

import { Dimensions, StyleSheet, View } from "react-native";
import VideoCardLoader from "./VideoCardLoader";
import VideoCardLayout from "./VideoCardLoader";

const { width: windowWidth } = Dimensions.get("window");

const CategoryLayout = () => (
  <View style={styles.container}>
    <View style={styles.categoryContainer}>
      <View style={styles.title} />
      <View style={styles.time} />
    </View>
    <View style={styles.videoContainer}>
      <VideoCardLayout />
    </View>
  </View>
);

export default CategoryLayout;

const styles = StyleSheet.create({
  container: {
    marginVertical: 10,
  },
  categoryContainer: {
    marginBottom: 10,
  },
  videoContainer: {
    flexDirection: "row",
    alignItems: "center",
  },
  title: {
    height: moderateScale(20),
    width: windowWidth * 0.7,
  },
  time: {
    height: moderateScale(16),
    width: windowWidth * 0.4,
  },
});