Redux Support
Can this library be used with redux? I assume that react-redux will not work so I was wondering how that could be done.
I'm sure it can be, but I'll let others answer this as I've personally never used Redux.
You can totally use redux, but you’ll have build your own version of react-redux (a haunted-redux, if you will). I would recommend looking at the /hooks folder on the react-redux repo https://github.com/reduxjs/react-redux/tree/master/src/hooks
This is quite interesting, It can work, there are some hooks missing in haunted. useCallback, useLayoutEffect, maybe more.
I thought why not to just plug react-redux and see if it works, if it does then big win for haunted:) I have setup a plunkr: https://next.plnkr.co/edit/8lukaUKTSzv6HOn5?preview
with importmap to load modules from unpkg and patch up things that do not work.
So now modules load,
react.js is reexporting haunted instead of React and also delivering some other hooks.
now it is failing to find a provider, but not sure why.
Do not forget to enable: importmap support. If you use chrome: chrome://flags/#enable-built-in-module-infra
context works now, problem was because of not specified module extensions in react-redux, or me specifying them on my side:). different modules were loading and singleton ReduxContext singletone module was loaded twice:)
Now useCallback and useLayoutEffect need to be implemented. react-redux can be repackaged for haunted or maybe made generic without referencing react.
Works now! https://next.plnkr.co/edit/8lukaUKTSzv6HOn5 Just replaced uselayoutEffect with useffect, and useCallback was already available.
Used es-module-shims, so no need to enable importmaps! Thanks @guybedford, works like a charm. Do not know how to feature detect if browser supports importmap to conditinally load esm-module-shims.
I hope It is clear now, what is missing, useeffect works in this example just fine. But maybe someone could show when it doesn't
https://github.com/reduxjs/react-redux/blob/5e6205a2f9bb18e1f8b0163382bc29230ea7d3d3/src/hooks/useSelector.js#L16
Here is a simple example with pure redux dependency:
import { component, html, useState, useEffect } from "haunted";
import { createStore } from "redux";
const counter = (state = 0, action) => {
switch (action.type) {
case "INCREASE":
return state + 1;
case "DECREASE":
return state - 1;
default:
return state;
}
};
const store = createStore(counter, 0);
const useSelector = selector => {
const [data, setData] = useState(selector(store.getState()));
useEffect(() => {
return store.subscribe(() => {
const d = selector(store.getState());
setData(d);
});
}, [selector]);
return data;
};
const useDispatch = () => action => store.dispatch(action);
const App = () => {
const val = useSelector(s => s);
const dispatch = useDispatch();
return html`
<p>val=${val}</p>
<button @click=${() => dispatch({ type: "INCREASE" })}>+</button>
<button @click=${() => dispatch({ type: "DECREASE" })}>-</button>
`;
};
customElements.define("my-el", component(App));
Did not test it much yet though and probably can be improved ...
useLayoutEffect should be coming pretty soon. Hadn't though of doing useCallback because it's not normal to use callbacks in web component code (events are used instead).
@jdin It works, for sure, it is simpler since it does not have a context. Well, simpler as long as all your store-connected (aka smart) components are build together with you application.
And each component using useDispatch is tighly coupled to the hook and the store it dispatches to:
See: https://next.plnkr.co/edit/TJt0M3OKT9iqfyy4 for example of two stores used, Notice that neither my-app nor my-counter has changed, and they could be shipped independently from application and combined in runtime;
Currently limitation is that Context resolution in haunted and react is performed by object reference;
Context1 === Context2
which is good if you have mutiple versions of the same context library, like react-redux in your application and want different behavior based on version, helpfull when migrating big application gradually.
But it can be changed to be checked by somthing like .valueOf
Context.valueOf = () => Symbol.for('some-context');
Context1.valueOf() === Context2.valueOf()
Then you have to manage context used by DOM tree, consumer will get the value from the closest context provider.
@matthewp My mistake, useCallback is already available in haunted!:) useLayoutEffect is not, But react is also a bit differently implemented in regards to scheduling. they say that useLayoutEffect callback has to be called just after commit before paint.
By this definition it can be performed in microtask after. But in docs they also mention that it callback is called synchronously after commit not sure what they mean there. Not sure if it matters if it synchronous or next microtask, since next scheduled microtask still blocks the eventloop
@matthewp Have made initial effort here
Better would be to create a haunted group to manage haunted related projects, if there will be any, but for will try to keep code there.
I have found a list of https://github.com/rehooks/awesome-react-hooks.
Some libraires just like redux have started publishing their hooks. Would be nice to make a port for them to, so that they can be used easily with haunted.
If you have questions why I have copied the code over instead of reusing the react-redux. I have made a comment about it. And some hooks kind of hooked into how React works, with ids scheduling and redering which differs from haunted.
React is synchronous. haunted is not(updates are batched).
If the hooks and rendering is aligned with React more, we could just make symbiotic packages consuming hooks from other libraries and re-generating them replacing react references with ones from haunted. Something like Renovte can help keeping packages upto date.
What do you think?
Related item come up with #130. We can revisit the scheduler if that will help with something.