-
Notifications
You must be signed in to change notification settings - Fork 47.3k
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
DevTools: Profiling tool improvements umbrella #22274
Comments
What would be the take-away knowing the before and after value? I feel like (strong emphasis on "feel") that most of the unintended updates are due to a value being updated that only changed due to referential equality e.g. functions and objects. And it's unlikely that a prop changed from a primitive to a complex value. So I suspect that knowing what prop changed is usually enough to recognize that the value needs memoization. So does my action really change knowing the value before and after? Maybe for starters we can experiment with tracking type changes first and see if people need even more granular data? At least for me seeing that some prop repeatedly changes and that it's an
When you talk about remounts does this only concern mount/unmount of the same element type or would this also highlight mounts/remounts in the same "place" (if that is something the Profiler knows about) i.e. mounts/unmounts of different element types. For example, a common beginner mistake is defining components during render which means they (and their subtrees) are remounted on every render (which is especially problematic if their host components contain state such as being focused): function App() {
const input = styled.input({ color: 'blue' });
return <input />
} Being able to highlight these with devtools may help avoid these mistakes or even notice previously unnoticed instances. |
Not sure, but it seems to be a commonly requested feature. (Maybe @lahmatiy can weigh in here?)
TBH I think a type change seems pretty unlikely, so I'd be hesitant to add code to track that.
When I wrote the issue, I was thinking more about the use case of wrapping a part of the tree (which causes a deep unmount and remount) but you're right that inline functions are another common cause. Ideally this would highlight both cases! |
fwiw, this is exactly what the Redux DevTools do. You can see:
All of those have value depending on what you're trying to do. Related to this, I would love to know what the initial cause was of a React render in the first place - what components were the first ones queued, what the actual state updates were in those, etc. I'd agree that as amazing as the React DevTools are right now, the biggest thing they're missing is the view of state+components over time. I realize that tracking that info may be difficult, but if you can figure out some ways to address that functionality, it could be hugely helpful. and yes, co-sign the idea of figuring out a way to notify users or highlight when components keep getting blown away due to defining types while rendering. |
Thanks @bvaughn for the great notes and spotlight some topics to discuss!
React render tracker (RRT) covers both cases. For example: import * as React from "react";
import ReactDOM from "react-dom";
function Content() {
return "some content";
}
function Wrapper({ children }) {
return children;
}
function Case1() {
const [count, setCount] = React.useState(0);
const StyledComponent = function () {
return <Content />;
};
return (
<div>
<StyledComponent />
<button onClick={() => setCount((c) => c + 1)}>{count}</button>
</div>
);
}
function Case2() {
const [count, setCount] = React.useState(0);
return (
<div>
{count % 2 ? (
<Wrapper>
<Content />
</Wrapper>
) : (
<Content />
)}
<button onClick={() => setCount((c) => c + 1)}>{count}</button>
</div>
);
}
ReactDOM.render([<Case1 />, <Case2 />], document.getElementById("root")); Here is the video how RRT handles it (note that RRT shows component's tree as owner-based hierarchy by default, that's why Wrapper and Content on the same level in the beginning): Screen.Recording.2021-09-09.at.22.00.46.mov
That's a general functionality for RRT to show what's was changed for a component over time. It doesn't contain all the state of a component (because it consumes a lot of memory) but a simple diff. In case of changed state or a context, it's usually a trigger for update (re-render) and RRT marks such updates with lightning icon. Here is an example: |
Just to clarify, this is showing changes for only the subtree rendered by the currently selected component? (The "owners tree" to use React DevTools terminology.) |
@bvaughn RRT component's tree showing the entire tree for a render root. Plans to add it is possible to limit what to show, like React Devtools owner tree but additionally to see entire component's subtree or subtree with selected types of components. In a real app there are a lot of components, most of them are not significant for an understanding what's going on. The simplest solution is to provide the user with a selection of the components that matter. But I think there must be a way to define what is worth showing and what is not. But these are still crazy ideas. I don't think owners tree is solving the problem. |
I see. I asked because (in my opinion) there is no way such a UI/UX scales for an app larger than a small, demo application. |
Hi folks! UpdatesDiff improvementsValues diff was improved. For instance for objects RRT takes a sample of 3 entries max (usually it's more than enough) to show some details in changes. The rest changes are shown as a comment how many more entries are different. A "shallow equal" badge was added for objects and arrays when no changes in entries or elements, so objects/arrays are different by a reference only. Size of payloadThe size of payload (data collecting from React internals and transferring to UI) depends on the type of app. Approximate its 2,5-3,5K events per 1MB. A payload size can be observed in status bar: Considering that a lot of new events and data have been added recently, and there have been no size optimizations yet, this is not so much. I believe the size can be reduced. Tracking for call stack traces and locations of hook usageAs you might see on a screenshot above, RRT is collecting call stack trace for hook usage in diffs now, as well as custom hook invocations. That's not only for state hooks (useState/useReducer), but also for context and memo hooks (useMemo/useCallback) Improve tracking for update reasonsTracking for Component#setState(), useState() / useReducer() hooks callback and Component#forceUpdate() were added. In most cases RRT may detect a location of such calls. There are two more update reasons (if I haven't missed anything) which are not supported by RRT, but I'm seeking a way to implement them. Added tracking for update bailoutsIt's was a little bit tricky, but turned out possible to track update bailouts. Such information is useful for understanding for which component, why and how often the wave of updates stops. Bailout updates can be observed on fiber's tree as count badge and as an event on events log: Most developers are surprised at the topic of update bailouts and that there is something else besides React.memo() and shouldComponentUpdate(). Info sectionsAdded more sections for a selected component to observe props updates, memoization effectiveness and context usage. Let me just attach screenshots of the slides as they are more informative: ConclusionAs I found, there are much more useful things which can be gathered from React internals than modern React tools provide. React is not friendly for devtools, so we need to use various hacks and tricks to get the information we need. Sometimes even hacks and tricks don't work. However, in most cases it's possible and that's much more than nothing. I would be happy if React's core will be tweaked to avoid hacks for data gathering, or at least to add more possibilities in data collecting. Nevertheless, I continue to work on getting data from React internals and visualizing it (a lot of things to do), showing what this can give us as developers of React apps. I am aware that React internals based solutions may break in future React releases. But I believe this can be fixed, or new versions of React can be released so that they can reproduce the same functionality, if that functionality is of value to developers. I get positive feedback from developers who were able to find and fix problems in their applications using the features described above. Other tools did not help with this. I think this is a good sign. Once I done with my experiments, I'm going to describe which things might be changed in React to make data gathering simpler (without hacks) or just possible. For now you can take a look at source of RTT (mostly in those modules: one, two) to see which approaches I used to make features described above possible. |
I met with @lahmatiy this afternoon to chat about his project react-render-tracker. Here are a few pain points mentioned during the meeting:
This is an umbrella issue for some ideas I wrote down during the discussion that might be good additions to the React DevTools/Profiler. The items listed below may be worked on independently– and some may turn out to be not worth doing (but they seem worth discussing and considering).
Legacy profiler
Track changed values
The Profiler currently has an opt-in setting for detecting why something re-rendered, but all this does is show the name of the state/prop that changed. Should we also add a setting to display the changed value?
We shied away from doing this for a long time because:
Maybe there are some ways to address the above concerns though? For instance, we could reduce the amount of data we needed to eagerly serialize by:
Perhaps this, combined with making the setting opt-in, would enable us to add this feature.
Explicitly display unmounts
The DevTools profiler only displays components that were committed (e.g. visible on the screen at the point in time when work was committed), but React also spends time unmounting components– and perhaps an unmount isn't expected in some cases (e.g. when a wrapper object is added and React deeply unmounts and remounts a tree).
Should we show some sort of rollup (e.g. at the commit level) for components that were un-mounted too? This way they wouldn't be invisible an easy to overlook.
Viewing changes for a subtree across time
The Components tab allows you to explore the entire application tree, or double-click to drill into what we call the "owners tree"– the list of things rendered by a particular component (the things it "owns").
Maybe the Profiler could provide a similar method to drill into a component (e.g. double-clicking it) to then provide a snapshot of how that component changed over time? This might make it easier to discover things like unmounts/remounts within a known scope. (Or maybe we could also show other component-level summary stats when in this mode?)
Scheduling profiler
Explicit unmount/mount markers
Should we add component un-mounts/mounts as explicit marks in the new profiler? Profiling tools currently focus on the time spent rendering (or perhaps mounting) a component, but unmounts are somewhat hidden. (Perhaps more importantly, unexpected remounts are not highlighted enough.)
The text was updated successfully, but these errors were encountered: