Closed fahad19 closed 6 years ago
I was thinking about this for a while and came to conclusion that adding this kind of functionality goes way out of the responsibility scope of compose
. Handling side-effects would fit much better in store (and it's already in the frint-store
in form of epics) not inline within compose()
. frint-props
provides withStore
method to connect to data the store.
User can also have action creators. So the only thing we're missing is the way to connect dispatching action creators into the props (mapDispatchToProps
in Redux terminology) and that's it.
I wouldn't go with this proposal further than adding mapDispatchToProps
analogue if it doesn't exist yet.
I should have been careful with my usage of the word "side effect".
If you notice more closely, the handler function is still dealing with props by calling a function that is already defined in the props object.
I can really see withHandlers
being very useful when we want to trigger a new async process (like fetching something from the server) at will from the component, and then return the results to the base component upon receiving the response. Not everything needs to touch the Store
.
Once you start having more or less complex state and operations on it, it's better to extract it to a separate entity such as a store. Then it's clear source of truth, it's easier to inspect, to debug and to understand how it works.
I don't want ad-hoc invoked compose()
HOC to accumulate all the state + handlers + async side-effects. It's going to a be a spaghetti of with...
functions with inline arrays of callbacks, observables. It can grow huge and unmanageable.
It's fine™ to have simple state management described inline, but growing it there is a bad idea. That's why I don't support this idea. Maybe with exception of handlers that modify props in sync way. But again, for that you can just add methods to your component.
Once you start having more or less complex state and operations on it, it's better to extract it to a separate entity such as a store. Then it's clear source of truth, it's easier to inspect, to debug and to understand how it works.
I understand your concerns. It's a difference in mindset between Redux (single App, single Store, single source of truth) and FrintJS (single Root App, multiple Providers, multiple Child Apps).
There is nothing stopping you from having multiple Redux stores in a single FrintJS App instance. They can be set as store
and anotherStore
providers for example.
I don't want ad-hoc invoked compose() HOC to accumulate all the state + handlers + async side-effects. It's going to a be a spaghetti of with... functions with inline arrays of callbacks, observables. It can grow huge and unmanageable.
That's the composition layer in custom applications. And it is more up to the developers how they use and implement it. We can always recommend them what not to do, and suggest using Stores when things get more complicated.
But I don't think that invalidates the need of a withHandler
function. It is needed for triggering some process based on an incoming event generated at the base component level (like onClick
).
If the state is needed to be shared across multiple components (and Child Apps), going for a Store (set as a Provider) is the way to go for sure like you mentioned.
If the state can live and die along with the component, it can just stay here. The logic doesn't need to be spread anywhere else.
And the state can be either generated in a synchronous or asynchronous way.
I understand your concerns. It's a difference in mindset between Redux
The concern is the same, disregarding number of stores/providers. Complex state management should happen in separate entity (provider/store)
And it is more up to the developers how they use and implement it.
It's not completely up to the developers. Would you let (or make it easier) for developers to write callback hell? With library we define API surface we have to maintain which also includes backward compatibility. The bigger it gets the harder it becomes. So we have to think carefully before adding random functionality.
Example usage
Basic side-effects:
The
props$
observable will now emit an object with these structure:counter
(Integer
)setCounter
(Function
)handleIncrement
(Function
)Trigger async process, that generates new props
The
props$
Observable will emit an object:todo
(null
ORObject
)fetchTodo
(Function
): Can be called asfetchTodo(todoId)
The
...args
infetchTodo
function are the same arguments as passed tocompose()(...args)
. Which from the perspective offrint-react
'sobserve
are(app, parentProps$)
.