Alternative interface idea that helps pave over the stale closure gotchas when working with React Hooks.
If you find yourself trying to connect data between multiple hooks like useEffect
's and useRef
's, in other words writing anything more than simple imperative logic in your components, then this gives you a saftey shield from even the mildest of react hook's dependecy/state state issues
import createComponent from './createComponent'
// Basic Usage
const Example1 = createComponent(() => {
// Rule 1: return a function that returns jsx
return () => <div>Hello</div>
});
// Props
// Rule 2: Don't destructure props at any point
const Example2 = createComponent(({ props }) => {
return () => <div>{props.children}</div>
});
// State
// Rule 3: Don't destructure state at any point
const Example2 = createComponent(({ props, state, setState }) => {
setState({ counter: 1 }) // aka. initial state
return () => <div>{state.counter}</div>
});
// Methods
const Example2 = createComponent(({ props, state, setState }) => {
setState({ counter: 1 })
function inc() {
setState({ counter: state.counter });
}
setInterval(() => inc, 1000)
return () => <div onClick={inc}>{state.counter}</div>
});
// Existing Hooks
const Example2 = createComponent(({ props, state, setState, hooks }) => {
setState({ counter: 1 })
let x, y, timeout, myEl
// Rule 4: Wrap hooks
hooks(() => {
myEl = useRef();
useEffect(() => {
// did mount (myEl.current is avaliable here)
return () => clearTimeout(timeout) // did unmount
}, []);
({ x, y } = useMousePosition(myEl.current))
});
timeout = setInterval(() => setState({ counter: state.counter + 1 }), 1000)
return () => <div ref={myEl}>{state.counter} {x},{y}</div>
})
// As a Hook
const useMyLogic = createComponent(({ props, state, setState, hooks }) => {
setState({ count: 0 })
function inc() {
setState({ count: state.count + 1 })
}
// Still return a function that returns the data
// Still don't destructure state
return () => [ state, inc ]
});
const MyControllerComponent = createComponent(({ hooks }) => {
let counterstate, inc
hooks({
[ counterstate, inc ] = useMyLogic({ /* props */ })
})
return () => <div onClick={inc}>{counterstate.count}</div>
})
const MyNormalComponent = (() => {
const [ counterstate, inc ] = useMyLogic({ /* props */ })
return <div onClick={inc}>{counterstate.count}</div>
})