From ccf02576c640dd69500a81720dc2f17f05fb8a2c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 13 Nov 2020 18:51:45 +0900 Subject: implement some global state --- src/App.js | 115 +++++++++++++++++++++++++++++++++++++++-------------------- src/utils.js | 36 ++++++++++++++++++- 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/src/App.js b/src/App.js index f2794dc..a10c27b 100644 --- a/src/App.js +++ b/src/App.js @@ -46,10 +46,11 @@ import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore'; import Grow from '@material-ui/core/Grow'; import Link from '@material-ui/core/Link'; import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Snackbar from '@material-ui/core/Snackbar'; import LazyLoad from 'react-lazyload'; import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel'; import useTimeout from 'use-timeout' -import { useWindowDimensions } from './utils.js'; +import { GlobalState, useGlobalState, useWindowDimensions } from './utils.js'; import 'pure-react-carousel/dist/react-carousel.es.css'; import 'fontsource-roboto'; @@ -71,6 +72,14 @@ const StyledBadge = withStyles((theme) => ({ }, }))(Badge); + +const global = { + store: { data: new GlobalState({data:[], loaded:false}), location: '/store' }, + library: { data: new GlobalState({data:[], loaded:false}), location: '/library' }, + account: { data: new GlobalState({data:[], loaded:false}), location: '/account' }, + status: new GlobalState(null), +} + function StoreItemDetails(props) { const { t } = useTranslation(); @@ -373,25 +382,31 @@ function StoreList(props) { } function StoreView(props) { - const [state, setState] = useState({data: [], loaded: false}); + const [state, setState] = useGlobalState(global.store.data); + const [, setStatus] = useGlobalState(global.status); useTimeout(() => { async function fetchData() { function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } - var result = await fetch('https://cors-anywhere.herokuapp.com/https://www.eisys-bcs.jp/data.json?key[]=dlsite-doujin_home_center2').then(result => result.json()); - result = result.data.['dlsite-doujin_home_center2'].banners.map(item => { return { - id: 'no-id', - author: he.decode(item.title), - title: he.decode(item.title), - shortdesc: 'foobar', - desc: 'foobar', - img: 'https:' + item.ssl_path, - cat: 'Misc', - price: getRandomInt(3000) + '円', - rating: getRandomInt(5), - }}) - setState({data: result, loaded: true}); + try { + var result = await fetch('https://cors-anywhere.herokuapp.com/https://www.eisys-bcs.jp/data.json?key[]=dlsite-doujin_home_center2').then(result => result.json()); + result = result.data.['dlsite-doujin_home_center2'].banners.map(item => { return { + id: 'no-id', + author: he.decode(item.title), + title: he.decode(item.title), + shortdesc: 'foobar', + desc: 'foobar', + img: 'https:' + item.ssl_path, + cat: 'Misc', + price: getRandomInt(3000) + '円', + rating: getRandomInt(5), + }}) + } catch (err) { + result = []; + setStatus(err); + } + setState({data:result, loaded:true}); } fetchData(); }, 600); @@ -406,25 +421,31 @@ function StoreView(props) { } function LibraryView(props) { - const [state, setState] = useState({data: [], loaded: false}); + const [state, setState] = useGlobalState(global.library.data); + const [, setStatus] = useGlobalState(global.status); useTimeout(() => { async function fetchData() { function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } - var result = await fetch('https://cors-anywhere.herokuapp.com/https://www.eisys-bcs.jp/data.json?key[]=dlsite-doujin_home_center2').then(result => result.json()); - result = result.data.['dlsite-doujin_home_center2'].banners.map(item => { return { - id: 'no-id', - author: he.decode(item.title), - title: he.decode(item.title), - shortdesc: 'foobar', - desc: 'foobar', - img: 'https:' + item.ssl_path, - cat: 'Misc', - price: getRandomInt(3000) + '円', - rating: getRandomInt(5), - }}) - setState({data: result, loaded: true}); + try { + var result = await fetch('https://cors-anywhere.herokuapp.com/https://www.eisys-bcs.jp/data.json?key[]=dlsite-doujin_home_center2').then(result => result.json()); + result = result.data.['dlsite-doujin_home_center2'].banners.map(item => { return { + id: 'no-id', + author: he.decode(item.title), + title: he.decode(item.title), + shortdesc: 'foobar', + desc: 'foobar', + img: 'https:' + item.ssl_path, + cat: 'Misc', + price: getRandomInt(3000) + '円', + rating: getRandomInt(5), + }}) + } catch (err) { + result = []; + setStatus(err); + } + setState({data:result, loaded:true}); } fetchData(); }, 600); @@ -527,6 +548,13 @@ function ItemSearch(props) { } function BottomBar(props) { + var location = useLocation(); + const components = location.pathname.split('/'); + + switch (components[1]) { + case 'store': case 'library': case 'account': global[components[1]].location = location; + } + const { t } = useTranslation(); const classes = makeStyles((theme) => ({ stickyBottom: { @@ -536,25 +564,27 @@ function BottomBar(props) { }, }))(); return ( - - } component={RouteLink} to='/store'/> - } component={RouteLink} to='/library'/> - } component={RouteLink} to='/account'/> + + } component={RouteLink} to={global.store.location}/> + } component={RouteLink} to={global.library.location}/> + } component={RouteLink} to={global.account.location}/> ); } function TopBar(props) { + var location = useLocation(); + const components = location.pathname.split('/'); const { t } = useTranslation(); const classes = makeStyles((theme) => ({ title: { flexGrow: 1 }, backButton: { marginRight: theme.spacing(1) }, }))(); - const search = (props.components[1] !== 'account'); + const search = (components[1] !== 'account'); return ( - 2} unmountOnExit> + 2} unmountOnExit> window.history.back()}> @@ -577,8 +607,6 @@ function TopBar(props) { } function Routing() { - var location = useLocation(); - const components = location.pathname.split('/'); const classes = makeStyles((theme) => ({ content: { paddingTop: 105, @@ -586,9 +614,10 @@ function Routing() { minHeight: '100vh', }, }))(); + const [status, setStatus] = useGlobalState(global.status); return ( <> - + @@ -597,8 +626,16 @@ function Routing() { + setStatus(null)} + autoHideDuration={6000} + anchorOrigin={{vertical: 'top', horizontal: 'center'}} + open={status !== null} + message={status ? status : ''} + key={status} + /> - + ); } diff --git a/src/utils.js b/src/utils.js index 3c072fa..c16c1b7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -11,7 +11,7 @@ export function useWindowDimensions() { function handleResize() { setWindowDimensions(getWindowDimensions()); } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); - }, []); + }); return windowDimensions; } @@ -23,3 +23,37 @@ export function useDebounce(value, delay) { }, [value, delay]); return debouncedValue; } + +export function GlobalState(initialValue) { + this.value = initialValue; + this.subscribers = []; + this.getValue = () => this.value; + + this.setValue = (newState) => { + if (this.getValue() === newState) return; + this.value = newState; + this.subscribers.forEach(subscriber => subscriber(this.value)); + } + + this.subscribe = (itemToSubscribe) => { + if (this.subscribers.indexOf(itemToSubscribe) > -1) return + this.subscribers.push(itemToSubscribe); + } + + this.unsubscribe = (itemToUnsubscribe) => { + this.subscribers = this.subscribers.filter(subscriber => subscriber !== itemToUnsubscribe); + } +} + +export function useGlobalState(globalState) { + const [, setState] = useState(); + const state = globalState.getValue(); + function reRender(newState) { setState({}); } + useEffect(() => { + globalState.subscribe(reRender); + return () => globalState.unsubscribe(reRender); + }) + function setGlobalState(newState) { globalState.setValue(newState); } + return [state, setGlobalState]; +} + -- cgit v1.2.3