diff --git a/src/components/Local.js b/src/components/Local.js
new file mode 100644
index 0000000..93689d7
--- /dev/null
+++ b/src/components/Local.js
@@ -0,0 +1,14 @@
+import { useLocalStorage } from "../hooks";
+
+const Local = () => {
+ const [ value, setValue ] = useLocalStorage("tester", "I am initial");
+
+ return (
+
+
{value || ''}
+ setValue(e.target.value)} />
+
+ )
+}
+
+export default Local;
\ No newline at end of file
diff --git a/src/components/Script.js b/src/components/Script.js
new file mode 100644
index 0000000..1f12a7b
--- /dev/null
+++ b/src/components/Script.js
@@ -0,0 +1,15 @@
+import { useScript } from "../hooks"
+
+const Script = () => {
+ const [ isLoaded, isError ] = useScript('https://www.google.com/recaptcha/api.js');
+ console.log('isLoaded, isError', isLoaded, isError);
+ if (!isLoaded) return null;
+
+ return (
+
+
Script
+
+ )
+}
+
+export default Script;
\ No newline at end of file
diff --git a/src/components/Theme.js b/src/components/Theme.js
new file mode 100644
index 0000000..56d4e3b
--- /dev/null
+++ b/src/components/Theme.js
@@ -0,0 +1,16 @@
+import { useTheme } from "../hooks";
+
+const Theme = () => {
+ const [theme, setTheme] = useTheme();
+ return (
+
+
+
+ )
+}
+
+export default Theme;
\ No newline at end of file
diff --git a/src/hooks/index.js b/src/hooks/index.js
index 0966ee1..8562d97 100644
--- a/src/hooks/index.js
+++ b/src/hooks/index.js
@@ -1,8 +1,11 @@
export * from './useCookie';
export * from './useHover';
export * from './useInc';
+export * from './useLocalStorage';
export * from './useMeasure';
export * from './useMount';
export * from './useScrollFreeze';
+export * from './useScript';
+export * from './useTheme';
export * from './useToggle';
export * from './useWindowWidth';
diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js
new file mode 100644
index 0000000..4255d35
--- /dev/null
+++ b/src/hooks/useLocalStorage.js
@@ -0,0 +1,12 @@
+import { useState, useEffect } from 'react';
+
+export const useLocalStorage = (key, initial) => {
+ const item = window.localStorage.getItem(key);
+ const [ value, setValue ] = useState(item || initial);
+
+ useEffect(() => {
+ window.localStorage.setItem(key, value);
+ }, [value, key, initial]);
+
+ return [ value, setValue ];
+};
\ No newline at end of file
diff --git a/src/hooks/useScript.js b/src/hooks/useScript.js
new file mode 100644
index 0000000..81471fd
--- /dev/null
+++ b/src/hooks/useScript.js
@@ -0,0 +1,59 @@
+import { useState, useEffect } from 'react';
+
+let cached = [];
+
+export const useScript = (src) => {
+ const [ status, setStatus ] = useState({
+ loaded: false,
+ error: false,
+ });
+
+ useEffect(() => {
+ if (cached.includes(src)) {
+ setStatus({
+ loaded: true,
+ error: false,
+ });
+ } else {
+ cached.push(src);
+
+ const script = document.createElement('script');
+ script.src = src;
+ script.async = true;
+
+ const onLoad = () => {
+ setStatus({
+ loaded: true,
+ error: false,
+ });
+ }
+
+ const onError = () => {
+ const i = cached.indexOf(src);
+ if (i >= 0) {
+ cached.splice(i, 1);
+ }
+ // cached = cached.filter(item => item !== src);
+
+ script.remove();
+
+ setStatus({
+ loaded: true,
+ error: true,
+ });
+ }
+
+ script.addEventListener('load', onLoad);
+ script.addEventListener('error', onError);
+
+ document.body.appendChild(script);
+
+ return () => {
+ script.removeEventListener('load', onLoad);
+ script.removeEventListener('error', onError);
+ }
+ }
+ }, [src]);
+
+ return [ status.loaded, status.error ];
+}
\ No newline at end of file
diff --git a/src/hooks/useTheme.js b/src/hooks/useTheme.js
new file mode 100644
index 0000000..2e5c1ee
--- /dev/null
+++ b/src/hooks/useTheme.js
@@ -0,0 +1,14 @@
+import { useEffect } from 'react';
+import { useLocalStorage } from './useLocalStorage';
+
+export const useTheme = () => {
+ const [theme, setTheme] = useLocalStorage('theme', 'light');
+
+ useEffect(() => {
+ // Removing all class names on body before setting, could keep and only remove what we want
+ document.body.className = "";
+ document.body.classList.add(theme);
+ }, [theme]);
+
+ return [theme, setTheme];
+}
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ec2585e..d51119f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +1,22 @@
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
+ "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
+
+body {
+ background: var(--bg);
+}
+
+body.dark {
+ --bg: #333;
+ --text: #fff;
+}