{"id":299,"date":"2016-03-16T17:58:47","date_gmt":"2016-03-16T17:58:47","guid":{"rendered":"http:\/\/biz197.inmotionhosting.com\/~blogsi8\/2016\/03\/16\/2016browser-dgaf-that-you-use-react\/"},"modified":"2023-01-06T13:14:53","modified_gmt":"2023-01-06T21:14:53","slug":"browser-dgaf-that-you-use-react","status":"publish","type":"post","link":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/","title":{"rendered":"Browser DGAF (that you use React)"},"content":{"rendered":"<h2>Adventures in React Performance Debugging<\/h2>\n<p>Recently I read Benchling\u2019s <a href=\"https:\/\/benchling.engineering\/performance-engineering-with-react\/\">2-part series<\/a> in debugging performance issues in React, and it really echoed the issues and solutions that I\u2019ve been working through on the <a href=\"https:\/\/demo.siftscience.com\/console\">Sift Science Console<\/a>. So I was inspired to chime in with some of my own React performance debugging experiences in what may become a short series itself. The first theme that I\u2019d like to touch on is that although we love using React for its one-way data flow, its easy architecture, and its ever-enlarging community, the browser really doesn\u2019t give a &amp;^@# what you use. You still need to play nicely within it, even if that means not doing things the <a href=\"https:\/\/facebook.github.io\/react\/docs\/thinking-in-react.html\">\u2018React way\u2019<\/a> in your components. In this post, I\u2019ll highlight how a previous version one of our components that was very clean and concise introduced some subtle performance issues, and how an uglier solution became, in fact, the better one. Because browsers <a href=\"https:\/\/twitter.com\/janicedgafuck\">DGAF<\/a>.<\/p>\n<h2 id=\"slidable\">Slidable<\/h2>\n<p>Our slidable component is responsible for taking content of unknown (auto) height and revealing it with a slide-down animation. As it gets new content, it slides to the new content\u2019s measured, but still auto, height. Here\u2019s a simplified version:<\/p>\n<div class=\"code-container\">\n<pre class=\"source-code prettyprint\"><span class=\"cm-keyword\">import<\/span> <span class=\"cm-variable\">_<\/span> <span class=\"cm-keyword\">from<\/span> <span class=\"cm-string\">'underscore'<\/span>;\r\n<span class=\"cm-keyword\">import<\/span> <span class=\"cm-variable\">React<\/span> <span class=\"cm-keyword\">from<\/span> <span class=\"cm-string\">'react'<\/span>;\r\n\r\n<span class=\"cm-keyword\">const<\/span> <span class=\"cm-variable\">propTypes<\/span> <span class=\"cm-operator\">=<\/span> {\r\n  <span class=\"cm-comment\">\/** Function to be called after the slider finishes its animation *\/<\/span>\r\n  <span class=\"cm-property\">onChangeHeight<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">PropTypes<\/span>.<span class=\"cm-property\">func<\/span>,\r\n  <span class=\"cm-comment\">\/**<\/span>\r\n<span class=\"cm-comment\">   * Since Slidable takes in children, it's really hard to tell when the<\/span>\r\n<span class=\"cm-comment\">   * children have changed. This prop helps determine when to update the <\/span>\r\n<span class=\"cm-comment\">   * component<\/span>\r\n<span class=\"cm-comment\">   *\/<\/span>\r\n   <span class=\"cm-property\">updateTriggerCondition<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">PropTypes<\/span>.<span class=\"cm-property\">any<\/span>,\r\n   <span class=\"cm-comment\">\/** The duration the height change should take in ms. Default 200. *\/<\/span>\r\n   <span class=\"cm-property\">transitionDuration<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">PropTypes<\/span>.<span class=\"cm-property\">number<\/span>\r\n};\r\n\r\n<span class=\"cm-keyword\">export<\/span> <span class=\"cm-keyword\">default<\/span> <span class=\"cm-keyword\">class<\/span> <span class=\"cm-variable\">Slidable<\/span> <span class=\"cm-keyword\">extends<\/span> <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">Component<\/span> {\r\n  <span class=\"cm-variable\">constructor<\/span>() {\r\n    <span class=\"cm-keyword\">super<\/span>();\r\n    \r\n    <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span> <span class=\"cm-operator\">=<\/span> {\r\n      <span class=\"cm-comment\">\/** {number} - The pixel height of the container. Or `null` to set the height to auto. *\/<\/span>\r\n      <span class=\"cm-property\">height<\/span>: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-comment\">\/**<\/span>\r\n<span class=\"cm-comment\">       * {React.Children} - reference to the previous children so that we can show them as the next transition occurs.<\/span>\r\n<span class=\"cm-comment\">       *\/<\/span>\r\n      <span class=\"cm-property\">prevChildren<\/span>: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-comment\">\/**<\/span>\r\n<span class=\"cm-comment\">        * {number} - The previous pixel height of the container, used to determine when to update.<\/span>\r\n<span class=\"cm-comment\">        *    To minimize forced layouts, we saved the current height as the previous height after<\/span>\r\n<span class=\"cm-comment\">        *    that height's animation.<\/span>\r\n<span class=\"cm-comment\">        *\/<\/span>\r\n      <span class=\"cm-property\">prevHeight<\/span>: <span class=\"cm-number\">0<\/span>,\r\n      <span class=\"cm-comment\">\/** {boolean} - Whether or not the component is in the process of changing height. *\/<\/span>\r\n      <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">false<\/span>\r\n    };\r\n  }\r\n\r\n  <span class=\"cm-variable\">componentWillReceiveProps<\/span>(<span class=\"cm-variable\">nextProps<\/span>) {\r\n    <span class=\"cm-keyword\">if<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span> <span class=\"cm-operator\">!==<\/span> <span class=\"cm-variable\">nextProps<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span>) {\r\n      <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n        <span class=\"cm-property\">prevChildren<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">cloneWithProps<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">children<\/span>),\r\n        <span class=\"cm-property\">prevHeight<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">findDOMNode<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">refs<\/span>.<span class=\"cm-property\">content<\/span>).<span class=\"cm-property\">offsetHeight<\/span>,\r\n        <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">false<\/span>\r\n      });\r\n    }\r\n  }\r\n\r\n  <span class=\"cm-variable\">componentDidUpdate<\/span>() {\r\n    <span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">contentHeight<\/span> <span class=\"cm-operator\">=<\/span> <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">findDOMNode<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">refs<\/span>.<span class=\"cm-property\">content<\/span>).<span class=\"cm-property\">offsetHeight<\/span>;\r\n\r\n    <span class=\"cm-comment\">\/\/ first step in new animation: set height from 'auto' back to a number<\/span>\r\n    <span class=\"cm-comment\">\/\/ so that we have a starting height.<\/span>\r\n    <span class=\"cm-keyword\">if<\/span> (<span class=\"cm-variable\">contentHeight<\/span> <span class=\"cm-operator\">!==<\/span> <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">prevHeight<\/span> <span class=\"cm-operator\">&amp;&amp;<\/span> <span class=\"cm-operator\">!<\/span><span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">transitioning<\/span>) {\r\n      <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n        <span class=\"cm-property\">height<\/span>: <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">prevHeight<\/span>,\r\n        <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">true<\/span>\r\n      }, () <span class=\"cm-operator\">=&gt;<\/span> {\r\n        <span class=\"cm-comment\">\/\/ we adjust height from old height (ie 0) to new height in next frame so that<\/span>\r\n        <span class=\"cm-comment\">\/\/ the browser doesn't batch style changes--otherwise the animation wouldn't trigger<\/span>\r\n        <span class=\"cm-variable\">window<\/span>.<span class=\"cm-property\">requestAnimationFrame<\/span>(() <span class=\"cm-operator\">=&gt;<\/span> {\r\n          <span class=\"cm-comment\">\/\/ use setTimeout because transitionend events fire inconsistently across browsers :(<\/span>\r\n          <span class=\"cm-variable\">window<\/span>.<span class=\"cm-property\">setTimeout<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">onTransitionEnd<\/span>, <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">transitionDuration<\/span>);\r\n          <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({<span class=\"cm-property\">height<\/span>: <span class=\"cm-variable\">contentHeight<\/span>})\r\n        });\r\n      });\r\n    }\r\n  }\r\n\r\n  <span class=\"cm-comment\">\/**<\/span>\r\n<span class=\"cm-comment\">   * Called when the height transition ends on the container. It removes the<\/span>\r\n<span class=\"cm-comment\">   * content from the DOM when the container is done sliding out, and sets<\/span>\r\n<span class=\"cm-comment\">   * the height to 'auto' after the container is done sliding in.<\/span>\r\n<span class=\"cm-comment\">   *\/<\/span>\r\n  <span class=\"cm-variable\">onTransitionEnd<\/span>() {\r\n    <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n      <span class=\"cm-property\">height<\/span>: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-property\">prevChildren<\/span>: <span class=\"cm-atom\">null<\/span>\r\n    }, <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">onChangeHeight<\/span>);\r\n  }\r\n\r\n  <span class=\"cm-variable\">render<\/span>() {\r\n    <span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">contentStyle<\/span> <span class=\"cm-operator\">=<\/span> {\r\n          <span class=\"cm-property\">position<\/span>: (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">height<\/span> <span class=\"cm-operator\">===<\/span> <span class=\"cm-atom\">null<\/span>) <span class=\"cm-operator\">?<\/span> <span class=\"cm-string\">'relative'<\/span> : <span class=\"cm-string\">'absolute'<\/span>\r\n        },\r\n        <span class=\"cm-variable\">containerStyle<\/span> <span class=\"cm-operator\">=<\/span> {\r\n          <span class=\"cm-property\">height<\/span>: (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">height<\/span> <span class=\"cm-operator\">===<\/span> <span class=\"cm-atom\">null<\/span>) <span class=\"cm-operator\">?<\/span> <span class=\"cm-string\">'auto'<\/span> : <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">height<\/span>,\r\n          <span class=\"cm-property\">transition<\/span>: <span class=\"cm-string-2\">`height ${<\/span><span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">transitionDuration<\/span><span class=\"cm-string-2\">}<\/span><span class=\"cm-string-2\">ms`<\/span>\r\n        };\r\n\r\n    <span class=\"cm-keyword\">return<\/span> (\r\n      <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'Slidable'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n        <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'container'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-variable\">containerStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-container'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n          <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'content'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-variable\">contentStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-content'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n            {<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-variable\">props<\/span>.<span class=\"cm-variable\">children<\/span>}\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'previous-children'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n              {<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-variable\">state<\/span>.<span class=\"cm-variable\">prevChildren<\/span>}\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n          <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n        <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n      <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n    );\r\n  }\r\n}\r\n\r\n<span class=\"cm-variable\">Slidable<\/span>.<span class=\"cm-property\">propTypes<\/span> <span class=\"cm-operator\">=<\/span> <span class=\"cm-variable\">propTypes<\/span>;\r\n\r\n<span class=\"cm-comment\">\/\/ use like:<\/span>\r\n<span class=\"cm-comment\">\/\/ &lt;Slidable updateTriggerCondition={someCondition}&gt;<\/span>\r\n<span class=\"cm-comment\">\/\/   &lt;h1&gt;Sift Science Slidables seem super slick!&lt;\/h1&gt;<\/span>\r\n<span class=\"cm-comment\">\/\/ &lt;\/Slidable&gt;<\/span>\r\n<\/pre>\n<\/div>\n<p>Okay, that\u2019s not so bad! A couple notes:<\/p>\n<ol>\n<li>It\u2019s really tough to determine when a component\u2019s children have changed, so we use the <code>updateTriggerCondition<\/code> prop to explicitly tell our Slidable component that it should re-calculate the height of its new children. So every time a new <code>updateTriggerCondition<\/code> is changed, we set the current children as our Slidable state\u2019s <code>prevChildren<\/code> so that the previous children are still visible during the animation to the new children\u2019s height.<\/li>\n<li>The real magic then happens in <code>componentDidUpdate<\/code>: we calculate the new children\u2019s height, set the height state back to the previous children\u2019s height, wait a frame, and then set the new height state, triggering our sliding animation.<\/li>\n<li>In our production component, Slidable optionally uses a CSS animation (default), a requestAnimationFrame javascript animation, or a <a href=\"https:\/\/aerotwist.com\/blog\/flip-your-animations\/\">FLIP<\/a>-style animation, but for simplicity, we\u2019ll just consider the CSS animation here.<\/li>\n<\/ol>\n<p>And here it is in action as the core of our Accordion component, revealing a list of ML signals for a user:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif\" alt=\"Okay, it's a gif, so...not the best quality and probably not at 60fps.\" \/><br \/>\n<sub style=\"display: block; font-size: 12px; text-align: center;\">Okay, it&#8217;s a gif, so&#8230;not the best quality and probably not at 60fps.<\/sub><\/p>\n<p>This feels pretty React-y\u2014we\u2019re cycling through new children as they are passed in and are updating our height accordingly as state. Previous children are also kept ephemerally as state while the height adjusts. It seems to perform pretty well\u2026or does it?<\/p>\n<h2 id=\"devtools-gives-it-to-you-straight\">Devtools gives it to you straight<\/h2>\n<p>A timeline of closing the accordion above looks like this:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1609\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/pre-slidable-update-dev-1-e1472869333134.png\" alt=\"pre-slidable-update-dev\" \/><\/p>\n<p>Huh. That\u2019s a lot of work going on after the initial click. For our users to have a good experience, we should be able to finish all our javascript within 100ms of the click (that\u2019s the R in <a href=\"https:\/\/www.smashingmagazine.com\/2015\/10\/rail-user-centric-model-performance\/\">RAIL<\/a>), and it looks like we\u2019re not close to meeting that right now. Part of the issue is that this is in development, and we\u2019re spending a lot of time validating propTypes &#8211; <a href=\"https:\/\/benchling.engineering\/performance-engineering-with-react\/#proptypes\">something we don\u2019t do in production<\/a>. But what about that animation frame stack trace after the click? And what\u2019s with those <a href=\"https:\/\/developers.google.com\/web\/tools\/chrome-devtools\/profile\/rendering-tools\/analyze-runtime#how-to-identify-layout-bottlenecks\">forced layouts<\/a>?<\/p>\n<p>Well, the problem actually lies within our render method:<\/p>\n<div class=\"code-container\">\n<pre class=\"source-code prettyprint\"><span class=\"cm-comment\">\/\/ ...<\/span>\r\n<span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'Slidable'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n  <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'container'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-property\">containerStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-container'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n    <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'content'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-property\">contentStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-content'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n      {<span class=\"cm-property\">this<\/span>.<span class=\"cm-variable\">props<\/span>.<span class=\"cm-variable\">children<\/span>}\r\n       <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'previous-children'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n         {<span class=\"cm-property\">this<\/span>.<span class=\"cm-variable\">state<\/span>.<span class=\"cm-variable\">prevChildren<\/span>}\r\n       <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n     <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n   <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n<span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n<span class=\"cm-comment\">\/\/ ...<\/span><\/pre>\n<\/div>\n<p>What I didn\u2019t realize was that by rendering <code>this.props.children<\/code> and <code>this.state.prevChildren<\/code> in different parts of the DOM, we <em>actually unmount and then remount them<\/em> as one moves to the other\u2014even though <code>prevChildren<\/code> is fleeting and is the exact same as the <code>children<\/code> being unmounted. For simple children with no or light work in <code>componentWillUnmount\/WillMount\/DidMount<\/code>, this isn\u2019t very noticeable. But in our example above, each ML signal in the list can be drag-and-dropped and thus is absolutely positioned by running a series of <code>.getBoundingClientRect()<\/code>s after mounting. And now that work is being unnecessarily repeated.<\/p>\n<p>The most apparent fix is to keep <code>this.props.children<\/code> in the same DOM position even as it becomes <code>this.state.prevChildren<\/code>, and simply alter the class of their containers (to adjust z-index and positioning). But to do that, we have to keep two sets of children in state as well as keep track of which one is current or which one is previous:<\/p>\n<div class=\"code-container\">\n<pre class=\"source-code prettyprint\"><span class=\"cm-comment\">\/\/ ...<\/span>\r\n<span class=\"cm-keyword\">export<\/span> <span class=\"cm-keyword\">default<\/span> <span class=\"cm-keyword\">class<\/span> <span class=\"cm-variable\">Slidable<\/span> <span class=\"cm-keyword\">extends<\/span> <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">Component<\/span> {\r\n  <span class=\"cm-variable\">constructor<\/span>(<span class=\"cm-variable\">props<\/span>) {\r\n    <span class=\"cm-comment\">\/\/ ...<\/span>\r\n\r\n    <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span> <span class=\"cm-operator\">=<\/span> {\r\n      <span class=\"cm-property\">height<\/span>: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-comment\">\/**<\/span>\r\n<span class=\"cm-comment\">       * {React.Children} - We now cycle through two children, children0 and children1, and<\/span>\r\n<span class=\"cm-comment\">       * update the classname of their container based on which one is currently the previous<\/span>\r\n<span class=\"cm-comment\">       * children. Initially, children0 is current and children1 is previous, which is null.<\/span>\r\n<span class=\"cm-comment\">       * After the transition, we set our previous children position to null.<\/span>\r\n<span class=\"cm-comment\">       *\/<\/span>\r\n      <span class=\"cm-property\">children0<\/span>: <span class=\"cm-variable\">props<\/span>.<span class=\"cm-property\">children<\/span>,\r\n      <span class=\"cm-property\">children1<\/span>: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-comment\">\/** {number} - reference to the which children set are the previous children *\/<\/span>\r\n      <span class=\"cm-property\">previousChildrenPosition<\/span>: <span class=\"cm-number\">1<\/span>,\r\n      <span class=\"cm-property\">prevHeight<\/span>: <span class=\"cm-number\">0<\/span>,\r\n      <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">false<\/span>\r\n    };\r\n  }\r\n\r\n  <span class=\"cm-variable\">componentWillReceiveProps<\/span>(<span class=\"cm-variable\">nextProps<\/span>) {\r\n    <span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">newChildrenNumber<\/span> <span class=\"cm-operator\">=<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">_arePreviousChildrenPositionOne<\/span>()) <span class=\"cm-operator\">?<\/span> <span class=\"cm-number\">1<\/span> : <span class=\"cm-number\">0<\/span>,\r\n        <span class=\"cm-variable\">newPreviousChildrenPosition<\/span> <span class=\"cm-operator\">=<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">_arePreviousChildrenPositionOne<\/span>()) <span class=\"cm-operator\">?<\/span> <span class=\"cm-number\">0<\/span> : <span class=\"cm-number\">1<\/span>;\r\n\r\n    <span class=\"cm-keyword\">if<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span> <span class=\"cm-operator\">!==<\/span> <span class=\"cm-variable\">nextProps<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span>) {\r\n      <span class=\"cm-comment\">\/\/ if previous children is currently child number 2, replace it with new children<\/span>\r\n      <span class=\"cm-comment\">\/\/ which would be `this.props` after update<\/span>\r\n      <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n        [<span class=\"cm-string-2\">`children${<\/span><span class=\"cm-variable\">newChildrenNumber<\/span><span class=\"cm-string-2\">}<\/span><span class=\"cm-string-2\">`<\/span>]: <span class=\"cm-variable\">nextProps<\/span>.<span class=\"cm-property\">children<\/span>,\r\n        <span class=\"cm-property\">previousChildrenPosition<\/span>: <span class=\"cm-variable\">newPreviousChildrenPosition<\/span>,\r\n        <span class=\"cm-property\">prevHeight<\/span>: <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">findDOMNode<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">refs<\/span>.<span class=\"cm-property\">content<\/span>).<span class=\"cm-property\">offsetHeight<\/span>,\r\n        <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">false<\/span>\r\n      });\r\n    } <span class=\"cm-keyword\">else<\/span> {\r\n      <span class=\"cm-comment\">\/\/ we have to update children props every time<\/span>\r\n      <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n        [<span class=\"cm-string-2\">`children${<\/span><span class=\"cm-variable\">newPreviousChildrenPosition<\/span><span class=\"cm-string-2\">}<\/span><span class=\"cm-string-2\">`<\/span>]: <span class=\"cm-variable\">nextProps<\/span>.<span class=\"cm-property\">children<\/span>\r\n      });\r\n    }\r\n  }\r\n\r\n  <span class=\"cm-comment\">\/\/ ...componentDidUpdate stays the same...<\/span>\r\n  \r\n  <span class=\"cm-variable\">onTransitionEnd<\/span>() {\r\n    <span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">childrenNumberToRemove<\/span> <span class=\"cm-operator\">=<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">_arePreviousChildrenPositionOne<\/span>()) <span class=\"cm-operator\">?<\/span> <span class=\"cm-number\">1<\/span> : <span class=\"cm-number\">0<\/span>;\r\n\r\n    <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n      [<span class=\"cm-string-2\">`children${<\/span><span class=\"cm-variable\">childrenNumberToRemove<\/span><span class=\"cm-string-2\">}<\/span><span class=\"cm-string-2\">`<\/span>]: <span class=\"cm-atom\">null<\/span>,\r\n      <span class=\"cm-property\">height<\/span>: <span class=\"cm-atom\">null<\/span>\r\n    }, <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">onChangeHeight<\/span>);\r\n  }\r\n\r\n  <span class=\"cm-variable\">_arePreviousChildrenPositionOne<\/span>() {\r\n    <span class=\"cm-keyword\">return<\/span> <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">previousChildrenPosition<\/span> <span class=\"cm-operator\">===<\/span> <span class=\"cm-number\">1<\/span>;\r\n  }\r\n  \r\n  <span class=\"cm-variable\">render<\/span>() {\r\n    <span class=\"cm-comment\">\/\/ ...<\/span>\r\n\r\n    <span class=\"cm-keyword\">return<\/span> (\r\n      <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'Slidable'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n        <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'container'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-variable\">containerStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-container'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n          <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">ref<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'content'<\/span> <span class=\"cm-variable\">style<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-variable\">contentStyle<\/span>} <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span><span class=\"cm-string\">'slidable-content'<\/span><span class=\"cm-operator\">&gt;<\/span>\r\n            {<span class=\"cm-comment\">\/* treat each child set identically and only edit the classname for each *\/<\/span>}\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-operator\">!<\/span><span class=\"cm-keyword\">this<\/span>.<span class=\"cm-variable\">_arePreviousChildrenPositionOne<\/span>() <span class=\"cm-operator\">?<\/span> <span class=\"cm-string\">'previous-children'<\/span> : <span class=\"cm-string\">''<\/span>}<span class=\"cm-operator\">&gt;<\/span>\r\n              {<span class=\"cm-property\">this<\/span>.<span class=\"cm-variable\">state<\/span>.<span class=\"cm-variable\">children0<\/span>}\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-variable\">div<\/span> <span class=\"cm-variable\">className<\/span><span class=\"cm-operator\">=<\/span>{<span class=\"cm-property\">this<\/span>.<span class=\"cm-variable\">_arePreviousChildrenPositionOne<\/span>() <span class=\"cm-operator\">?<\/span> <span class=\"cm-string\">'previous-children'<\/span> : <span class=\"cm-string\">''<\/span>}<span class=\"cm-operator\">&gt;<\/span>\r\n              {<span class=\"cm-property\">this<\/span>.<span class=\"cm-variable\">state<\/span>.<span class=\"cm-variable\">children1<\/span>}\r\n            <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n          <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n        <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n      <span class=\"cm-operator\">&lt;<\/span><span class=\"cm-string-2\">\/div&gt;<\/span>\r\n    );\r\n  }\r\n}\r\n<span class=\"cm-comment\">\/\/ ...<\/span><\/pre>\n<\/div>\n<p>Not only is this component tougher to read, it feels much further from the React philosophy of minimizing state, since we are never actually using <code>props.children<\/code> as props\u2014-we immediately set them as state. Additionally, and quite confusingly, <code>children0<\/code> and <code>children1<\/code> have no semantic notion about which is the current or previous set of children. But Browser DGAF that we want to use React best practices in our components.<\/p>\n<p><strong>Browser DGAF.<\/strong><\/p>\n<p>Here is our new timeline:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1611\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-e1472869624702.png\" alt=\"post-slidable-update\" \/><\/p>\n<p>No animation frame following the click handler. Beautiful!<\/p>\n<h2 id=\"those-red-triangles-tho\">Those red triangles, tho<\/h2>\n<p>Okay, now how do we also get rid of those forced layouts (denoted by the blocks with the red triangles in the upper right)? This one is easier to debug, because we can see by zooming in that it\u2019s coming directly from the Slidable component itself:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1612\" style=\"max-width: 400px; width: 80%; margin: 0 auto; display: block;\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/layout-closeup.png\" alt=\"layout-closeup\" \/><\/p>\n<p>And that points to the first line in our original <code>componentDidUpdate<\/code>:<\/p>\n<div class=\"code-container\">\n<pre class=\"source-code prettyprint\"><span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">contentHeight<\/span> <span class=\"cm-operator\">=<\/span> <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">findDOMNode<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">refs<\/span>.<span class=\"cm-property\">content<\/span>).<span class=\"cm-property\">offsetHeight<\/span>;<\/pre>\n<\/div>\n<p>which queries the height of the new content div on every update, causing a reflow. This brings me to my second piece of DGAF advice:<\/p>\n<p><strong>Browser DGAF about the DOM manipulation you want to put in your reusable components\u2019 <code>componentDidMount\/Update<\/code> methods.<\/strong><\/p>\n<p>So be careful! You\u2019re never totally sure of all the ways these components will be used, and they can cause significant lags in performance. This one above was only 5ms, but there are two of them, and we actually have 10 of these ML-signal accordions per page. All of a sudden that\u2019s an extra 100ms. Oops. What happens when the layout takes 20ms instead?<\/p>\n<p>Wrapping that <code>offsetHeight<\/code> call in a conditional that only executes when <code>updateTriggerCondition<\/code> changes takes care of the second forced layout, and wrapping it in a requestAnimationFrame takes care of the first. Our very pyramid-y <code>componentDidUpdate<\/code> now looks like this:<\/p>\n<div class=\"code-container\">\n<pre class=\"source-code prettyprint\">  <span class=\"cm-comment\">\/\/ ...<\/span>\r\n  <span class=\"cm-variable\">componentDidUpdate<\/span>(<span class=\"cm-variable\">prevProps<\/span>) {\r\n    <span class=\"cm-keyword\">var<\/span> <span class=\"cm-variable\">contentHeight<\/span>;\r\n\r\n    <span class=\"cm-keyword\">if<\/span> (<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span> <span class=\"cm-operator\">!==<\/span> <span class=\"cm-variable\">prevProps<\/span>.<span class=\"cm-property\">updateTriggerCondition<\/span>) {\r\n      <span class=\"cm-variable\">window<\/span>.<span class=\"cm-property\">requestAnimationFrame<\/span>(() <span class=\"cm-operator\">=&gt;<\/span> {\r\n        <span class=\"cm-variable\">contentHeight<\/span> <span class=\"cm-operator\">=<\/span> <span class=\"cm-variable\">React<\/span>.<span class=\"cm-property\">findDOMNode<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">refs<\/span>.<span class=\"cm-property\">content<\/span>).<span class=\"cm-property\">offsetHeight<\/span>;\r\n\r\n        <span class=\"cm-keyword\">if<\/span> (<span class=\"cm-variable\">contentHeight<\/span> <span class=\"cm-operator\">!==<\/span> <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">prevHeight<\/span> <span class=\"cm-operator\">&amp;&amp;<\/span> <span class=\"cm-operator\">!<\/span><span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">transitioning<\/span>) {\r\n          <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({\r\n            <span class=\"cm-property\">height<\/span>: <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">state<\/span>.<span class=\"cm-property\">prevHeight<\/span>,\r\n            <span class=\"cm-property\">transitioning<\/span>: <span class=\"cm-atom\">true<\/span>\r\n          }, () <span class=\"cm-operator\">=&gt;<\/span> {\r\n            <span class=\"cm-variable\">window<\/span>.<span class=\"cm-property\">requestAnimationFrame<\/span>(() <span class=\"cm-operator\">=&gt;<\/span> {\r\n              <span class=\"cm-variable\">window<\/span>.<span class=\"cm-property\">setTimeout<\/span>(<span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">onTransitionEnd<\/span>, <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">props<\/span>.<span class=\"cm-property\">transitionDuration<\/span>);\r\n              <span class=\"cm-keyword\">this<\/span>.<span class=\"cm-property\">setState<\/span>({<span class=\"cm-property\">height<\/span>: <span class=\"cm-variable\">contentHeight<\/span>})\r\n            });\r\n          });\r\n        }\r\n      });\r\n    }\r\n  }\r\n  <span class=\"cm-comment\">\/\/ ...<\/span><\/pre>\n<\/div>\n<p>Here\u2019s our final timeline\u2014in a production build, just to prove what a difference leaving out prop validation makes:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1761\" src=\"https:\/\/blog.siftscience.com\/wp-content\/uploads\/2016\/08\/post-slidable-update-prod.jpg\" alt=\"post-slidable-update-prod\" width=\"100%\" \/><\/p>\n<p>That click work takes less than 20ms!<\/p>\n<p>By the way, astute readers may see that our frame rate during the animation is well under 60fps. This is partly because, as mentioned above, we\u2019re animating height with CSS, which means heavy repainting (and apparently style updating?) of everything below the expanding div. While not the point of this post, <a href=\"https:\/\/aerotwist.com\/blog\/flip-your-animations\/\">FLIP<\/a>-ing your height animations would easily make this run 60fps. FLIP-ing is also pretty messy in React, and would make a great follow-up post in this series, so stay tuned!<\/p>\n<p>And remember: Browser DGAF.<br \/>\n<script src=\"https:\/\/cdn.rawgit.com\/google\/code-prettify\/master\/loader\/run_prettify.js\" async><\/script><\/p>\n<p class=\"footnote\" style=\"font-size: 12px; text-align: center;\" data-preserve-html-node=\"true\">Love React? Love frontend performance? We love <em data-preserve-html-node=\"true\">you<\/em>. <a href=\"https:\/\/siftscience.com\/jobs\" data-preserve-html-node=\"true\">Come fight fraud with us!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Adventures in React Performance Debugging<\/p><p>Recently I read Benchling\u2019s 2-part series in debugging performance issues in React, and it really echoed the issues and solutions that I\u2019ve been working through on the Sift Science Console.&nbsp;So I was inspired to chime in with some of my own React performance debugging experiences in what may become a short series itself.<\/p>","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[20,21,10,11,12],"ppma_author":[142],"class_list":["post-299","post","type-post","status-publish","format-standard","hentry","category-articles","tag-browser-dgaf","tag-browserdgaf","tag-engineering","tag-javascript","tag-react"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Browser DGAF (that you use React) - Sift Engineering Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Browser DGAF (that you use React) - Sift Engineering Blog\" \/>\n<meta property=\"og:description\" content=\"Adventures in React Performance DebuggingRecently I read Benchling\u2019s 2-part series in debugging performance issues in React, and it really echoed the issues and solutions that I\u2019ve been working through on the Sift Science Console.&nbsp;So I was inspired to chime in with some of my own React performance debugging experiences in what may become a short series itself.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/\" \/>\n<meta property=\"og:site_name\" content=\"Sift Engineering Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-03-16T17:58:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-01-06T21:14:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/engineering.sift.com\/wp-content\/uploads\/2016\/03\/DGAF.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"900\" \/>\n\t<meta property=\"og:image:height\" content=\"745\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Sift\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"sifteng\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/\",\"url\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/\",\"name\":\"Browser DGAF (that you use React) - Sift Engineering Blog\",\"isPartOf\":{\"@id\":\"https:\/\/engineering.sift.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif\",\"datePublished\":\"2016-03-16T17:58:47+00:00\",\"dateModified\":\"2023-01-06T21:14:53+00:00\",\"author\":{\"@id\":\"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e\"},\"breadcrumb\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage\",\"url\":\"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif\",\"contentUrl\":\"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/engineering.sift.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Browser DGAF (that you use React)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/engineering.sift.com\/#website\",\"url\":\"https:\/\/engineering.sift.com\/\",\"name\":\"Sift Engineering Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/engineering.sift.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e\",\"name\":\"sifteng\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/engineering.sift.com\/#\/schema\/person\/image\/11bd71da17ee3700c58d026fcc80fc3a\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/2809d658dcfdf2f48d7d157f9d2579a9627ace0d9059de3a5ca301f7ca0e4f2a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/2809d658dcfdf2f48d7d157f9d2579a9627ace0d9059de3a5ca301f7ca0e4f2a?s=96&d=mm&r=g\",\"caption\":\"sifteng\"},\"sameAs\":[\"https:\/\/engineering.siftscience.com\"],\"url\":\"https:\/\/engineering.sift.com\/author\/sifteng\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Browser DGAF (that you use React) - Sift Engineering Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/","og_locale":"en_US","og_type":"article","og_title":"Browser DGAF (that you use React) - Sift Engineering Blog","og_description":"Adventures in React Performance DebuggingRecently I read Benchling\u2019s 2-part series in debugging performance issues in React, and it really echoed the issues and solutions that I\u2019ve been working through on the Sift Science Console.&nbsp;So I was inspired to chime in with some of my own React performance debugging experiences in what may become a short series itself.","og_url":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/","og_site_name":"Sift Engineering Blog","article_published_time":"2016-03-16T17:58:47+00:00","article_modified_time":"2023-01-06T21:14:53+00:00","og_image":[{"width":900,"height":745,"url":"https:\/\/engineering.sift.com\/wp-content\/uploads\/2016\/03\/DGAF.jpg","type":"image\/jpeg"}],"author":"Sift","twitter_card":"summary_large_image","twitter_misc":{"Written by":"sifteng","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/","url":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/","name":"Browser DGAF (that you use React) - Sift Engineering Blog","isPartOf":{"@id":"https:\/\/engineering.sift.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage"},"image":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage"},"thumbnailUrl":"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif","datePublished":"2016-03-16T17:58:47+00:00","dateModified":"2023-01-06T21:14:53+00:00","author":{"@id":"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e"},"breadcrumb":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#primaryimage","url":"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif","contentUrl":"https:\/\/static1.squarespace.com\/static\/54056f5de4b0f888a3536667\/5408e757e4b05e7de38fb683\/56d62a4d2eeb816ca61da7a0\/1456876622730\/\/img.gif"},{"@type":"BreadcrumbList","@id":"https:\/\/engineering.sift.com\/browser-dgaf-that-you-use-react\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/engineering.sift.com\/"},{"@type":"ListItem","position":2,"name":"Browser DGAF (that you use React)"}]},{"@type":"WebSite","@id":"https:\/\/engineering.sift.com\/#website","url":"https:\/\/engineering.sift.com\/","name":"Sift Engineering Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/engineering.sift.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e","name":"sifteng","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.sift.com\/#\/schema\/person\/image\/11bd71da17ee3700c58d026fcc80fc3a","url":"https:\/\/secure.gravatar.com\/avatar\/2809d658dcfdf2f48d7d157f9d2579a9627ace0d9059de3a5ca301f7ca0e4f2a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2809d658dcfdf2f48d7d157f9d2579a9627ace0d9059de3a5ca301f7ca0e4f2a?s=96&d=mm&r=g","caption":"sifteng"},"sameAs":["https:\/\/engineering.siftscience.com"],"url":"https:\/\/engineering.sift.com\/author\/sifteng\/"}]}},"authors":[{"term_id":142,"user_id":0,"is_guest":1,"slug":"emily","display_name":"Sift","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/453da2c4a48af2508b96ed72085b17566e216270dbc47faab96d44f9f14ccdde?s=96&d=mm&r=g","author_category":"","first_name":"Sift","last_name":"","user_url":"","job_title":"","description":""}],"_links":{"self":[{"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/posts\/299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/comments?post=299"}],"version-history":[{"count":0,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/posts\/299\/revisions"}],"wp:attachment":[{"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/media?parent=299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/categories?post=299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/tags?post=299"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/ppma_author?post=299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}