Created
November 27, 2019 18:21
-
-
Save elsangedy/62ab5d4f961632603a785d5ebd663f60 to your computer and use it in GitHub Desktop.
hooks-as-plugin.jsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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