-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Closed
Description
Let's use this thread to discuss actual design considerations for an actual hooks API.
Prior references:
Along those lines, I've collated a spreadsheet listing ~30 different unofficial useRedux
-type hooks libraries.
update
I've posted a summary of my current thoughts and a possible path forward.
leslieJt, esamattis, spoeck, dmytro-lymarenko, r3b311i0n and 15 more
Activity
esamattis commentedon Feb 8, 2019
Based on my experiments I've come up with following wishlist for the official redux hooks api:
Provide low level primitives
useMapState()
- with setState like===
check based render bailoutuseDispatch()
- just return the dispatch functionuseStore()
- Too powerful?Maybe these higher level APIs
useActionCreators()
takes an actions creators object and binds it to dispatch (memoized)useSelector()
- reselect like helperDesigned for good TypeScript support. TypeScript is growing like crazy and the HOC based connector is and has been pain for TypeScript users. This is an awesome opportunity to serve TS users propertly.
For the curious I would engourage you to try the hook bindings here
https://github.com/epeli/redux-hooks
It's more than a toy as it attempts to actually implement all the performance requirements needed for real world usage and I would really appreciate any feedback because feedback on it would help the design of these official ones too.
jcestibariz commentedon Feb 8, 2019
There's a similar project in the Facebook incubator: https://github.com/facebookincubator/redux-react-hook
Jessidhia commentedon Feb 8, 2019
Personally I lean towards not providing an API that's expected to produce objects at all, but rather a separate invocation for each selector or action creator you want to use.
This does have the benefit of never giving you direct access to
dispatch
, though! Always using bound dispatchers feels way more ergonomic to me. If you want to improve usability further (such as binding certain arguments of a multi-argument action creator) you could always wrap either the input or the output in anotheruseMemo
.This would also have the side-effect of creating a separate subscription per
useSelector
, though. I don't know if that's a relevant performance consideration or not.I had an idea to share subscriptions between
useSelector
calls but it feels redundant:The complicated part is when you have a subscription value that depends on the result of other subscriptions -- but you only need one of the subscriptions to update for the component to rerender, and at that point the other selectors will be re-invoked when the
useSelector
is reached.If you really want to you can also just return an object but then you have to handle memoization yourself and you can't use
useMemo
(directly) for it.Jessidhia commentedon Feb 8, 2019
I also thought of a possible
effect
-like API but it feels dirty to use. It's "too global" as it's not necessarily coupled to your component; or even if it is, what would it mean to have multiple copies of this component mounted?It's like a
useEffect
but it'd also be invoked outside the React render cycle if the store state changed. Probably too low-level / dangerous, but is roughly the equivalent of getting thestore
from the context and callingsubscribe
yourself.chris-pardy commentedon Feb 8, 2019
Thinking about this as well and would suggest:
useSelect
which would copy the select effect API from sagas. That would let you use your existing map state to props functions with no real changes.useDispatch
which would wrap a call tobindActionCreators
letting you pass either an action creator, or object to create dispatch functions.Both hooks would use an identity function as the default first argument so the effect of calling them without arguments would be to return the entire state, or a dispatch function respectively.
I think there's lots of room for building on top of these two base hooks but why not start super simple and let the community evolve some patterns?
Partial typescript API (doing this from my phone, so excuse any oddities)
Full implementation (sans tests, examples, etc.) in this Gist - https://gist.github.com/chris-pardy/6ff60fdae7404f5745a865423989e0db
esamattis commentedon Feb 8, 2019
Here's an interesting API idea: Passive state mapping hook that does not subscribe to store changes at all. It only executes when the deps change.
Implementation is basically this:
It makes no sense as a standalone hook but when combined with an active hook it opens up a whole new world of optimization techniques.
Example:
I don't think you can get more efficient than that. Pretty readable too.
Pretty much a microptimization but avoiding new references can save renders downstream from pure components.
This is available for testing here.
adamkleingit commentedon Feb 9, 2019
I'm for this API a lot. On occasions, you need the dispatch (for dynamic actions that can't be treated with actionCreators), so I would add useDispatch.
I think this library should focus on the basic API to allow developers to extend with custom hooks. So caching/side-effect etc. should not be included
chris-pardy commentedon Feb 12, 2019
100% Agree on this, I think this is the direction things should generally be going with hooks, and it seems to jive with what facebook did with useState.
This feels overwrought, I suggested a simple wrapper around
bindActionCreators
but even if that's not exactly the API, just getting a dispatch function feels like the right level of simplicity. Something that needs to handle Thunk action creators feels overwrought.markerikson commentedon Feb 12, 2019
I think it's worth going all the way back to issue #1 as a reference. Dan laid out a list of constraints that the new in-progress React-Redux API would need to follow. Here's that list:
Obviously a lot of that isn't exactly relevant for hooks, but which ones are useful, and what other constraints might be good goals?
chris-pardy commentedon Feb 12, 2019
Feels like most of those original criteria are still relevant. I would rephrase:
As "shouldn't impact performance".
I'm concerned that hooks would be the ultimate foot-gun for:
But I'm not sure there's a good solution other than lots of evangelizing about the benefits of separation of concerns.
ricokahler commentedon Feb 12, 2019
I think this actually becomes less clear with hooks regardless. I think hooks makes it easier to understand and separate
smartcontainer vsdumbpresentational components but the effort has to be conscious.PresentationalComponent.js
connect HOC
hooks
Also addressing
This is it for hooks imo:
Because you have to manually create the container component, it's less obvious that you should separate container and presentational components. For example, some users will probably think, "why not just put
useReduxState
in the presentational component"?I still think the separation of container and presentational components is important but I'm not sure it's possible to create an API where we can make it obvious to encourage the separation.
Maybe this is a problem solely docs can solve?
adamkleingit commentedon Feb 12, 2019
When using custom hooks predictability is an issue on all fronts.
If you see:
in your component, it's not straightforward whether this component is aware of Redux or not, unless you enforce conventions in your team, like:
saboya commentedon Feb 14, 2019
@adamkleingit Not knowing that the component uses
Redux
or not is actually better for your business logic design. Redux is an implementation detail. If your hook is calleduseCurrentUser
, the only thing that the hook consumer should rely on is the fact that the current user will be returned. If later on you decide to switchRedux
for something else, you only have to work on your custom hooks, and nowhere else.199 remaining items
MrLoh commentedon Apr 22, 2019
@Dudeonyx I don't think this would work, as it would lead to a serenader as soon as the screen becomes inactive and everything goes to an empty/loading state. Nevertheless it should be possible to create a special memoized selector that saves and returns the last state when the screen is not inView
dimaqq commentedon Apr 24, 2019
Here's a crazy idea:
Provide syntax sugar with exact same behaviour as
mapStateToProps, mapDispatchToProps
.P.S. I'm not experienced enough to tell if the latter is better served by context instead.
MrLoh commentedon Apr 24, 2019
@dimaqq Discussion has moved to #1252 but that will probably not happen, the nice thing is, it's super easy to build your own custom hooks with whatever flavor you need based on the redux hooks
createAction
reduxjs/redux-toolkit#673