React is great and fast most of the time. But sometimes, due to heavy calculations, it slows down, that’s when we need to measure and optimize our Components to avoid “wasted renders”. Optimizations come with its cost, if it’s not done properly, the situation might get worse. In today’s blog post, we get to know the rendering process, learn the cause of wasted renders, solutions & how it’s broken. Table of contents Process Overview, Render & Commit Phase What is “Rendering” ?: The cause of wasted renders in Render Phase Standard Render Behavior: Some techniques Improving Rendering Performance: The detail of the problem How New Props References break Optimizations: useMemo & useCallback Optimizing Props References: Memoize Everything? What is “Rendering” ? is the process of React asking your Components to describe what the section UI looks like, on the current combination of Props and State. Rendering Process Overview During the process, React will start at the root of the component tree and loop downwards to find the components flagged as needing updates. For each flagged components, it will call (for class components) or (for function components), and save the render outputs. render() FunctionComponent() A component render’s output is written in . Either from or , the output eventually becomes . These elements are used together to form the virtual tree (tempt tree). JSX render() FunctionComponent() ReactElement After collecting the new tree, React will diff it, collect lists of all changes need to be applied to make the real tree look like the current desired output. This process is called . Reconciliation Above is very basic process to create (the tree output). The host tree can be vary of types, base on different platforms (web, mobile,…). Dan Abramov wrote a great explanation for it Host tree Here . React team divides above work into 2 phases: The “ ” contains all the work of rendering components and calculating changes. Render phase The “ ” is the process of applying those changes to the host tree. Commit phase More layers for React Native (You can skip this and move on if you’re not interested) React Native creates a tree hierarchy to define the initial layout and creates a diff of that tree on each layout change like above. Except React Native manages the UI updates through couple of architecture layers that in the end translate how views should be rendered. 1. The Yoga layout engine is a cross-platform layout engine written in C which implements Flexbox through bindings to the native views . Yoga (Java Android Views / Objective-C iOS UIKit) All the layout calculations of the various views, texts and images in React-Native are done through yoga, this is basically the last step before our views get displayed on the screen 2. Shadow tree/Shadow nodes When react-native sends the commands to render the layout, a group of shadow nodes are assembled to build shadow tree which represented the mutable native side of the layout (i.e: written in the corresponding native respective language, Java for Android and Objective-C for iOS) which is then translated to the actual views on screen (using Yoga). 3. ViewManager The ViewManger is an interface that knows how to translate the View Types sent from the JavaScript into their native UI components. The ViewManager knows how to create a shadow node, a native view node and to update the views. In the React-Native framework, there are a lot of ViewManager that enable the usage of the native components. If for example, you’d someday like to create a new custom view and add it to react-native, that view will have to implement the ViewManager interface 4. UIManager The UIManager is the final piece of the puzzle, or actually the first. The JavaScript JSX declarative commands are sent to the native as Imperative commands that tell React-Native how to layout the views, step by step iteratively. So as a first render, the UIManager will dispatch the command to create the necessary views and will continue to send the update diffs as the UI of the app changes over time. So React Native basically still uses React’s ability to calculate The difference between the previous and the current rendering representation and dispatches the events to the UIManager accordingly. Standard Render Behavior It is important that: React’s default behavior is that when a parent component renders, React will recursively render all child components inside of it! For example, say we have a component tree of A > B > C. We trigger a re-render in ( or ). B setState setter of useState React starts the render pass from the top of the treeReact sees that is not marked as needing an update, and past it A React sees that is marked as needing an update, and renders it. returns as it did last time. B B <C /> was originally marked as needing an update. However, because its parent rendered, React now moves downwards and renders as well. C not B C Now, it’s likely that most of the components will return the exact render result as last time, therefore, React won’t need to make change to the real tree. However, React will still have to ask the components re-render themselves and diff the render output. Both of those take time and effort, especially when the components are large & have heavy calculations. This is how wasted renders happens. Improving Rendering Performance Renders are normal expected part of React. It’s also true that sometimes the effort is wasted if a component’s render output hasn’t changed, and that part of the tree doesn’t need updating. Render should always based on current of component. If we know ahead of time that Props and State won’t change. the render output won’t change, then we can safely skip the rendering process of that component. Props and State When it comes to optimization, you can make it run faster or do less work. Most of React optimization is about doing less work. Remember to measure before any optimization, so you don’t commit premature optimization. React offers three primary APIs for skipping rendering of a component. A component lifecycle happens early in the render process. (happening in updating lifecycle). If return , React will skip rendering component. By default, it always return , so when you need to skip rendering component, you can add your own logic. Commonly, when we custom this lifecycle, we compare the old props, state with the new ones, and return if nothing change. React.Component.shouldComponentUpdate : false true false Since the comparison of props and state is the most common way to implement is a base class implements that behavior by default. Can be used instead of React.PureComponent: shouldComponentUpdate. PureComponent React.Component + shouldComponentUpdate. A built-in It accepts your component and return a new wrapper component. Wrapper component’s default behavior is to check if any props has changed, if not, it prevents rendering. It also accept your custom logic for the comparison work, commonly this is used to compare specific props, instead of all of them. React.memo : higher order component . All of these approaches use a comparison technique called . This means checking individual field in two different objects, and seeing if any difference in the contents of objects. The technique compares with , a simple and fast way JS engine can do. Shallow Equality === How New Props References break Optimizations As we learned about techniques with above, it’s apparent that passing new objects will fail the comparison because “===” compares , even if the contents haven’t changed. That breaks our optimizations, the component still renders, but diffing through props comparison & diffing tree. Be careful ! shallow equality reference wasting more diffing effort, In the example, we pass and as props to . Although we optimize ChildComponent, it still re-render every ParentComponent updating. Because onClick data MemoizedChildComponent MemoizedChildComponent’s props get new objects every time. We expect skip rendering because it’s props contents are the same. Let’s move on and figure how we can fix this. MemoizedChildComponent Optimizing Props References Class components don’t have to worry about accidentally creating new callback object references as much, because they can have instance methods that are always the same reference. However, they may need to generate unique callbacks for separate child list items, or capture a value in an anonymous function and pass that to a child. Which resulting in new objects, React hasn’t come with any built-in to optimize those cases. Function component, React offer two hooks (for callback function) and (for any kind of data like creating objects or complex calculations). useCallback useMemo The intention of this post is to draw the problem out, not teaching about Hook, i believe there are many sources explaining those Hook well. So i’m not going into detail here. Maybe in the next posts, who knows right ^^ Memoize Everything? Apparently , every optimization comes with its cost. Optimizing carelessly ends up making the performance worse, always measure first, by React devtool or any of your favorite, find the bottleneck, and then optimize. NO It’s not always benefit, if it was, React would make it the default implementation, right ? :D Kent C. Dodds mentioned a case when worse . And my favorite Dan’s tweet: useCallback here Why doesn’t React put memo() around every component by default? Isn’t it faster? Should we make a benchmark to check? Ask yourself: Why don’t you put Lodash memoize() around every function? Wouldn’t that make all functions faster? Do we need a benchmark for this? Why not? Summary Well, that’s the end of this post. To summary, Rendering process of React renders children components due to updating parent components, that’s not bad, that’s how React knows the changes. And sometimes the rendering effort is wasted. Skipping rendering is a common way to optimize this, and the work relates to a lot. Optimizing with care, don’t premature optimization. props references Props Reference has more problems than that. Recently, i love Ben’s on how it affects dependencies in useEffect hook. article For any further questions or comments, let me know. Thanks ! Also published on: https://medium.com/javascript-in-plain-english/react-native-why-props-references-break-optimizations-79c463ca0723