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

Extract Skeleton Items to another component then reuse it? #56

Open
hoangvu12 opened this issue Jul 11, 2021 · 3 comments
Open

Extract Skeleton Items to another component then reuse it? #56

hoangvu12 opened this issue Jul 11, 2021 · 3 comments

Comments

@hoangvu12
Copy link

hoangvu12 commented Jul 11, 2021

I have a component called Loader.tsx

import React from "react";
import SkeletonPlaceholder from "react-native-skeleton-placeholder";

type LoaderProps = {
  children: JSX.Element | JSX.Element[];
};

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

  return (
    <SkeletonPlaceholder highlightColor="#333333" backgroundColor="#121212">
      {children}
    </SkeletonPlaceholder>
  );
}

Now I create a component contains Skeleton items

const VideoCardLoader = () => {
  return (
      <SkeletonPlaceholder.Item
        width={CardWidth}
        marginRight={CardPaddingRight}
        marginBottom={CardPaddingBottom}
      >
        <SkeletonPlaceholder.Item
          width={CardWidth}
          height={ImageHeight * ImageRatio}
          marginBottom={5}
        />
        <SkeletonPlaceholder.Item
          height={TitleFontSize}
          width={CardWidth * 0.7}
          marginBottom={5}
        />
        <SkeletonPlaceholder.Item
          height={StudiosFontSize}
          width={CardWidth * 0.4}
        />
      </SkeletonPlaceholder.Item>
  );
};

Now when I try to use it in HomeScreen, I would write

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

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Loader>
          <VideoCardLoader />
        </Loader>
      </View>
    );
  }

 return <Text>Loaded....</Text>
}

Sadly this way didn't works as I expected. This should works but somehow it didn't...

Edit: After minutes of trying, I found out this way works as I expected. But the syntax looks not that good.

import React from 'react';
import { SafeAreaView, Dimensions } from 'react-native';
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';

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

const Loader = ({ loader: Loader }) => (
  <SkeletonPlaceholder>{Loader}</SkeletonPlaceholder>
);

const AvatarLoader = (
  <SkeletonPlaceholder.Item flexDirection="row" alignItems="center">
    <SkeletonPlaceholder.Item width={60} height={60} borderRadius={50} />
    <SkeletonPlaceholder.Item marginLeft={20}>
      <SkeletonPlaceholder.Item width={120} height={20} borderRadius={4} />
      <SkeletonPlaceholder.Item
        marginTop={6}
        width={80}
        height={20}
        borderRadius={4}
      />
    </SkeletonPlaceholder.Item>
  </SkeletonPlaceholder.Item>
);

const PostLoader = (
  <>
    {AvatarLoader}
    <SkeletonPlaceholder.Item width={windowWidth} height={300} marginTop={10} />
  </>
);

const App = () => {
  return (
    <SafeAreaView>
      <Loader loader={PostLoader} />
    </SafeAreaView>
  );
};

export default App;
@chramos
Copy link
Owner

chramos commented Jul 16, 2021

hmmm, good catch
I'll try to do something to make it work

@hoangvu12
Copy link
Author

hmmm, good catch
I'll try to do something to make it work

After looking at your source code, I figured out why it didn't work. So I tried this approach, take a look at it to see if it helps.

const getChildren = React.useCallback(
  (element: JSX.Element | JSX.Element[]) => {
    return React.Children.map(element, (child: JSX.Element, index: number) => {

      // If the component neither SkeletonItem nor View.
      // Then assume it is a custom component that using SkeletonPlaceholder
      // just return it directly.

      if (
        child.type.displayName !== "SkeletonPlaceholderItem" ||
        child.type.displayName !== "View"
      ) {
        return child;
      }

      let style: ViewStyle;
      if (child.type.displayName === "SkeletonPlaceholderItem") {
        const { children, ...styles } = child.props;
        style = styles;
      } else {
        style = child.props.style;
      }
      if (child.props.children) {
        return (
          <View key={index} style={style}>
            {getChildren(child.props.children)}
          </View>
        );
      } else {
        return (
          <View key={index} style={styles.childContainer}>
            <View style={[style, viewStyle]} />
          </View>
        );
      }
    });
  },
  [viewStyle]
);

@Kakaranara
Copy link

is this issue solved? im trying to make my component reusable

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

3 participants