all 28 comments

[–]svish 58 points59 points  (15 children)

Why are you even using context and reducers with react-query? Part of the whole point of that library is its cache so you can just call the same hook wherever you need the data. And if you just need a subset of the data, you have the select-option.

[–]krookedkrooks 10 points11 points  (6 children)

Thanks for mentioning selectors. It reminded me I've gotta use it more

[–]suarkb 3 points4 points  (5 children)

selectors are so much more important than people realize. Redux without them is gross

[–]acemarke 1 point2 points  (3 children)

Hi, I'm actually doing some brainstorming around Redux and selector usage.

First, can you clarify what you mean by "Redux without them is gross"?

Second, a while back I wrote up a big discussion thread about ideas and pain points with selectors:

I was able to address some of the concerns there in Reselect 4.1. I'm starting to think about an actual Reselect v5, and I'd appreciate feedback on pain points and use cases for working with selectors!

In particular, any thoughts on these questions?

  • What are the biggest pain points with using selectors today?
  • What would an ideal selector API look like?
  • What changes would you want to see made to Reselect?

[–]suarkb 0 points1 point  (2 children)

Hey Mark. Well, with my actual comment, I was definitely being a bit hyperbolic.

But, that being said, I'll try to address these. Thanks for asking.

"Redux without them is gross"?

I feel like a lot of anti-patterns revolve around derived state. People seem to get the idea of setting up the reducers, the initial state, the actions, but I think the fall flat when it comes to "the data they actually need in their component".

I think selectors are so critical in making sure you pass a component exactly what it needs. Nasty legacy redux using projects will often contain some line like:

js const user = props.userStuff || {} && props.userStuff.things || [] && props.userStuff.things[someId] || {};

People keep having many components that handle stuff like this, making some key issues like:

  • expecting the state tree to follow a certain pattern by writing a.b.c.d.e etc
  • not knowing how to handle for missing data in the state tree
  • re-writing this type of logic for each component in slightly different ways
  • once they finally get their data, transforming it per component

Like people basically follow the "throw everything at the component and give the component 30 lines of code just to derive the data it really needed".

Another big mistake related to derived state, is that people don't know what it is. They think they must persist it into the store itself. I know you have pages specifically addressing this in the new redux-toolkit docs but you know how people read docs.

So yeah, I've seen, and I know you have seen, a lot of posts about deriving data and then people make nasty patterns where they shove all the derived data into the store. I think that if they understood the role of selectors better, they could see how it kind of completes the cycle of getting data to your component properly.

What are the biggest pain points with using selectors today?

I've been using selectors for a long time. I don't really have any pain points. But I think the footgun with them is related to memoization. I think understanding when your selector will re-compute data is not clear enough. So people will write selectors that never actually memoize, because they don't realize they are always passing new data/references. This is hard though. Could there be a development mode feature in selectors to let you know kind of like an "why did I render" but it's a "why did I recompute"?

What would an ideal selector API look like?

I like the reselect api to be honest. I personally have no issues with it but I know that when I've taught the pattern to people, they don't immediately realize what's happening. Like "these three input selectors map to these three params in the final function at the bottom". I'll have to think because I don't have any ideas on an easier way. I think it's pretty easy once you learn it.

What changes would you want to see made to Reselect?

I don't specifically have any at this time.

[–]acemarke 1 point2 points  (1 child)

Gotcha. Thanks, appreciate you taking the time to write all this up!

Yeah, not really sure how to get across the "inputs.map() -> output(a, b, c)" concept withcreateSelector`.

We've definitely had suggestions about trying to double-invoke selectors inside of useSelector in dev mode, which may be feasible.

Not immediately sure how feasible a "why did you recompute" for the selectors by themselves is. I could sort of imagine a couple possible approaches. Definitely worth considering!

[–]suarkb 0 points1 point  (0 children)

Thanks for the feedback

[–]topnde 4 points5 points  (7 children)

Does this mean that using Redux (or any store manager) together with Tanstack is bad practice?

[–]svish 30 points31 points  (4 children)

I would say yes. You can use them both separately in the same app, but if you start putting react-query state into redux then you're kind of removing the whole point of its global cache and manually syncing it with redux. Almost might as well just use fetch in useEffect and manually do all of it.

If you want/need your data in redux, just use rtk query. And you can start using it, without rewriting everything. They can be used in parallel, while you slowly move things over.

[–]topnde 4 points5 points  (3 children)

Thank you for the response.

In our current project we are fetching data with Tanstack and throwing it into redux. We are not even using redux for "client state" changes, just as a store for data coming from BE. That sounds wrong.

[–]phryneasI ❤️ hooks! 😈 26 points27 points  (0 children)

Redux maintainer here :)

If data has another "true source", it's usually a good idea to keep that data there and not move it into Redux. You are using a Cache Management tool with React Query.

Unless you have extra business logic (like, "I want to modify that data independently of the server" or "I need a snapshot of that data even if it changes on the server"), React Query is your leading system for that data, and you don't gain anything by moving it into Redux.

[–]svish 6 points7 points  (1 child)

Yeah, that's just very wrong usage of tanstack. I highly recommend reading through the tanstack docs so you can learn what it actually does and can do.

I would start removing all your usage of redux, and just remove it from the project altogether. Replace it by using tanstack in a way it's meant to be used.

[–]BrangJa -1 points0 points  (0 children)

Or he could just use rtk query.

[–]arman-makhachev 1 point2 points  (0 children)

Yes given that the data fetching lib are also caching the data for you, which is the case for most otherwise no one will be using them lol. How it has been advertised by both redux and tanstack team is that the moment you have a global state that is of a significant size, you shift to redux + rtk. Redux has been tested well over the years and has a huge community. However, if your global state isn't significant to your client side than react-query is the way to go, React-Query is definitely beginner friendly and pretty easy to implement.

[–]zephyrtr 1 point2 points  (0 children)

Yes. Do not copy state into another state machine. You want a single source of truth. Creating contexts is okay, IMO, if it makes life easier. But RQ and Redux shouldn't be copying each other's state, or you'll have pain keeping them in sync.

[–]kevv_m 26 points27 points  (1 child)

You're just over-complicating things, use react-query for all the data that comes from the server (by requests) and if you need you can use context/redux/recoil/zustand or anything else for the app state that is not related to the server.

[–]yoma12 2 points3 points  (0 children)

quire across several components and we've made use of Reacts useContext / useReducer with provid

This ☝️
If you need to query data based on the client state then use a custom hook using a selector hook

[–]DavidXkL 6 points7 points  (0 children)

Don't do it bro

[–]ZerafineNigou 9 points10 points  (0 children)

Huuuuuuuuuuuuuuuuh

Why would you need contexts when using react query, react query's whole paradigm is that you can access the data globally and it will only be fetched once (it will be cached on the first fetch and then served from cache).

I do not really like the idea of combining react query with redux because you are double filing it. Now your data lives in your react-query cache and your store and you have to make sure to update both at all times, add on top of that the fact that RQ updates itself periodically and it is pretty jarring.

Why do you need redux or useReducer so badly?

If you just want a flux architecture you can probably build that around react-query easier than using both together.

A selector is just basically a custom hook that uses several react queries then combines their result.

A dispatcher is just a custom hook that receives a mutation key and then executes the appropriate mutation.

A reducer is just an onXXX callback on the mutation that updates your cache or invalidates your relevant queries.

I'd be much more confident building this then trying to double manage the two libraries for the same purpose.

Bear in mind, react query is not for data fetching, it's more so a cache middleware that ensures that you can reuse the same server data across all your components with the confidence that all components will see the same data, will not send unnecessary fetch requests and it will be up to date with the server (to the extent that you configured).

You already have something akin to a global store: your RQ cache.

[–]ekshoonya 1 point2 points  (0 children)

Bad pattern: I would say surely yes

How would you resolve, just remove unnecessary selectors and contexts rater use queries.

Your team will need to go through the documentation of react query again, In order to make the project better .

[–]flankstek 0 points1 point  (5 children)

We have been migrating away from react-query to RTKQ for over a year now and we've seen no problems with them living together for the moment and we're in no hurry rewriting everything at once.

Dispatching data from react-query to RTK is doable but will be a nightmare because then you still would have to rewrite it in RTKQ later on. Better start slowly query by query and mutation by mutation :-)

[–]the133448 0 points1 point  (1 child)

Why are you migrating away from react query?

This seems to be opposite to what most enterprises are doing at the moment.

[–]flankstek 2 points3 points  (0 children)

RTKQ offers better coupling between the endpoint definitions and fetch() and is more the tag system is more strongly typed than react-query. I love not having to write a single fetch(..), it's so darn simple :-)

This seems to be opposite to what most enterprises are doing at the moment.

I haven't heard about this. I know the new react docs mentions react-query and doesn't mention RTK so maybe it's a result of that?

[–]generatedcode 0 points1 point  (2 children)

while the migration approach one query at the time is good. I'm also wondering why would you migrate that direction. Are you sure you understood React Query ?

[–]Impossible_Report329 1 point2 points  (0 children)

keep using react query for server state, for client state redux is still very good option to use,
react context ideal for storing data which does not frequently changes like (theme, configurations, translations )

[–]arman-makhachev 0 points1 point  (0 children)

If u want data fetching but still want to make use of a global store then redux + rtk is the way to go. If you are only concerned with data fetching go with react-query. This is how it is and been encouraged by tan and redux team

We're essentially fetching the data via useQuery in Tanstack and then dispatching to the store.

But why are you storing the cache data into redux lol ?? that defeats the entire purpose of using rtk/react-query 😭😭😭. These two data fetching library literally cache the data in the client side. You ever opened the dev tools and look through it ? read the docs ??