Skip to content

Instantly share code, notes, and snippets.

@elsangedy
Created November 27, 2019 18:21
Show Gist options
  • Save elsangedy/62ab5d4f961632603a785d5ebd663f60 to your computer and use it in GitHub Desktop.
Save elsangedy/62ab5d4f961632603a785d5ebd663f60 to your computer and use it in GitHub Desktop.
hooks-as-plugin.jsx
import React, { useRef, useMemo, useState, useCallback, useEffect } from "react";
import ReactDOM from "react-dom";
function applyHooks(hooks, initial, ...args) {
return hooks.reduce((prev, next) => {
const nextValue = next(prev, ...args);
if (typeof nextValue === "undefined") {
throw new Error("A hook just returned undefined! This is not allowed.");
}
return nextValue;
}, initial);
}
const defaultState = {};
function useDrawer(props = {}, ...plugins) {
let {
initialState = {},
state: userState,
reducer = (old, newState) => newState
} = props;
let [originalState, originalSetState] = useState({
...defaultState,
...initialState
});
const state = useMemo(() => {
if (userState) {
const newState = {
...originalState
};
Object.keys(userState).forEach(key => {
newState[key] = userState[key];
});
return newState;
}
return originalState;
}, [originalState, userState]);
const setState = useCallback(
(updater, type) => {
return originalSetState(old => {
const newState = typeof updater === "function" ? updater(old) : updater;
return reducer(old, newState, type);
});
},
[reducer]
);
let instanceRef = useRef({});
Object.assign(instanceRef.current, {
...props,
state,
setState, // The resolved table state
plugins, // All resolved plugins
hooks: {
useMain: [],
useOnClose: []
}
});
plugins.filter(Boolean).forEach(plugin => {
plugin(instanceRef.current.hooks);
});
instanceRef.current = applyHooks(
instanceRef.current.hooks.useMain,
instanceRef.current
);
return instanceRef.current;
}
//---
defaultState.isOpen = false;
function useDisclosure(hooks) {
hooks.useMain.push(useDisclosureMain);
}
function useDisclosureMain(instance) {
const { state, setState, hooks: { useOnClose } } = instance;
useEffect(() => {
if (!state.isOpen) {
applyHooks(
useOnClose,
state,
instance
)
}
}, [state.isOpen])
instance.open = useCallback(() => {
setState(old => ({ ...old, isOpen: true }));
}, []);
instance.close = useCallback(() => {
setState(old => ({ ...old, isOpen: false }));
}, []);
instance.toggle = useCallback(() => {
setState(old => ({ ...old, isOpen: !old.isOpen }));
}, []);
return instance;
}
//---
defaultState.initialValue = {};
function useInitialValue(hooks) {
hooks.useMain.push(useInitialValueMain);
hooks.useOnClose.push(useInitialValueOnClose);
}
function useInitialValueOnClose(state, instance) {
instance.setState({ ...state, initialValue: {} })
return instance
}
function useInitialValueMain(instance) {
const { setState } = instance;
instance.setInitialValue = useCallback((initialValue) => {
setState(old => ({ ...old, initialValue }));
}, []);
return instance;
}
//---
defaultState.tab = 0;
function useTabs(hooks) {
hooks.useMain.push(useTabsMain);
}
function useTabsMain(instance) {
const { setState } = instance;
instance.setTab = useCallback((tab) => {
setState(old => ({ ...old, tab }));
}, []);
return instance;
}
//---
function Page() {
const a = useDrawer({}, useDisclosure, useInitialValue, useTabs);
const open = (initialValue) => {
a.open()
a.setInitialValue(initialValue)
}
return (
<div>
<button onClick={a.open}>open</button>
<button onClick={a.close}>close</button>
<button onClick={a.toggle}>toggle</button>
<button onClick={() => open({ name: 'Munir' })}>open with initial value</button>
<button onClick={() => a.setTab(1)}>set tab 1</button>
<pre>{JSON.stringify(a.state, null, 2)}</pre>
<pre>{JSON.stringify(Object.keys(a), null, 2)}</pre>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
</div>
);
}
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Page />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment