Added local storage, script, and theme hooks.

This commit is contained in:
Bradley Shellnut 2021-09-27 20:14:16 -07:00
parent 5cab62fd7e
commit 43a73aae8a
12 changed files with 157 additions and 7 deletions

View file

@ -4,4 +4,6 @@
--blue: #4f8cdd; --blue: #4f8cdd;
--black: #3b2c41; --black: #3b2c41;
--green: #a3ffc1; --green: #a3ffc1;
--bg: #fff;
--text: #333;
} }

View file

@ -6,6 +6,9 @@ import Inc from './components/Inc';
import Mount from './components/Mount'; import Mount from './components/Mount';
import Hover from './components/Hover'; import Hover from './components/Hover';
import Cookie from "./components/Cookie"; import Cookie from "./components/Cookie";
import Script from './components/Script';
import Local from './components/Local';
import Theme from './components/Theme';
import { PageWrapper } from "./state"; import { PageWrapper } from "./state";
import Nav from "./components/Nav"; import Nav from "./components/Nav";
import Menu from "./Menu"; import Menu from "./Menu";
@ -25,10 +28,13 @@ function App() {
<Nav /> <Nav />
<Container> <Container>
<h2>Super Cool</h2> <h2>Super Cool</h2>
<Theme />
<Script />
<Toggle /> <Toggle />
<Inc /> <Inc />
<Mount /> <Mount />
<Cookie /> <Cookie />
<Local />
<Hover /> <Hover />
<CardGrid> <CardGrid>

View file

@ -21,6 +21,7 @@ export const Container = styled.div`
max-width: 600px; max-width: 600px;
margin: 0 auto; margin: 0 auto;
padding-bottom: 60px; padding-bottom: 60px;
color: var(--text);
`; `;
export const CardGrid = styled.div` export const CardGrid = styled.div`

View file

@ -1,4 +1,3 @@
import React from 'react';
import { Card } from '../Elements'; import { Card } from '../Elements';
import black from "../black.png" import black from "../black.png"
import { useHover, useWindowWidth, useMeasure } from '../hooks'; import { useHover, useWindowWidth, useMeasure } from '../hooks';
@ -6,15 +5,15 @@ import { useHover, useWindowWidth, useMeasure } from '../hooks';
export const Hover = () => { export const Hover = () => {
const [ isHovered, bind ] = useHover(); const [ isHovered, bind ] = useHover();
const width = useWindowWidth(); const width = useWindowWidth();
const [{ref}, bounds] = useMeasure(); // const [{ref}, bounds] = useMeasure();
console.log('bounds', bounds); // console.log('bounds', bounds);
if (width < 500) return null; if (width < 500) return null;
return ( return (
<div> <div>
<Card <Card
ref={ref} // ref={ref}
{...bind} {...bind}
style={{ background: isHovered ? "var(--purp)" : "var(--black)" }} style={{ background: isHovered ? "var(--purp)" : "var(--black)" }}
> >

14
src/components/Local.js Normal file
View file

@ -0,0 +1,14 @@
import { useLocalStorage } from "../hooks";
const Local = () => {
const [ value, setValue ] = useLocalStorage("tester", "I am initial");
return (
<div>
<h1>{value || ''}</h1>
<input type="text" value={value} onChange={(e) => setValue(e.target.value)} />
</div>
)
}
export default Local;

15
src/components/Script.js Normal file
View file

@ -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 (
<div>
<h3>Script</h3>
</div>
)
}
export default Script;

16
src/components/Theme.js Normal file
View file

@ -0,0 +1,16 @@
import { useTheme } from "../hooks";
const Theme = () => {
const [theme, setTheme] = useTheme();
return (
<div>
<select name="" id="" defaultValue={theme}
onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
)
}
export default Theme;

View file

@ -1,8 +1,11 @@
export * from './useCookie'; export * from './useCookie';
export * from './useHover'; export * from './useHover';
export * from './useInc'; export * from './useInc';
export * from './useLocalStorage';
export * from './useMeasure'; export * from './useMeasure';
export * from './useMount'; export * from './useMount';
export * from './useScrollFreeze'; export * from './useScrollFreeze';
export * from './useScript';
export * from './useTheme';
export * from './useToggle'; export * from './useToggle';
export * from './useWindowWidth'; export * from './useWindowWidth';

View file

@ -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 ];
};

59
src/hooks/useScript.js Normal file
View file

@ -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 ];
}

14
src/hooks/useTheme.js Normal file
View file

@ -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];
}

View file

@ -1,13 +1,22 @@
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif; sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace; monospace;
} }
body {
background: var(--bg);
}
body.dark {
--bg: #333;
--text: #fff;
}