Closed lukejagodzinski closed 4 years ago
Yeah, good point. That's one of the biggest design differences from Redux. React Tracked does just use React state. This is very important to support Concurrent Mode. (Even my other lib react-hooks-global-state isn't fully CM compatible, because it allows Redux-style dispatch.)
I'm pretty sure we use useEffect for your requirement. Could you tell me your use case as simple as possible? And, I'd create a new example to show the use case.
@dai-shi so the use case is that I have the React app in the iframe on the host website. I collect events from the host website and send them down to React app. But all the initialization happens in the React app. One example could be:
import store from "./store";
function handleHostEvents(hostWindow: Window) {
hostWindow.document.addEventListener("click", () => {
store.dispatch({ type: "INCREMENT_CLICKS" });
});
}
where the handleHostEvents
is being invoked outside of the React context.
The other example which is probably antipattern is to add some value from the state on each HTTP request.
import store from "./store";
async function makeRequest(url: string, data: any) {
const state = store.getState();
await fetch(url, {
body: JSON.stringify({ ...data, clicksCount: state.clicksCount });
});
}
any hint how to deal with that would be appreciated :). Thank you!
@lukejagodzinski Here you go:
This one is probably easy. Here's an example.
import { useReducer, useEffect } from 'react';
import { createContainer } from 'react-tracked';
const initialState = ...;
const reducer = ...;
const useValue = ({ hostWindow }) => {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
const listener = () => {
dispatch({ type: "INCREMENT_CLICKS" });
};
hostWindow.document.addEventListener("click", listener);
return () => {
hostWindow.document.removeEventListener("click", listener);
};
}, [hostWindow]);
return [state, dispatch];
};
const {
Provider,
useTrackedState,
useSelector,
useUpdate: useDispatch,
} = createContainer(useValue);
const App = () => (
<Provider hostWindow={...}>
...
</Provider>
);
If hostWindow
is globally accessible, you might not need to pass it as props.
I'd probably put something like this in Recipes.
It depends on where you invoke makeRequest
.
The current React Tracked tutorial uses use-reducer-async. In this case, you can getState
. It should work in usual cases, but there can be edge cases that need some consideration in the future.
Hmm the addEventListener example is actually quite elegant and I could create my custom hook to do all of the events handling in the separate file. I will try that, thanks! :) and the hostWindow
is just injected dependency accessible globally, so yes I will not have to pass it as the prop.
About the makeRequest, it's actually a helper function that I've created that is being used like async action but without using async actions. It's probably anti pattern but I was switching back and forth between React Context and Redux back then and settled on this working but not pretty solution. But as you said I could probably access state just in the async action :). Thanks for the help!
And yes it's definitely worth adding such an example to Recipes :)
You are welcome. Hope you like it!
In Redux, I can do something like:
in whatever place I want. When using React Context, I'm kinda forced to use it only from within the component. There are ways of creating functions that get reference to
dispatch
function but still everything has to be done in the context of Provider.I would like to use
react-tracked
but I guess it's currently impossible to switch from Redux when someone is using store outside of the React context? Right?