4 ms·
1. that's actually pretty neat. Collecting dependencies like Signals but without the callback... I retract my reservations :) 3. I was thinking of any arbitrar
by SebastianKra 2y ago
1. that's actually pretty neat. Collecting dependencies like Signals but without the callback... I retract my reservations :)
3. I was thinking of any arbitrary event source that supports the typical subscribe/unsubscribe pattern. Either push based like rxjs or pull based like Reacts useSyncExternalStore. rxjs would probably require some extra work to integrate error handling, and I prefer that the pull based approach is just generally more friendly to debugging, tracing & stack traces.
4. That example looks reasonable to me, although we're now once again running into the problem of starting with uninitialised state and only later populating it. In the case of local storage it's not much of a problem since you need a default-value anyways, but what if we try to derive data from react-query? I agree that the framework itself should be agnostic of the actual storage method though.
2, 3 and 4 are all different facets of the same requirement: An observability solution must be able to derive from any external source, or we are back to doing most of our derivations inside react hooks. At least that's the issue I always ran into when dealing with redux, react-query & co. I would have 40% of my state in redux, 40% in react-query, 10% in the URL, 10% in local storage. Then I would do most of the heavy transformations with react hooks, because that's the only place which allows me to combine all sources. Im aware that some of this could be solved with redux middleware / rtk-query, but then you're once again loosing type-safety due to every component needing to handle the uninitialised case.
You were absolutely right in saying that observability is better than reconciliation for state management. However, I think this does not only apply to state management but to all types of derived data, including server state, local storage etc...
- christianalfoni 2y agoHi again and thanks for this discussion :) Yes, what you are saying makes a lot of sense. And traditionally you would need to do some combination of a global state store like Redux, react-query and custom hooks. Impact does support a new type of asynchronous pattern with React, which I am very excited about. It is supported in React 18 using the polyfilled `use` hook in Impact, but with React 19 you can use the `use` hook of React itself. What this means is that you do not have to differentiate sync/async values in signals. Signals makes promises observable. So: const data = signal(fetchData()) const count = signal(0) Both are observable values, where the data promise can be consumed in components with the `use` hook or by checking its data.status property directly. So instead of having to split your state management between redux, react-query and hooks you are now able to just think state and co locate it with the same primitives and abstractions using stores. If you read the https://impact-react.dev/deep-dive/queries-and-mutations https://impact-react.dev/deep-dive/queries-and-mutations docs, you'll see how the low level usage of this is. I would expect someone to create a similar data store abstraction for Impact like react-query, to manage expiration etc. at a higher level, like: const DataStore = createDataStore(...) which allows you to: function MyComponent({ id }) { using dataStore = useDataStore() const data = use(dataStore.fetch(id)) } But unlike react-query it would be based on observable promises. But yeah, I do not want to state that Impact should just "do everything", but it is a low enough abstraction to co locate all state management in the same abstraction, if that is a priority. It might still feel too low level at the moment, but I am very curious to see where it goes. When it comes to RxJS types of observables it is possible to do the same as `toSignal` in Angular, but I am unsure if it should be a first class concept like promises. Maybe it should, but also something the community can build :) Would love for you to read https://impact-react.dev/deep-dive/queries-and-mutations https://impact-react.dev/deep-dive/queries-and-mutations and give your thoughts. We refactored a part of our own application where we needed to: 1. Fetch data about the content (promise) 2. Use that to instantiate a process (promise) 3. But async instantiation of this process also emitted events during instantiation we wanted to display as part of loading the process 4. Finally mount state management for this data bound to the process Previously these steps where multiple components, but with this new async pattern we where able to express it all in a single component, line by line, using a store. It blew my mind :)
- SebastianKra 2y agoI get what you're saying in that document, but I would still prefer react-query. Although your example seems simple enough, it would become increasingly complex once you think about swr, caching, persistence, invalidation-timers etc... Requiring everything to be based on your low-level implementation seems like a tall ask. One thing that I love about React is how easily it can be configured to read from any reactivity-solution. This makes it possible to integrate pre-existing code or libraries. In impact, I see two possible clean entry points for external state: the Provider props, or a signal constructor similar to useSyncExternal store. I did some experimentation to see if I can write a function that converts an rxjs stream to a signal, but it involved multiple dirty hacks such as extracting the resolve/reject functions from the promise callback.
- christianalfoni 2y agoYeah, I agree that is definitely a tall ask :) I just published a version with the signal props. Doing this made me realise that it creates this strong concept that StoreProviders becomes the bridge between the reconciling world of React and the observable world of Impact. You have plain values going in as props, becoming signals in Impact. And you have signals going out of Impact, consumed as plain values in React. So with react-query you would be able to just use that data fetching hook and pass data to a StoreProvider, where it becomes a signal. So you could have a store like: ``` function MyStore(props) { const reactQueryData = props.data effect(() => { console.log(reactQueryData()) }) return {} } ``` React will update that signal whenever react-query causes reconciliation and the value changed. In case of RxJS I was thinking of doing something like: ``` function toSignal(rxJsObservable, initialValue) { const value = signal(initialValue) const subscription = rxJsObservable.subscribe(value) cleanup(() => subscription.unsubscribe()) return value } ``` So basically you give a signal a default value and subscribe to the updates, updating the signal. But as I understand RxJS there are several ways to think about these observables... hot/cold... single event, multiple events etc. etc. Not sure how to create a single abstraction over that.
- SebastianKra 2y ago