├── assets
├── icon.png
├── splash.png
└── placeholderLoading.gif
├── babel.config.js
├── .expo-shared
└── assets.json
├── .gitignore
├── .eslintrc
├── app.json
├── README.md
├── components
├── BookCardPlaceholderComponent.js
└── BookCardComponent.js
├── package.json
└── App.js
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vikrantnegi/react-native-placeholder-loading/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vikrantnegi/react-native-placeholder-loading/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/assets/placeholderLoading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vikrantnegi/react-native-placeholder-loading/HEAD/assets/placeholderLoading.gif
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
3 | "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 | web-report/
12 |
13 | # macOS
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["wesbos"],
3 | "rules": {
4 | "global-require": 0,
5 | "no-use-before-define": 0,
6 | "no-console": 2,
7 | "prettier/prettier": ["error"],
8 | "react/prop-types": 0,
9 | "react/destructuring-assignment": [
10 | true,
11 | "always",
12 | {
13 | "ignoreClassFields": true
14 | }
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Placeholder Loading",
4 | "slug": "placeholderLoading",
5 | "privacy": "public",
6 | "sdkVersion": "36.0.0",
7 | "platforms": [
8 | "ios",
9 | "android",
10 | "web"
11 | ],
12 | "version": "1.0.0",
13 | "orientation": "portrait",
14 | "icon": "./assets/icon.png",
15 | "splash": {
16 | "image": "./assets/splash.png",
17 | "resizeMode": "contain",
18 | "backgroundColor": "#ffffff"
19 | },
20 | "updates": {
21 | "fallbackToCacheTimeout": 0
22 | },
23 | "assetBundlePatterns": [
24 | "**/*"
25 | ],
26 | "ios": {
27 | "supportsTablet": true
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Facebook Like Placeholder Loading in React Native
2 |
3 | > Using Placeholder Loaders in React Native apps using rn-placeholder
4 |
5 |
6 |
7 | ## Clone the repo
8 |
9 | ```bash
10 | $ git clone https://github.com/vikrantnegi/react-native-placeholder-loading
11 | ```
12 |
13 | ## Install the dependencies
14 |
15 | ```bash
16 | $ yarn
17 | ```
18 |
19 | ## Run the app
20 |
21 | ```bash
22 | $ yarn start
23 | ```
24 |
25 | Press i to run the iOS Simulator. This will automatically run the iOS Simulator even if it's not opened.
26 |
27 | Press a to run the Android Emulator. Note that the emulator must be installed and started already before typing a otherwise it will throw an error in the terminal.
28 |
29 | You can also run the app in your device by installing expo app from the app store and then scanning the QR code.
30 |
31 | ## License
32 |
33 | MIT © [Vikrant Negi](https://github.com/vikrantnegi)
34 |
--------------------------------------------------------------------------------
/components/BookCardPlaceholderComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Placeholder,
4 | PlaceholderMedia,
5 | PlaceholderLine,
6 | ShineOverlay,
7 | } from 'rn-placeholder';
8 | import {
9 | responsiveHeight,
10 | responsiveWidth,
11 | } from 'react-native-responsive-dimensions';
12 |
13 | const BookCardPlaceholderComponent = () => (
14 | (
22 |
31 | )}
32 | >
33 |
34 |
35 |
36 |
37 | );
38 |
39 | export default BookCardPlaceholderComponent;
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "expo": "~36.0.0",
12 | "react": "~16.9.0",
13 | "react-dom": "~16.9.0",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
15 | "react-native-responsive-dimensions": "^3.0.0",
16 | "react-native-web": "~0.11.7",
17 | "rn-placeholder": "^3.0.0"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.0.0",
21 | "babel-eslint": "^9.0.0",
22 | "babel-preset-expo": "~8.0.0",
23 | "eslint": "^5.14.1",
24 | "eslint-config-airbnb": "^17.1.0",
25 | "eslint-config-prettier": "^4.1.0",
26 | "eslint-config-wesbos": "0.0.19",
27 | "eslint-plugin-html": "^5.0.3",
28 | "eslint-plugin-import": "^2.16.0",
29 | "eslint-plugin-jsx-a11y": "^6.2.1",
30 | "eslint-plugin-prettier": "^3.0.1",
31 | "eslint-plugin-react": "^7.12.4",
32 | "eslint-plugin-react-hooks": "^1.3.0",
33 | "prettier": "^1.16.4"
34 | },
35 | "private": true
36 | }
37 |
--------------------------------------------------------------------------------
/components/BookCardComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text, StyleSheet, View, TouchableOpacity, Image } from 'react-native';
3 | import {
4 | responsiveHeight,
5 | responsiveWidth,
6 | } from 'react-native-responsive-dimensions';
7 |
8 | // https://stenbeck.io/styling-shadows-in-react-native-ios-and-android/
9 | function elevationShadowStyle(elevation) {
10 | return {
11 | elevation,
12 | shadowColor: 'black',
13 | shadowOffset: { width: 0, height: 0.5 * elevation },
14 | shadowOpacity: 0.3,
15 | shadowRadius: 0.8 * elevation,
16 | };
17 | }
18 |
19 | const BookCardComponent = props => {
20 | const { thumbnail, title, authors, onPress } = props;
21 |
22 | return (
23 |
24 |
31 |
39 |
49 |
50 |
51 |
57 | {title}
58 |
63 | by {authors}
64 |
65 |
66 |
67 |
68 | );
69 | };
70 |
71 | const styles = StyleSheet.create({
72 | shadow: {
73 | borderRadius: 3,
74 | marginVertical: 5,
75 | marginHorizontal: 12,
76 | ...elevationShadowStyle(1),
77 | backgroundColor: 'white',
78 | },
79 | });
80 |
81 | export default BookCardComponent;
82 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { StyleSheet, SafeAreaView, FlatList } from 'react-native';
3 |
4 | import BookCardComponent from './components/BookCardComponent';
5 | import BookCardPlaceholder from './components/BookCardPlaceholderComponent';
6 |
7 | export default function App() {
8 | const [books, setBooks] = useState([...new Array(10).fill({})]);
9 | const [isDataFetched, setDataFetched] = useState(false);
10 |
11 | useEffect(() => {
12 | fetch(
13 | 'https://www.googleapis.com/books/v1/volumes/?maxResults=30&q=danbrown'
14 | )
15 | .then(response => response.json())
16 | .then(responseJson => {
17 | const { items } = responseJson;
18 |
19 | const booksList = items.map(book => {
20 | const {
21 | volumeInfo: { title, authors, imageLinks },
22 | id: bookId,
23 | } = book;
24 |
25 | return {
26 | bookId,
27 | thumbnail: imageLinks
28 | ? imageLinks.thumbnail
29 | : 'https://i.ibb.co/YLC0nQQ/not-found.png',
30 | title,
31 | authors: authors ? authors.toString().replace(/,/g, ', ') : '-',
32 | };
33 | });
34 |
35 | setBooks(booksList);
36 | setDataFetched(true);
37 | })
38 | .catch(error => {
39 | console.error(error);
40 | });
41 | }, [books]);
42 |
43 | const renderBookComponent = ({ item }) => {
44 | const { thumbnail, title, authors, bookId } = item;
45 |
46 | return (
47 |
53 | );
54 | };
55 |
56 | const renderX = () => (
57 | item.bookId}
61 | />
62 | );
63 |
64 | const renderPlaceholders = () =>
65 | books.map((e, i) => );
66 |
67 | return (
68 |
69 | {isDataFetched ? renderX() : renderPlaceholders()}
70 |
71 | );
72 | }
73 |
74 | const styles = StyleSheet.create({
75 | container: {
76 | flex: 1,
77 | backgroundColor: '#fafafa',
78 | paddingTop: 35,
79 | },
80 | });
81 |
--------------------------------------------------------------------------------