├── 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 | --------------------------------------------------------------------------------