Skip to content

Commit

Permalink
fix: better shared state hook
Browse files Browse the repository at this point in the history
  • Loading branch information
broofa committed Apr 18, 2021
1 parent df58b0d commit e5fe7c5
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 77 deletions.
10 changes: 0 additions & 10 deletions docs/index.80c6c8bb.js

This file was deleted.

1 change: 0 additions & 1 deletion docs/index.80c6c8bb.js.map

This file was deleted.

10 changes: 10 additions & 0 deletions docs/index.964b2bfe.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/index.964b2bfe.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
www.googletagmanager.com
www.gravatar.com
blob:
"><meta name="description" content="Graph / visualize of npm dependencies"><meta name="keywords" content="visualize, visualization, graph, npm, npm modules, npm graph, npm licenses"><link rel="icon" type="image/png" href="/favicon.d8e6c380.png"><link href="https://fonts.googleapis.com/css?family=Roboto+Condensed|Material+Icons" rel="stylesheet"><link href="/index.c28881e8.css" rel="stylesheet"><title>NPMGraph - Visualize NPM Module Dependencies</title></head><body> <div id="app" class="theme-lite"></div> </body></html><script src="https://d2wy8f7a9ursnm.cloudfront.net/v5/bugsnag.min.js"></script><script src="https://www.googletagmanager.com/gtag/js?id=UA-6434483-2"></script><script>!function(){function a(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],window.GA_TRACKING_ID="UA-6434483-2",a("js",new Date),a("config",window.GA_TRACKING_ID)}();</script><script type="module" src="/index.80c6c8bb.js"></script><script>window.addEventListener("DOMContentLoaded",(function(){if(window.indexLoaded)return;const e=document.body;e.className="fail",e.innerHTML='<div style="\n position: absolute;\n background-color: var(--bg0);\n left: 0;\n top: 0;\n bottom: 0;\n right: 35%;\n border-radius: 2rem 2rem;\n color: var(--text);\n padding: 1em;\n margin: 1em;">\n <h1>Well this is awkward...</h1>\n <p>Something\'s not working right. Please check the following:</p>\n\n <ul>\n <li>Make sure your browser is up-to-date (Chrome, Firefox, Safari, or Edge should work.)</li>\n <li style="margin-top: 1em">Make sure your ad-blocker is disabled for this site. <span style="color: #00000088; font-style: italic">(We don\'t serve ads, but ad blockers may interfere with our bug reporting and analytics scripts.)</span></li>\n </ul>\n\n <p>If the problem persists, don\'t sweat it. It\'s been reported and we should have it fixed soon. It it lasts more than a few days, go ahead and <a href="https://www.github.com/npmgraph/npmgraph">create an issue</a>).</p>\n\n \x3c!-- text bubble arrow --\x3e\n <div style="position: absolute;\n width: 0;\n height: 0;\n border-left: 1.5rem solid var(--bg0);\n border-top: 1rem solid transparent;\n border-bottom: 1rem solid transparent;\n bottom: 50%;\n right: -1.5rem;"></div>\n '}));</script>
"><meta name="description" content="Graph / visualize of npm dependencies"><meta name="keywords" content="visualize, visualization, graph, npm, npm modules, npm graph, npm licenses"><link rel="icon" type="image/png" href="/favicon.d8e6c380.png"><link href="https://fonts.googleapis.com/css?family=Roboto+Condensed|Material+Icons" rel="stylesheet"><link href="/index.c28881e8.css" rel="stylesheet"><title>NPMGraph - Visualize NPM Module Dependencies</title></head><body> <div id="app" class="theme-lite"></div> </body></html><script src="https://d2wy8f7a9ursnm.cloudfront.net/v5/bugsnag.min.js"></script><script src="https://www.googletagmanager.com/gtag/js?id=UA-6434483-2"></script><script>!function(){function a(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],window.GA_TRACKING_ID="UA-6434483-2",a("js",new Date),a("config",window.GA_TRACKING_ID)}();</script><script type="module" src="/index.964b2bfe.js"></script><script>window.addEventListener("DOMContentLoaded",(function(){if(window.indexLoaded)return;const e=document.body;e.className="fail",e.innerHTML='<div style="\n position: absolute;\n background-color: var(--bg0);\n left: 0;\n top: 0;\n bottom: 0;\n right: 35%;\n border-radius: 2rem 2rem;\n color: var(--text);\n padding: 1em;\n margin: 1em;">\n <h1>Well this is awkward...</h1>\n <p>Something\'s not working right. Please check the following:</p>\n\n <ul>\n <li>Make sure your browser is up-to-date (Chrome, Firefox, Safari, or Edge should work.)</li>\n <li style="margin-top: 1em">Make sure your ad-blocker is disabled for this site. <span style="color: #00000088; font-style: italic">(We don\'t serve ads, but ad blockers may interfere with our bug reporting and analytics scripts.)</span></li>\n </ul>\n\n <p>If the problem persists, don\'t sweat it. It\'s been reported and we should have it fixed soon. It it lasts more than a few days, go ahead and <a href="https://www.github.com/npmgraph/npmgraph">create an issue</a>).</p>\n\n \x3c!-- text bubble arrow --\x3e\n <div style="position: absolute;\n width: 0;\n height: 0;\n border-left: 1.5rem solid var(--bg0);\n border-top: 1rem solid transparent;\n border-bottom: 1rem solid transparent;\n bottom: 50%;\n right: -1.5rem;"></div>\n '}));</script>
35 changes: 17 additions & 18 deletions js/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ import Graph from './Graph';
import { LoadActivity } from './util';
import Store from './Store';
import { Loader } from './Components';
import createSharedState from './createSharedState';
import sharedStateHook from './sharedStateHook';

export const usePane = sharedStateHook('info', 'pane');
export const useInspectorOpen = sharedStateHook(true, 'inspectorOpen');
export const useQuery = sharedStateHook(queryFromLocation(), 'query');
export const useModule = sharedStateHook([], 'module');
export const useGraph = sharedStateHook([], 'graph');
export const useColorize = sharedStateHook(false, 'colorize');
export const useDepIncludes = sharedStateHook(['dependencies'], 'depIncludes');
export const useExcludes = sharedStateHook([], 'excludes');

function Splitter({ onClick, isOpen }) {
return <div id='splitter' className='theme-dark bright-hover' onClick={onClick}>{isOpen ? '\u{25b6}' : '\u{25c0}'}</div>;
}

export const sharedState = createSharedState({
pane: 'info',
inspectorOpen: true,
query: queryFromLocation(),
module: [],
graph: [],
colorize: false,
depIncludes: ['dependencies']
});

// Parse url query param from browser location, "q"
function queryFromLocation() {
const q = /q=([^&]+)/.test(location.search) && RegExp.$1;
Expand All @@ -36,7 +35,7 @@ export function useActivity() {

export default function App() {
const activity = useActivity();
const [, setQuery] = sharedState.use('query');
const [, setQuery] = useQuery();

useEffect(() => {
function handlePopState() {
Expand All @@ -50,12 +49,12 @@ export default function App() {
};
}, []);

const [inspectorOpen, setInspectorOpen] = sharedState.use('inspectorOpen');
const [inspectorOpen, setInspectorOpen] = useInspectorOpen();

return <>
{activity.total > 0 ? <Loader activity={activity} /> : null}
<Graph />
<Splitter isOpen={inspectorOpen} onClick={() => setInspectorOpen(!inspectorOpen)} />
<Inspector className={inspectorOpen ? 'open' : ''} />
</>;
{activity.total > 0 ? <Loader activity={activity} /> : null}
<Graph />
<Splitter isOpen={inspectorOpen} onClick={() => setInspectorOpen(!inspectorOpen)} />
<Inspector className={inspectorOpen ? 'open' : ''} />
</>;
}
16 changes: 8 additions & 8 deletions js/Graph.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import * as d3 from 'd3';

import { sharedState, store, activity } from './App';
import { store, activity, useQuery, useDepIncludes, usePane, useInspectorOpen, useModule, useGraph, useColorize } from './App';
import { $, tagElement, report, fetchJSON } from './util';
import { graphviz } from '@hpcc-js/wasm';
import wasmUrl from 'url:@hpcc-js/wasm/dist/graphvizlib.wasm';
Expand Down Expand Up @@ -268,13 +268,13 @@ export function GraphControls() {
}

export default function Graph(props) {
const [query] = sharedState.use('query');
const [depIncludes] = sharedState.use('depIncludes');
const [, setPane] = sharedState.use('pane');
const [, setInspectorOpen] = sharedState.use('inspectorOpen');
const [, setModule] = sharedState.use('module');
const [, setGraph] = sharedState.use('graph');
const [colorize] = sharedState.use('colorize');
const [query] = useQuery();
const [depIncludes] = useDepIncludes();
const [, setPane] = usePane();
const [, setInspectorOpen] = useInspectorOpen();
const [, setModule] = useModule();
const [, setGraph] = useGraph();
const [colorize] = useColorize();

const [svg, setSvg] = useState();

Expand Down
6 changes: 3 additions & 3 deletions js/GraphPane.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as d3 from 'd3';
import React, { useEffect, useRef } from 'react';
import { sharedState } from './App';
import { useColorize, useDepIncludes } from './App';
import { Pane, Section, Tags, Tag } from './Inspector';
import { simplur } from './util';
import { Toggle } from './Components';
import { hslFor } from './Graph';

function DepInclude({ type, ...props }) {
const [depIncludes, setDepIncludes] = sharedState.use('depIncludes');
const [depIncludes, setDepIncludes] = useDepIncludes();

let arrow = null;
switch (type) {
Expand Down Expand Up @@ -91,7 +91,7 @@ function PieGraph({ entries, ...props }) {
export default function GraphPane({ graph }) {
const compareEntryKey = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
const compareEntryValue = ([, a], [, b]) => a < b ? -1 : a > b ? 1 : 0;
const [colorize, setColorize] = sharedState.use('colorize');
const [colorize, setColorize] = useColorize();

const occurances = {};
const maintainers = {};
Expand Down
4 changes: 2 additions & 2 deletions js/InfoPane.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Pane, QueryLink } from './Inspector';
import { store, sharedState } from './App';
import { store, useQuery } from './App';

// Get names of uploaded modules in session storage
function getFileEntries() {
Expand All @@ -9,7 +9,7 @@ function getFileEntries() {
}

export default function InfoPane() {
const [, setQuery] = sharedState.use('query');
const [, setQuery] = useQuery();

const [recents, setRecents] = useState(getFileEntries());

Expand Down
12 changes: 6 additions & 6 deletions js/Inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import GraphPane from './GraphPane';
import InfoPane from './InfoPane';
import filterAlteredClicks from 'filter-altered-clicks';
import { selectTag } from './Graph';
import { sharedState } from './App';
import { useQuery, usePane, useModule, useGraph } from './App';
import { version as VERSION } from '../package.json';

export function Fix() {
Expand All @@ -21,7 +21,7 @@ export function ExternalLink({ href, children, target = '_blank', className, ...
}

export function QueryLink({ query }) {
const [, setQuery] = sharedState.use('query');
const [, setQuery] = useQuery();
if (!Array.isArray(query)) query = [query];
const url = `${location.pathname}?q=${query.join(',')}`;
function onClick(e) {
Expand Down Expand Up @@ -67,10 +67,10 @@ function Tab({ active, children, ...props }) {
}

export default function Inspector({ className, ...props }) {
const [query, setQuery] = sharedState.use('query');
const [pane, setPane] = sharedState.use('pane');
const [module] = sharedState.use('module');
const [graph] = sharedState.use('graph');
const [query, setQuery] = useQuery();
const [pane, setPane] = usePane();
const [module] = useModule();
const [graph] = useGraph();

let paneComponent;
switch (pane) {
Expand Down
26 changes: 0 additions & 26 deletions js/createSharedState.js

This file was deleted.

58 changes: 58 additions & 0 deletions js/sharedStateHook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react';
/**
* Factory function for creating React shared-state hooks.
*
* If you want to share some state across components and `props` propagation
* or "Context" isn't cutting it, then this is probably what you're after. A
* simple, easy, way to share common state across any number of components.
*
* # Example
*
* // In "app.js" (or wherever) create the hook, passing an initial value
* export const useFoo = sharedStateHook('hello');
*
* // Import hook
* import {useFoo} from 'app.js';
*
* function SomeComponent(props) {
* // Use hook as you would `useState()` (sans the initial value)
* const [foo, setFoo] = usefoo();
*
* // `foo` is shared by all components that use this hook
* // `setFoo` sets `foo` for all components that use this hook
* ...
* }
*
* # ... but why not React Context?
*
* 1. The Provider / Consumer model doesn't easily accomodate components that both
* provide and consume.
* 2. Contexts require coercing state into the React DOM, which isn't always
* convenient.
* 3. Contexts push developers toward a model where the context is a single "catchall"
* Object that holds many, largely unrelated, properties. This leads to components
* that depend on just one of the properties in a context, but that rerender when
* *any* property is changed.
*
* @param {any} value
* @returns {Function} React hook function
*/

export default function(value, name /* for debugging */) {
const setters = new Set();

return function useSharedState() {
const [val, setVal] = useState(value);

useEffect(() => {
setters.add(setVal);
return () => setters.delete(setVal);
}, [val]);

return [val, v => {
// console.log(name, '->', v);
value = v;
for (const setter of setters) setter(value);
}];
};
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"standard-version": "9.2.0"
},
"scripts": {
"start": "parcel index.html --dist-dir docs",
"build": "rm -fr ./docs && parcel build index.html --dist-dir docs && cp CNAME docs/CNAME",
"start": "rm -fr .parcel-cache && parcel index.html --dist-dir docs",
"build": "rm -fr .parcel-cache ./docs && parcel build index.html --dist-dir docs && cp CNAME docs/CNAME",
"build:diff": "npm run build && git diff --quiet docs",
"release": "standard-version",
"test": "eslint ."
Expand Down

0 comments on commit e5fe7c5

Please sign in to comment.