-
Notifications
You must be signed in to change notification settings - Fork 670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Enhancement] Selector Debugging Tools #279
Comments
I LOVE this idea. I could also be accused of overusing selectors. One of my app data models in particular relies on lots of data that's derived from the redux state tree (things like totals, moving averages, and lots of other stats that are derived). By having these things in memoized selectors it made the logic much simpler not having to keep all the data in sync, and perf is quite good. However, as you mentioned, having so much of the data model in selectors means I get less value from the redux dev tools because a large portion of my state is actually hidden from the tools. It would be great if the tool kept a count of how often the selector was able to return cached data vs re-computing. Imagine if each node in your screenshot has a label such as "23/1195", meaning this selector was called 1195 times and 23 of those times required re-computing due to input change. This can help find places where people are unintentionally causing the selector to recompute or where the selector wasn't needed in the first place. |
Yeah that's a great idea, I'd love to know which of my selectors is doing the most work, and how effective its memoization is. I know some people like to increase the size of the cache in the I'm not quite sure what the best way of implementing it is. It's really nice that Reselect already keeps track of the cache hits, but considering the performance choices that have been made, it seems like the core lib shouldn't also keep track of cache misses (@markerikson thoughts?). It seems like at a minimum, we'd need to change the use of Looks like that work is being done here: #297 |
Not sure I have specific thoughts, other than I think this is a good idea to play around with. |
How's the progress on this? Anything I can help out with? |
I have the client library done, but I'm new to chrome extension development so I've been dealing with hurdles there. My plan right now is to release the library first and get some feedback on it - so you could help by reviewing the code or beta testing for me! Thanks for offering! |
This is a great idea, I was just thinking how useful something like this would be and came to the issues to see if anyone had tried or succeeded in getting this sort of debugging set up. I agree that being able to see cache hits/misses would also be very useful. I use https://github.com/jhen0409/react-native-debugger rather than Chrome itself for showing the React devtools, Redux devtools and standard Chrome devtools while working on React Native projects. Hopefully it will be possible to use the client library in this context once you've got it all working. |
Here is the client library: If you have any feedback whatsoever, please create an issue. This is also my first npm lib, so let me know if there are any package-maintainer things I ought to be doing but am not. I apologize for the delay, I've been quite busy at work. I'm hoping to bang out the extension pretty soon! |
Here is the chrome extension. As is, I've found that it's useful for allowing me to identify selectors that are recomputing often and why. It's also handy for sanity checks to see what a given selector's inputs and outputs are at any given time. There's still quite a few rough edges, and I think I'm only scratching the surface of what's possible. Some of my ideas (and yours!) are listed here |
This looks really neat. I've added a link to it in the related projects section of the readme. |
Hey @ellbee thanks! I actually just updated the verbiage so I'll put a PR (#302) in to reflect that the extension is released. Speaking of which! I've now published a simple demo page that you can pop open the devtools on so that you can get a better idea of the features before instrumenting your app. As I mention in the new getting started section, it would be great if reselect itself could have the hooks for the devtools built in. @markerikson mentioned you were the main maintainer of this lib. Could we have a chat some time to discuss that possibility? What is a good way to reach you? On another note I have actually been using the tools to make performance improvements to my app! const foo$ = (state) => state.foo;
const bar$ = createSelector(foo$, (foo) => foo.baz);
const bar2$ = createSelector(bar$, (bar) => bar + 2); Switching to const bar$ = (state) => state.foo.bar;
const bar2$ = createSelector(bar$, (bar) => bar + 2); Solved a performance issue I didn't even know I had. |
Wow, this looks really cool! Definitely gonna try it out, been looking for something like this since i started using reselect. Do you think it would be possible to measure recompute time by providing a custom memoize function? That could be really useful when looking for bottlenecks. |
@skortchmark9, I think this is a great idea, and I'd really love to be able to take advantage. Has there been any movement toward getting the necessary hooks/integration points added to |
@skortchmark9 Looks like #251 and #297 are in! Any hopes of getting this moving again? It would be amazing to have this readily available in the chrome store :D! |
They are in but not yet released, so I've been holding off on doing any work. As soon as 4.0.0 is out I'll update the tools. Also - the extension is actually already available: https://chrome.google.com/webstore/detail/reselect-devtools/cjmaipngmabglflfeepmdiffcijhjlbb |
Hi @skortchmark9, v4.0.0 has been released now. |
Following up on this (very belatedly). @skortchmark9 , anything else that needs to be done here to help you? |
Nah, IIRC I picked up these changes and released a new version. Closing this down. |
Cool. FYI, I'm temporarily stepping in to help with some Reselect maintenance, and will be posting a v5 roadmap discussion thread "soon" (hopefully next couple days if I can find time to finish writing it). Would love to have you take a look at it once it's posted! |
Firstly, I love reselect, it's possibly my favorite part of the redux ecosystem. I love it so much in fact, that I could be accused of overusing selectors - I like to shove as much logic as possible into them, so that my teammates and I can share computed data in a performant way across our app.
Because we rely so heavily on selectors for our logic, selectors are often where our bugs crop up. Redux's debugging tools are excellent, and I'm imagining reselect could pair its debugging tools nicely along with them. A particular emphasis is "No Print Lines". I like that all the information about my app's state store is already present for me to view without a code change. If all the information about the app's computed state were available also, I would never have to change my code to figure out what was broken.
Ideas
1. Graph
The first idea is essentially the same as what was originally proposed in #71 : a visual dependency graph of selector parents and children:
(picture grabbed from #71 , originally from @MattSPalmer)
This would be really useful for refactoring heavily depended-on selectors, as well as debugging.
2. Click-to-select
The ability to click a selector on that graph to see what its inputs / outputs are at a given state.
I don't really like the logging solutions in #71 because they don't let you surgically inspect a particular selector without making code changes. Same issue with @ajwhite 's https://github.com/ajwhite/reselect-devtools.
Implementation
I don't think it would be too hard to make a drop-in wrapper for
createSelector
which populates the dependency graph:And click-to-select just needs
getState()
injected, either via middleware or something even more vanilla:Challenges
Naming
The one thing I am struggling with is getting the selector names. They're usually anonymous! In my
createSelector
wrapper, I'm keying the graph based on theresultFunc
'stoString()
, which is going to result in pretty ugly display names. For example:A hackish trick that I tried was to name the selectors based on
resultFunc
's arguments:But even if I had a perfect argument-name-parsing regex (hard!), it could still break if users used a selector in two places but named its output differently in
resultFunc
, or if they used object destructuring, or if they uglified their code.Obviously, we could add string names to the functions but that would add more overhead than I'd like for users of this lib.
[EDIT] Ok, I thought about it for a while and I don't think there's a way to get the names across every environment without writing them explicitly somewhere. I think what I'll do instead is give this lib another function (just one!) called:
This'll be opt-in, otherwise I'll just give the selectors names based on their position in the tree.
Conclusion
Thanks for reading!
The text was updated successfully, but these errors were encountered: