11:02 PM] localjo:: I'm trying to improve my ability to debug a React app. Right now I'm just using console.log all over the place to inspect what's going on and it's slow and tedious. I'd like to take advantage of Chrome Devtools to set breakpoints and step through code, but I'm having trouble because most of the time Devtools is stepping through a bunch of internal React functions and I get completely lost. Is there an easier way to step through just my own code?
[11:08 PM] acemarke: what kinds of stuff are you trying to debug?
[11:08 PM] localjo:: I know Devtools has a blackboxing feature, but the docs for that are outdated.
[11:08 PM] acemarke: in theory, one of the advantages of React is that it should be easy to trace dataflow
[11:09 PM] acemarke: but yeah, my experience is that stepping into React itself is not anything that's going to help
[11:09 PM] localjo:: Hmm, I can't even trace things like what's happening that's preventing a component from re-rendering when I expected that it would without getting mixed up in React code.
[11:09 PM] acemarke: I haven't had to blackbox anything new in a while, but I think it's just right-clicking a frame in the call stick list and selecting "Blackbox Script"
[11:10 PM] localjo:: Nope, they seem to have removed the right click option for that.
[11:10 PM] acemarke: what version of Chrome?
[11:11 PM] localjo:: 53
[11:11 PM] acemarke: current instructions are here: https://developers.google.com/web/tools/chrome-devtools/javascript/step-code
Google Developers
How to step through your code | Web | Google Developers
By executing code one line or one function at a time, you can observe changes in the data and in the page to understand exactly what is happening.
[11:11 PM] acemarke: supposedly it's using a filter pattern in the DevTools Settings window
[11:12 PM] acemarke: or patterns plural
[11:12 PM] acemarke: but, more specifically for your current issue
[11:12 PM] acemarke: components re-render for three reasons
[11:12 PM] acemarke: 1) the parent re-rendered; 2) the component called this.setState()
; 3) the component called this.forceUpdate()
[11:13 PM] acemarke: which one are you expecting to be the cause here?
[11:18 PM] localjo:: Oh, ugh, Google has an outdated copy of their docs still live with a tiny little notice saying that the docs moved π¦
[11:19 PM] localjo:: Anyway, what I'm doing is complex, and hard to describe so that's why I'm just trying to get my devtools working.
[11:20 PM] acemarke: if you'd like to try to talk it over, go ahead
[11:20 PM] acemarke: out of curiosity, are you using Redux?
[11:20 PM] localjo:: Bascially on /review of my app, componentWillMount()
calls a function called primeData()
that gets some database data, and redirects the app to /review/:latestId
. I then expect the primeData()
in componentWillMount()
to be called again, but it's not.
[11:21 PM] localjo:: Yeah, Redux and react-router
[11:21 PM] acemarke: ah. I'd guess that the component isn't actually unmounting
[11:21 PM] localjo:: I would have expected that navigating from /review
to /review/:id
would unmount, no?
[11:21 PM] acemarke: nope
[11:22 PM] acemarke: if the overall rendered tree of components is the same types, then it would keep the same instances
[11:22 PM] acemarke: React's algorithm makes several assumptions
[11:22 PM] acemarke: one of those is that if the type of component at a given point in the tree changes, then everything below it is going to be different, and so it'll just throw that portion away rather than actually diffing it
[11:23 PM] acemarke: on the flip side, if a particular component type is consistently rendered in about the same point in the tree, it'll keep the same component instance alive there(edited)
[11:23 PM] acemarke: so if the routing isn't actually causing the relevant parts of the tree to drastically change, then it's presumably keeping the same component there
[11:24 PM] acemarke: and given that it looks like you're just adding an ID value to the URL, I'd guess that's not a major difference in the routing
[11:24 PM] acemarke: so the component is only created and mounted once
[11:25 PM] localjo:: I see. Interesting. Thanks for the explanation. I wish I had a better of mental model of how this all fits together. Have any recommendations on how I can improve that? I've been building React apps for 9 months and I still struggle with knowing what's really going on.
[11:26 PM] acemarke: hmm. a couple random thoughts first
[11:26 PM] acemarke: my previous app was built with Backbone
[11:26 PM] acemarke: one of the things I loved about Backbone was that it was really small, and that there's absolutely no "magic" in it
[11:27 PM] acemarke: any time I had a question, I could step right into it and see what was happening, often following event trigger chains out to the listeners
[11:27 PM] acemarke: React, as I said earlier, is vastly more complex internally
[11:27 PM] acemarke: however, the overall mental model that it gives you is very powerful
[11:28 PM] acemarke: "why does my UI look this way? Because of the props and state the component had. Where did the props come from? The parent. Where did that data come from? ......"
[11:28 PM] acemarke: so yeah, stepping into React isn't something I ever want to do
[11:28 PM] acemarke: and there's definitely times when seeing an error gives an annoyingly long callstack of React internals
[11:28 PM] acemarke: but, looking at the error is almost always enough to point out what happened and why
[11:28 PM] acemarke: ("almost" being the key word :) )
[11:29 PM] localjo:: Hehe right π
[11:29 PM] acemarke: so, those are semi-rambly thoughts, but the point is that React lets you think about pieces of your app in relative isolation
[11:30 PM] acemarke: and most of the time you should be able to step your way back up the component tree to see where a given piece of data changed to affect the component you're looking at now
[11:30 PM] acemarke: so usually it's just a matter of slapping breakpoints into render methods and event callbacks
[11:30 PM] acemarke: now, do you have the React DevTools extension installed?
[11:30 PM] localjo:: I do
[11:31 PM] acemarke: good. and the Redux DevTools extension?
[11:31 PM] localjo:: Yeah, I know how to use the basics of both of them, but I'm sure there's lots of power I'm missing out on because I don't know how to use it.
[11:31 PM] acemarke: those are both incredibly valuable for being able to dig into the component tree and the state tree at any point
[11:32 PM] acemarke: most of the time, what I do with the React tools is to right-click a component on the screen and "Inspect" it
[11:32 PM] acemarke: that jumps to the "Elements" tab
[11:32 PM] acemarke: and if you then click on the "React" tab, it'll sync its position to the component for that element
[11:32 PM] localjo:: I was trying to put a breakpoint on componentWillMount() but then I got lost and came here after some frustration. π
[11:32 PM] acemarke: so you can jump right to viewing the props and state for a component that's deeply nested in your UI, without having to expand every parent in the React DevTools tab
[11:33 PM] localjo:: So, in my case, I want to figure out why componentWillMount() isn't called again, and figure out if I need to change something so that it is called again or if I need to put my primeData() call somewhere else.
[11:34 PM] acemarke: With the Redux DevTools, I'm usually looking to see what actions were dispatched, whether they contained the correct data, what the state diff was, and what the overall state looks like at that point
[11:34 PM] acemarke: yeah, admittedly, I'm not sure either DevTools extension would help as much with that one
[11:34 PM] acemarke: I think that's more a case of knowing enough about React's lifecycle and behavior to understand when and why a component is unmounted
[11:35 PM] acemarke: aaaaand that's my cue for the nightly link-pasting! :)
[11:35 PM] localjo:: Yeah. I'm thinking maybe I need a different lifecycle event
[11:35 PM] acemarke: if you haven't yet seen it, I keep a big list of links to high-quality tutorials, articles, and resources for React, Redux, and related topics, at https://github.com/markerikson/react-redux-links
GitHub
markerikson/react-redux-links
react-redux-links - Curated tutorial and resource links I've collected on React, Redux, ES6, and more
[11:36 PM] acemarke: tons of good info linked from there, including articles on the React lifecycle methods
[11:36 PM] localjo:: Ok, I'll take a look.
[11:36 PM] acemarke: (which I believe are in the "React/Redux Architecture" category)
[11:37 PM] localjo:: Basically what I'm trying to do, is if I land on a /review page without a valid ID, I check the database for the latest available valid id, and redirect there, /review/:id and then I need to check the database again using the same function.
[11:37 PM] acemarke: but yeah, overall, a component is really only going to unmount if its parent stops rendering it
[11:39 PM] localjo:: So;
- Navigate to /review
- componentWillMount() {primeData()} // Get IDs and keywords
- Navigate to/review/:id4)componentWillMount() {primeData(id)} // Get IDs and keywords again
[11:39 PM] localjo:: But steps 2 and 4 might need different lifecycle events.(edited)
[11:39 PM] acemarke: while I definitely have not actually done any routing, I wouldn't expect that to meaningfully change render output
[11:39 PM] acemarke: I would guess that the router would extract the ID field and pass it to some component
[11:40 PM] acemarke: but not actually change the components that are being rendered
[11:40 PM] localjo:: Well the children change.
[11:40 PM] localjo:: These pages are displaying a list of items that correspond to the id, so without the ID, the list is empty.
[11:41 PM] acemarke: what does the component tree look like, roughly?
[11:41 PM] acemarke: just something "A > B > C"-ish
[11:41 PM] localjo:: (It's all most complicated than that in my actual app, I'm just trying to simplify it for the sake of explanation)
[11:41 PM] acemarke: sure
[11:41 PM] localjo:: Let's see... it's like this...
[11:43 PM] localjo::
<Page>
<ListOfGroups>
<Group>
<ListOfItems>
<Item/>
<Item/>
</ListOfItems>
</Group>
<Group>
<ListOfItems>
<Item/>
<Item/>
</ListOfItems>
</Group>
</ListOfGroups>
</Page>
(edited)
[11:44 PM] acemarke: and which of those is trying to do this request?
[11:44 PM] localjo::
[11:44 PM] acemarke: (also, fyi, use triple backticks to do code blocks, plus an optional language abbreviation to get highlighting)
[11:44 PM] acemarke: like:
```js
code here
```
[11:45 PM] localjo:: Yeah, sorry, I was just being lazy with that last one. π¬
[11:45 PM] acemarke: AAAHHHHH! IT'S A NORMAL FONT AND NOT MONOSPACED! I CAN'T READ IT!!!!
[11:45 PM] acemarke: whew
[11:45 PM] localjo:: Haha
[11:45 PM] acemarke: but yeah, given that structure, I would totally expect to not unmount
[11:47 PM] localjo:: Maybe if I give an attribute like key={id} it would be forced to remount?
[11:47 PM] acemarke: not if the key stays stable
[11:48 PM] acemarke: I think what you really want is doing the request in multiple lifecycle methods
[11:48 PM] localjo:: I mean, when the :id segment of the route changes
[11:48 PM] acemarke: in particular, componentDidMount, and then probably either componentWillReceiveProps or componentDidUpdate
[11:48 PM] acemarke: Dan Abramov gives an example of that here: http://stackoverflow.com/questions/32846337/how-to-fetch-the-new-data-in-response-to-react-router-change-with-redux
How to fetch the new data in response to React Router change wit...
I'm using Redux, redux-router and reactjs. I'm trying to make an app where I fetch information on route change, so, I've something like: <Route path="
[11:49 PM] acemarke: the idea is that the request in cDM is unconditional, and then the request in the other lifecycle method is conditional based on the props possibly changing
[11:49 PM] localjo:: Ha, that answer looks like an almost identical problem to what I'm trying to solve.
[11:49 PM] localjo:: Reading up now. π
[11:49 PM] acemarke: doing stuff in response to props changing is a very common pattern
[11:49 PM] localjo:: How do you find this stuff? You seem like you have a mental catalog of all of the info there is to know about React haha
[11:49 PM] acemarke: well, uh... sort of yes :)
[11:50 PM] acemarke: in this specific case, I just googled for react componentdidmount componentdidupdate request data
(edited)
[11:50 PM] acemarke: because I knew the overall kind of info I was looking for, and just needed a good example
[11:51 PM] acemarke: but yeah, this is very heavily correlated to the links list I've put together
[11:51 PM] acemarke: which started off as "here's the best tutorials I found while learning React"
[11:52 PM] acemarke: and eventually turned into "I'm reading every React article I can find, here's the best ones for a variety of topics"
[11:52 PM] acemarke: tack on answering lots of questions in here (or at least reading other people's discussions)
[11:53 PM] acemarke: and so even though there's a lot of stuff I've never actually tried myself, much of the time I've seen something relevant
[11:53 PM] localjo:: Nice π
[11:54 PM] acemarke: I'll put in a couple more obligatory plugs/links
[11:54 PM] localjo:: Sounds good. I'm going to work on getting this to work, and I'll report back.
[11:54 PM] acemarke: in addition to the articles list, I also keep a catalog of Redux-related addons and utilities: https://github.com/markerikson/redux-ecosystem-links
GitHub
markerikson/redux-ecosystem-links
redux-ecosystem-links - A categorized list of Redux-related addons, libraries, and utilities
[11:54 PM] acemarke: and I've contributed a couple big sections to the Redux docs: the FAQ pages and the new "Structuring Reducers" section
[11:55 PM] acemarke: lots of good info and references in there
[11:55 PM] acemarke: (which, again, it's not so much that I came up with stuff that was particularly new or brilliant, just that I gathered together lots of info in one or two places)
[11:56 PM] acemarke: http://redux.js.org/docs/recipes/StructuringReducers.html
[11:56 PM] acemarke: sounds like routing is a more pressing issue atm, but I would encourage you to read through those sections in the Redux docs when you get a chance
[11:57 PM] localjo:: Thanks, I will! π
[11:58 PM] acemarke: so, hopefully that helped answer your question overall?
[11:58 PM] localjo:: I think so π I've got to tinker with it to really be sure I grasped the idea
October 20, 2016
[12:00 AM] acemarke: sure
[12:01 AM] acemarke: it's about time for me to call it a night here. I know you said you feel like you're having issues constructing a mental model for how stuff fits together. If you've got any questions or specific issues, feel free to ping me another night.
[12:02 AM] localjo:: Thanks!
[12:03 AM] acemarke: np