Created
April 27, 2023 15:36
-
-
Save ali-sabry/9a9349bce3908911be2b41a27cc47b2b to your computer and use it in GitHub Desktop.
useOfflineFirst custom hook: this hook allows you to build offline-first apps which means apps that can work with or without an Internet connection, to provide a fast and reliable user experience and they are especially useful for scenarios where the network is unreliable or unavailable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//=== Custom Hook | |
import React, { useState, useEffect } from "react"; | |
function useOfflineFirst(url) { | |
const [isOnline, setIsOnline] = useState(true); | |
const [data, setData] = useState(null); | |
const updateNetworkStatus = () => { | |
if (typeof navigator !== "undefined" && typeof navigator.onLine === "boolean") setIsOnline(navigator.onLine); | |
}; | |
const openDatabase = () => { | |
return new Promise((resolve, reject) => { | |
if (typeof indexedDB !== "undefined") { | |
const request = indexedDB.open("offline-first", 1); | |
request.onsuccess = (e) => resolve(e.target.result); | |
request.onerror = (e) => reject(e.target.error); | |
request.onupgradeneeded = (event) => { | |
const db = event.target.result; | |
db.createObjectStore("data", { keyPath: "url" }); | |
}; | |
} else reject(new Error("IndexedDB is not supported")); | |
}); | |
}; | |
const saveData = (url, data) => { | |
openDatabase() | |
.then((db) => { | |
const tx = db.transaction("data", "readwrite"); | |
const store = tx.objectStore("data"); | |
store.put({ url, data }); | |
}).catch((error) => console.error(error)); | |
}; | |
const loadData = (url) => { | |
openDatabase() | |
.then((db) => { | |
const tx = db.transaction("data", "readonly"); | |
const store = tx.objectStore("data"); | |
const request = store.get(url); | |
request.onsuccess = (e) => { | |
if (e.target.result) setData(e.target.result.data); | |
}; | |
request.onerror = (e) => console.error(e.target.error); | |
}).catch((error) => console.error(error)); | |
}; | |
const fetchData = (url) => { | |
fetch(url) | |
.then((response) => { | |
if (response.ok) return response.json(); | |
else throw new Error(response.statusText); | |
}).then((data) => { | |
setData(data); | |
saveData(url, data); | |
}).catch((error) => console.error(error)); | |
}; | |
useEffect(() => { | |
updateNetworkStatus(); | |
window.addEventListener("online", updateNetworkStatus); | |
window.addEventListener("offline", updateNetworkStatus); | |
return () => { | |
window.removeEventListener("online", updateNetworkStatus); | |
window.removeEventListener("offline", updateNetworkStatus); | |
}; | |
}, []); // Run only once on mount and unmount | |
useEffect(() => { | |
if (url) { | |
if (isOnline) return fetchData(url) | |
else loadData(url); | |
} | |
}, [isOnline, url]); // Run whenever the network status or url changes | |
return { isOnline, data }; | |
} | |
//==== Usage | |
const App = ()=> { | |
const { isOnline, data } = useOfflineFirst("https://jsonplaceholder.typicode.com/users"); | |
return ( | |
<div className="App"> | |
<h1>Offline-First App</h1> | |
{isOnline ? ( // Render a message based on the network status | |
<p>You are online. Data is fetched from the server.</p> | |
) : ( | |
<p>You are offline. Data is loaded from IndexedDB.</p> | |
)} | |
{data ? ( // Render a list of users if data is available | |
<ul > | |
{data.map((user) => ( | |
<li key={user.id}>{user.name}</li> | |
))} | |
</ul> | |
) : ( | |
<p>Loading...</p> | |
)} | |
</div> | |
); | |
} | |
export default App; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment