{"id":1607,"date":"2016-09-21T20:05:25","date_gmt":"2016-09-22T03:05:25","guid":{"rendered":"http:\/\/engineering.siftscience.com\/?p=1607"},"modified":"2023-01-06T13:14:14","modified_gmt":"2023-01-06T21:14:14","slug":"browser-dgaf-use-react-pt-2-flipping-react","status":"publish","type":"post","link":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/","title":{"rendered":"Browser DGAF (that you use React) Pt. 2: FLIPping in React"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">In the <a href=\"https:\/\/blog.siftscience.com\/2016\/browser-dgaf-that-you-use-react\/\" target=\"_blank\" rel=\"noopener\">first post in this series<\/a><\/span><span style=\"font-weight: 400;\">, we looked at how coding within the React paradigm could lead to poor browser performance. We fixed a couple of those issues in the context of an auto-height, slide-down animation, but let\u2019s look at the timeline we finished with:<\/span><\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1761\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg\" alt=\"post-slidable-update-prod\" width=\"100%\" \/><\/p>\n<p><span style=\"font-weight: 400;\">As you can see, we\u2019re busting a lot of frames with some heavy style recalculations and layouts, and we\u2019re definitely not hitting 60fps. The reason for this is that we\u2019re still animating the height property, and the height property changes the position of all of its neighbors. This means that in <\/span><i><span style=\"font-weight: 400;\">every frame<\/span><\/i><span style=\"font-weight: 400;\">, the browser has to recalculate styles and positioning for all surrounding elements (and, in reality, every element on the page\u2014until we have <a href=\"https:\/\/drafts.csswg.org\/css-containment\/\" target=\"_blank\" rel=\"noopener\">CSS containment<\/a><\/span><span style=\"font-weight: 400;\">). \u00a0Hence we\u2019re not making 60fps\u2014our work per frame is just too much. You can see an example of the janky animation in <a href=\"https:\/\/codepen.io\/noahgrant\/pen\/akaraR\" target=\"_blank\" rel=\"noopener\">this Codepen<\/a>:<\/span><\/p>\n<p class=\"codepen\" data-height=\"500\" data-theme-id=\"0\" data-slug-hash=\"akaraR\" data-default-tab=\"result\" data-user=\"noahgrant\" data-embed-version=\"2\" data-preview=\"true\">See the Pen <a href=\"https:\/\/codepen.io\/noahgrant\/pen\/akaraR\/\">Slidable, no-FLIP, React<\/a> by Noah Grant (<a href=\"https:\/\/codepen.io\/noahgrant\">@noahgrant<\/a>) on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<\/p>\n<p><script async src=\"\/\/assets.codepen.io\/assets\/embed\/ei.js\"><\/script><\/p>\n<p><span style=\"font-weight: 400;\">But how do we make a slide-down animation without changing height?\u00a0<\/span><span style=\"font-weight: 400;\">We <a href=\"https:\/\/aerotwist.com\/blog\/flip-your-animations\/\" target=\"_blank\" rel=\"noopener\">FLIP<\/a><\/span><span style=\"font-weight: 400;\">\u00a0it!<\/span><\/p>\n<h2><\/h2>\n<h2><strong>FLIP<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\">FLIP stands for <\/span><b>First, Last, Invert, and Play<\/b><span style=\"font-weight: 400;\">, and it is a technique for making animations using CSS transforms instead. Transforms, which run through the compositor, don\u2019t affect style or flow properties of any neighboring elements, drastically reducing the amount of work the browser has to do per frame. Here\u2019s how we use transforms to our advantage with FLIP:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><b>F:<\/b> We calculate the initial position of the element we want to animate<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><b>L:<\/b> We move the element to its final position and measure its position<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><b>I:<\/b> We apply <\/span><i><span style=\"font-weight: 400;\">a negative transform equal to the difference between the two<\/span><\/i><span style=\"font-weight: 400;\">, which effectively makes it look like the element <\/span><i><span style=\"font-weight: 400;\">hasn\u2019t moved at all<\/span><\/i><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><b>P:<\/b> We remove the negative transform, which, if the element has a CSS transform transition applied, will animate gracefully to its final position<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">In the process, we immediately render the accordion body in its entirety and bump everything below it into its own layer to be handled by the GPU. So it kinda looks like this:<\/span><\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1766\" style=\"width: 80%; display: block; margin: 0 auto;\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/accordian-blog-post.jpg\" alt=\"accordian-blog-post\" width=\"80%\" \/><\/p>\n<p><span style=\"font-weight: 400;\">For more on FLIP, read it straight from its creator, Paul Lewis: <\/span><a href=\"https:\/\/aerotwist.com\/blog\/flip-your-animations\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/aerotwist.com\/blog\/flip-your-animations<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<h2><strong>An aside on the slide<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\">Before looking at how to FLIP a slide-down animation such as the one in the CodePen, one thing to note is that, unlike the height animation, we need to translate more than just our slidable component\u2014since transforms happen outside the flow of the document (which is why we don\u2019t need to perform all those layouts), we also need to translate everything below the slidable. For example, say we had this DOM structure:<\/span><\/p>\n<div class=\"code-container\">\n<pre class=\"prettyprint\"><span style=\"font-weight: 400;\">&lt;section&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;div class=\"slidable\"&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0&lt;div class=\"this-is-the-thing-to-slide-up-and-down\"&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;p&gt;some content&lt;\/p&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;div class=\"a-sibling\"&gt;&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;div class=\"another-sibling\"&gt;&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;div class=\"bottom-part-of-the-section\"&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;!-- \u2026 --&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\">&lt;\/section&gt;\r\n<\/span><\/pre>\n<\/div>\n<p><span style=\"font-weight: 400;\">We would need to translate not just <\/span><span style=\"font-weight: 400;\"><code>.this-is-the-thing-to-slid-up-and-down<\/code><\/span><span style=\"font-weight: 400;\">, but also <\/span><span style=\"font-weight: 400;\"><code>.a-sibling<\/code><\/span><span style=\"font-weight: 400;\">, <\/span><span style=\"font-weight: 400;\"><code>.another-sibling<\/code><\/span><span style=\"font-weight: 400;\">, and <\/span><span style=\"font-weight: 400;\"><code>.bottom-part-of-the-section<\/code><\/span><span style=\"font-weight: 400;\">. In React, this means manipulating elements from within the <code>Slidable<\/code> component that are <\/span><i><span style=\"font-weight: 400;\">not within the <code>Slidable<\/code>\u2019s component tree<\/span><\/i><span style=\"font-weight: 400;\">. Normally, React would control any style transformations, but controlling styles outside of a component\u2019s tree is not something it\u2019s well-equipped to do. So, to make this work, we once again have to go outside of the React paradigm.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Because Browser DGAF that you use React.<\/span> <\/span><\/p>\n<h2><strong>FLIP in the real (React) world<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\">So, taking the <code>Slidable<\/code> we ended with in the <a href=\"https:\/\/blog.siftscience.com\/2016\/browser-dgaf-that-you-use-react\/\" target=\"_blank\" rel=\"noopener\">previous blog post<\/a>, we literally pass in the selector string for all DOM elements we want to translate as a prop:<\/span><\/p>\n<div class=\"code-container\">\n<pre class=\"prettyprint\"><span style=\"font-weight: 400;\">&lt;section&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;Slidable<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0FLIPSelector='.a-sibling, .another-sibling, .bottom-part-of-the-section'<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0updateTriggerCondition={this.state.myCondition}<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0{this.state.myCondition ? (<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;div className='this-is-the-thing-to-slide-up-and-down'&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;p&gt;some content&lt;\/p&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0) : null}<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;\/Slidable&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;div className='a-sibling'&gt;&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;div className='another-sibling'&gt;&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;div className='bottom-part-of-the-section'&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;!-- \u2026 --&gt;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0&lt;\/div&gt;<\/span>\r\n<span style=\"font-weight: 400;\">&lt;\/section&gt;\r\n<\/span><\/pre>\n<\/div>\n<p><span style=\"font-weight: 400;\">Our <code>Slidable<\/code>\u2019s <\/span><span style=\"font-weight: 400;\"><code>componentDidUpdate<\/code><\/span><span style=\"font-weight: 400;\"> and <\/span><span style=\"font-weight: 400;\"><code>onTransitionEnd<\/code> <\/span><span style=\"font-weight: 400;\">now look like this:<\/span><\/p>\n<div class=\"code-container\">\n<pre class=\"prettyprint\"><span style=\"font-weight: 400;\">\/\/ ...<\/span>\r\n<span style=\"font-weight: 400;\">componentDidUpdate(prevProps) {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0var containerEl = React.findDOMNode(this.refs.container),<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0contentHeight,<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0translateHeight,<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FLIPEls;<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0if (this.props.updateTriggerCondition !== prevProps.updateTriggerCondition) {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\/\/ this.state.prevHeight is First. here\u2019s our Last<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0contentHeight = React.findDOMNode(this.refs.content).offsetHeight;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\/\/ this is how much we are going to Invert by<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0translateHeight = this.state.prevHeight - contentHeight;<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0if (contentHeight !== this.state.prevHeight &amp;&amp; !this.state.transitioning) {<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.setState({<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0height: !this.props.FLIPSelector ? this.state.prevHeight : null,<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0transitioning: true<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0}, () =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (this.props.FLIPSelector) {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FLIPEls = [...document.querySelectorAll(this.props.FLIPSelector)];<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FLIPEls.forEach((el) =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.position = 'relative';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.transform = `translateY(${translateHeight}px)`;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.willChange = 'transform';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.zIndex = 100000;<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0containerEl.style.overflowY = 'visible';<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ sometimes the first raf called will be invoked in the same call<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ stack, which won't trigger an animation. \u00a0<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window.requestAnimationFrame(() =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FLIPEls.forEach((el) =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ and this is where we Play!<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.transform = 'none';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.transition = `transform ${this.props.transitionDuration}ms`;<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window.setTimeout(this.onTransitionEnd, this.props.transitionDuration);<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} else {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ by default we just animate height as before<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window.requestAnimationFrame(() =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window.setTimeout(this.onTransitionEnd, this.props.transitionDuration);<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.setState({height: contentHeight});<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0});<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0}<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0}<\/span>\r\n<span style=\"font-weight: 400;\">}<\/span>\r\n\r\n<span style=\"font-weight: 400;\">onTransitionEnd() {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0var childrenNumberToRemove = (this._isPreviousChildrenSecond()) ? 1 : 0,<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0containerEl = React.findDOMNode(this.refs.container);<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0if (this.props.FLIPSelector) {<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\/\/ unset all changed CSS on any of the FLIPped elements<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0  [...document.querySelectorAll(this.props.FLIPSelector)].forEach((el) =&gt; {<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0el.style.position = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0el.style.transform = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0el.style.transition = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0el.style.willChange = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0el.style.zIndex = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0});<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0containerEl.style.overflowY = '';<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400;\"> \u00a0this.setState({<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0[`children${childrenNumberToRemove}`]: null,<\/span>\r\n<span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0height: null<\/span>\r\n<span style=\"font-weight: 400;\">\u00a0\u00a0}, this.props.onChangeHeight);<\/span>\r\n<span style=\"font-weight: 400;\">}\r\n<\/span>\r\n<span style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">\/\/ ...<\/span>\r\n<\/span><\/pre>\n<\/div>\n<p><span style=\"font-weight: 400;\">The code isn\u2019t very \u2018React-y\u2019, but the result sure is pretty! Take a look: <\/span><\/p>\n<p class=\"codepen\" data-height=\"500\" data-theme-id=\"0\" data-slug-hash=\"ZOrgYr\" data-default-tab=\"result\" data-user=\"noahgrant\" data-embed-version=\"2\" data-preview=\"true\">See the Pen <a href=\"https:\/\/codepen.io\/noahgrant\/pen\/ZOrgYr\/\">React FLIP<\/a> by Noah Grant (<a href=\"https:\/\/codepen.io\/noahgrant\">@noahgrant<\/a>) on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<\/p>\n<p><script async src=\"\/\/assets.codepen.io\/assets\/embed\/ei.js\"><\/script><\/p>\n<p><span style=\"font-weight: 400;\">So <a href=\"https:\/\/www.theonion.com\/personalities\/smoove-b-1022\" target=\"_blank\" rel=\"noopener\">smoove<\/a><\/span><span style=\"font-weight: 400;\">! Here&#8217;s our new, much-improved timeline:<\/span><\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-1759\" src=\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/FLIP_timeline.jpg\" alt=\"60 FPS Timeline\" width=\"100%\" \/><\/p>\n<h2><\/h2>\n<h2><strong>Conclusion<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\">We can take this even further and implement FLIPping for a list of accordions as shown in the graphic above, like an FAQ section or the <a href=\"https:\/\/demo.siftscience.com\/console\/developer\/api-logs\" target=\"_blank\" rel=\"noopener\">API logs<\/a><\/span><span style=\"font-weight: 400;\">\u00a0in our console. In this case, we want to translate just a select portion of the selectors we pass to <code>&lt;Slidable \/&gt;.<\/code> But I\u2019ll leave that as an exercise for the reader.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A HUGE thank you to Paul Lewis for creating and championing this idea! I <\/span><i><span style=\"font-weight: 400;\">highly<\/span><\/i><span style=\"font-weight: 400;\"> recommend watching his talks on FLIP and <a href=\"https:\/\/www.youtube.com\/watch?v=uJMA2n4RL6s\" target=\"_blank\" rel=\"noopener\">RAIL<\/a><\/span><span style=\"font-weight: 400;\">, if for no other reason than to giggle at the way he says \u201cschedule\u201d (he\u2019s British).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Until next time, remember, as always: Browser DGAF.<\/span><br \/>\n<script src=\"https:\/\/cdn.rawgit.com\/google\/code-prettify\/master\/loader\/run_prettify.js\" async><\/script><\/p>\n","protected":false},"excerpt":{"rendered":"In the first post in this series, we looked at how coding within the React paradigm could lead to poor browser performance. We fixed a couple of those issues in the context of an auto-height, slide-down animation, but let\u2019s look at the timeline we finished with: As you can see, we\u2019re busting a lot of [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[25,20,26,27,28,11,12,29],"ppma_author":[142],"class_list":["post-1607","post","type-post","status-publish","format-standard","hentry","category-articles","tag-accordion","tag-browser-dgaf","tag-css-animation","tag-flip","tag-frontend-performance","tag-javascript","tag-react","tag-react-flip-accordion"],"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) Pt. 2: FLIPping in 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-use-react-pt-2-flipping-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) Pt. 2: FLIPping in React - Sift Engineering Blog\" \/>\n<meta property=\"og:description\" content=\"In the first post in this series, we looked at how coding within the React paradigm could lead to poor browser performance. We fixed a couple of those issues in the context of an auto-height, slide-down animation, but let\u2019s look at the timeline we finished with: As you can see, we\u2019re busting a lot of [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/\" \/>\n<meta property=\"og:site_name\" content=\"Sift Engineering Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-09-22T03:05:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-01-06T21:14:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/engineering.sift.com\/wp-content\/uploads\/2016\/03\/React-ll.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=\"6 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-use-react-pt-2-flipping-react\/\",\"url\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/\",\"name\":\"Browser DGAF (that you use React) Pt. 2: FLIPping in React - Sift Engineering Blog\",\"isPartOf\":{\"@id\":\"https:\/\/engineering.sift.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg\",\"datePublished\":\"2016-09-22T03:05:25+00:00\",\"dateModified\":\"2023-01-06T21:14:14+00:00\",\"author\":{\"@id\":\"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e\"},\"breadcrumb\":{\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage\",\"url\":\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg\",\"contentUrl\":\"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-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) Pt. 2: FLIPping in 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) Pt. 2: FLIPping in 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-use-react-pt-2-flipping-react\/","og_locale":"en_US","og_type":"article","og_title":"Browser DGAF (that you use React) Pt. 2: FLIPping in React - Sift Engineering Blog","og_description":"In the first post in this series, we looked at how coding within the React paradigm could lead to poor browser performance. We fixed a couple of those issues in the context of an auto-height, slide-down animation, but let\u2019s look at the timeline we finished with: As you can see, we\u2019re busting a lot of [&hellip;]","og_url":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/","og_site_name":"Sift Engineering Blog","article_published_time":"2016-09-22T03:05:25+00:00","article_modified_time":"2023-01-06T21:14:14+00:00","og_image":[{"width":900,"height":745,"url":"https:\/\/engineering.sift.com\/wp-content\/uploads\/2016\/03\/React-ll.jpg","type":"image\/jpeg"}],"author":"Sift","twitter_card":"summary_large_image","twitter_misc":{"Written by":"sifteng","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/","url":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/","name":"Browser DGAF (that you use React) Pt. 2: FLIPping in React - Sift Engineering Blog","isPartOf":{"@id":"https:\/\/engineering.sift.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage"},"image":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg","datePublished":"2016-09-22T03:05:25+00:00","dateModified":"2023-01-06T21:14:14+00:00","author":{"@id":"https:\/\/engineering.sift.com\/#\/schema\/person\/423fc55f41d708d384561eaa4a9c126e"},"breadcrumb":{"@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-react\/#primaryimage","url":"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg","contentUrl":"https:\/\/engineering.siftscience.com\/wp-content\/uploads\/2016\/09\/post-slidable-update-prod-1.jpg"},{"@type":"BreadcrumbList","@id":"https:\/\/engineering.sift.com\/browser-dgaf-use-react-pt-2-flipping-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) Pt. 2: FLIPping in 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\/1607","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=1607"}],"version-history":[{"count":0,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/posts\/1607\/revisions"}],"wp:attachment":[{"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/media?parent=1607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/categories?post=1607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/tags?post=1607"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/engineering.sift.com\/wp-json\/wp\/v2\/ppma_author?post=1607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}