4 ms·
Hi Sebastian and thanks so much for your feedback :) 1. So with observation an `ObserverContext` is created when using a store in a component. This "ObserverCo
by christianalfoni 2y ago
Hi Sebastian and thanks so much for your feedback :)
1. So with observation an `ObserverContext` is created when using a store in a component. This "ObserverContext" tracks what signals you access in the component. This "ObserverContext" has to end its tracking when the component function exits. This is what `using` does. It runs logic when the component function exits to stop tracking signals. Then indeed we run a `useEffect`, or rather `syncExternalStore`, subscribing to the `ObserverContext` created. Traditionally you would have to wrap your component function in a higher order component, but then you have an additional import and you have to define your components as variable assignments. This also affects debugging as your components are typically not named with this signature. So yeah, think of `using` as "tracking signal access in the component scope" :)
2. So until now I have not been in a scenario where you would need to do this. Either you use a "key" on the `StoreProvider` to unmount and mount with new props. Which makes sense if the prop for example represents a user or a ticket in a project management app. Typically subscriptions created internally in the store would be its dynamic nature. That said, this does bring up a very interesting idea. We could make the props signals. So you would access the props in a store like any other signal you define in the store. I need to reflect a bit more on it and create a POC, but it is something we should implement before the official release as it would be a breaking change later. Thanks a bunch for this perspective!
3. A bit of text was lost there, but as I understand you suggest giving first class support for traditional Observables in signals? And you mean RxJS types of observables? I did reflect on this early on. Like Angular has a `toSignal` on their observables. This is something that can easily be added later given that we find a good way to execute on it. I'll experiment some more on this, thanks for bringing it up :)
4. Impact is considered a much lower level API than traditional global state management. So if you want to synchronise a signal with a url or local storage you would currently have to do that manually. But as a lower level API I hope to see people build abstractions. For example there could be a local storage sync API that looks like:
```
function MyStore() {
const count = signal(0)
const flag = signal(false)
syncLocalStorage('$SOME-KEY', { count, flag })
return {
get count() {
return count()
},
get flag() {
return flag()
}
}
}
```
In this case the `syncLocalStorage` would be a two way sync with local storage. You define the initial signals and then any changes to them will be synced to local storage. Any changes in local storage is synced back to them, including any initial value. When the store unmounts, the sync is disposed of.
But yeah, I am not sure to what degree these things should be added to the library (It adds maintenance cost which I have been hurt by before). I want to first see if the community sees these opportunities and creates abstractions :)
Again, thanks a bunch for the feedback, I hope my responses made sense and have a great day!
- SebastianKra 2y ago1. 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 :)