One place for hosting & domains

      Performance

      How To Avoid Performance Pitfalls in React with memo, useMemo, and useCallback


      The author selected Creative Commons to receive a donation as part of the Write for DOnations program.

      Introduction

      In React applications, performance problems can come from network latency, overworked APIs, inefficient third-party libraries, and even well-structured code that works fine until it encounters an unusually large load. Identifying the root cause of performance problems can be difficult, but many of these problems originate from component re-rendering. Either the component re-renders more than expected or the component has a data-heavy operation that can cause each render to be slow. Because of this, learning how to prevent unneeded re-renders can help to optimize the performance of your React application and create a better experience for your user.

      In this tutorial, you’ll focus on optimizing performance in React components. To explore the problem, you’ll build a component to analyze a block of text. You’ll look at how different actions can trigger re-renders and how you can use Hooks and memoization to minimize expensive data calculations. By the end of this tutorial, you’ll be familiar with many performance enhancing Hooks, such as the useMemo and useCallback Hook, and the circumstances that will require them.

      Prerequisites

      • You will need a development environment running Node.js; this tutorial was tested on Node.js version 10.22.0 and npm version 6.14.6. To install this on macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Development Environment on macOS or the Installing Using a PPA section of How To Install Node.js on Ubuntu 18.04.

      • A React development environment set up with Create React App, with the non-essential boilerplate removed. To set this up, follow Step 1 — Creating an Empty Project of the How To Manage State on React Class Components tutorial. This tutorial will use performance-tutorial as the project name.

      • If you are new to debugging in React, check out the tutorial How To Debug React Components Using React Developer Tools, and familiarize yourself with the developer tools in the browser you are using, such as Chrome DevTools and Firefox Developer Tools.

      • You will also need a basic knowledge of JavaScript, HTML, and CSS, which you can find in our How To Build a Website With HTML series, How To Build a Website With CSS series, and in How To Code in JavaScript.

      Step 1 — Preventing Re-renders with memo

      In this step, you’ll build a text analyzing component. You’ll create an input to take a block of text and a component that will calculate the frequency of letters and symbols. You’ll then create a scenario where the text analyzer performs poorly and you’ll identify the root cause of the performance problem. Finally, you’ll use the React memo function to prevent re-renders on the component when a parent changes, but the props to the child component do not change.

      By the end of this step, you’ll have a working component that you’ll use throughout the rest of the tutorial and an understanding of how parent re-rendering can create performance problems in child components.

      Building a Text Analyzer

      To begin, add a <textarea> element to App.js.

      Open App.js in a text editor of your choice:

      • nano src/components/App/App.js

      Then add a <textarea>input with a <label>. Place the label inside a <div> with a className of wrapper by adding the following highlighted code:

      performance-tutorial/src/components/App/App.js

      import React from 'react';
      import './App.css';
      
      function App() {
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Add Your Text Here:</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
              >
              </textarea>
            </label>
          </div>
        )
      }
      
      export default App;
      

      This will create an input box for the sample application. Save and close the file.

      Open App.css to add some styles:

      • nano src/components/App/App.css

      Inside App.css, add padding to the .wrapper class, then add a margin to the div elements. Replace the CSS with the following:

      performance-tutorial/src/components/App/App.css

      
      .wrapper {
          padding: 20px;
      }
      
      .wrapper div {
          margin: 20px 0;
      }
      

      This will add separation between the input and the data display. Save and close the file.

      Next, create a directory for the CharacterMap component. This component will analyze the text, calculate the frequency of each letter and symbol, and display the results.

      First, make the directory:

      • mkdir src/components/CharacterMap

      Then open CharacterMap.js in a text editor:

      • nano src/components/CharacterMap/CharacterMap.js

      Inside, create a component called CharacterMap that takes text as a prop and displays the text inside a <div>:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React from 'react';
      import PropTypes from 'prop-types';
      
      export default function CharacterMap({ text }) {
        return(
          <div>
            Character Map:
            {text}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        text: PropTypes.string.isRequired
      }
      

      Notice that you are adding a PropType for the text prop to introduce some weak typing.

      Add a function to loop over the text and extract the character information. Name the function itemize and pass the text as an argument. The itemize function is going to loop over every character several times and will be very slow as the text size increases. This will make it a good way to test performance:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        const letters = text.split('')
          .filter(l => l !== ' ')
          .reduce((collection, item) => {
            const letter = item.toLowerCase();
            return {
              ...collection,
              [letter]: (collection[letter] || 0) + 1
            }
          }, {})
        return letters;
      }
      
      export default function CharacterMap({ text }) {
        return(
          <div>
            Character Map:
            {text}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        text: PropTypes.string.isRequired
      }
      

      Inside itemize, you convert the text into an array by using .split on every character. Then you remove the spaces using the .filter method and use the .reduce method to iterate over each letter. Inside the .reduce method, use an object as the initial value, then normalize the character by converting it to lower case and adding 1 to the previous total or 0 if there was no previous total. Update the object with the new value while preserving previous values using the Object spread operator.

      Now that you have created an object with a count for each character, you need to sort it by the highest character. Convert the object to an array of pairs with Object.entries. The first item in the array is the character and the second item is the count. Use the .sort method to place the most common characters on top:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        const letters = text.split('')
          .filter(l => l !== ' ')
          .reduce((collection, item) => {
            const letter = item.toLowerCase();
            return {
              ...collection,
              [letter]: (collection[letter] || 0) + 1
            }
          }, {})
        return Object.entries(letters)
          .sort((a, b) => b[1] - a[1]);
      }
      
      export default function CharacterMap({ text }) {
        return(
          <div>
            Character Map:
            {text}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        text: PropTypes.string.isRequired
      }
      

      Finally, call the itemize function with the text and display the results:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      
      import React from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        const letters = text.split('')
          .filter(l => l !== ' ')
          .reduce((collection, item) => {
            const letter = item.toLowerCase();
            return {
              ...collection,
              [letter]: (collection[letter] || 0) + 1
            }
          }, {})
        return Object.entries(letters)
          .sort((a, b) => b[1] - a[1]);
      }
      
      export default function CharacterMap({ text }) {
        return(
          <div>
            Character Map:
            {itemize(text).map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        text: PropTypes.string.isRequired
      }
      

      Save and close the file.

      Now import the component and render inside of App.js. Open App.js:

      • nano src/components/App/App.js

      Before you can use the component, you need a way to store the text. Import useState then call the function and store the values on a variable called text and an update function called setText.

      To update the text, add a function to onChange that will pass the event.target.value to the setText function:

      performance-tutorial/src/components/App/App.js

      import React, { useState } from 'react';
      import './App.css';
      
      function App() {
        const [text, setText] = useState('');
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
          </div>
        )
      }
      
      export default App;
      

      Notice that you are initializing useState with an empty string. This will ensure that the value you pass to the CharacterMap component is always a string even if the user has not yet entered text.

      Import CharacterMap and render it after the <label> element. Pass the text state to the text prop:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { useState } from 'react';
      import './App.css';
      
      import CharacterMap from '../CharacterMap/CharacterMap';
      
      function App() {
        const [text, setText] = useState('');
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
            <CharacterMap text={text} />
          </div>
        )
      }
      
      export default App;
      

      Save the file. When you do, the browser will refresh and when you add text, you’ll find the character analysis after the input:

      Input with results below

      As shown in the example, the component performs fairly well with a small amount of text. With every keystroke, React will update the CharacterMap with new data. But performance locally can be misleading. Not all devices will have the same memory as your development environment.

      Testing Performance

      There are multiple ways to test performance of your application. You can add large volumes of text or you can set your browser to use less memory. To push the component to a performance bottleneck, copy the Wikipedia entry for GNU and paste it in the text box. Your sample may be slightly different depending on how the Wikipedia page is edited.

      After pasting the entry into your text box, try typing the additional letter e and notice how long it takes to display. There will be a significant pause before the character map updates:

      Animation showing the delay when typing

      If the component is not slow enough and you are using Firefox, Edge, or some other browser, add more text until you notice a slowdown.

      If you are using Chrome, you can throttle the CPU inside the performance tab. This is a great way to emulate a smartphone or an older piece of hardware. For more information, check out the Chrome DevTools documentation.

      Performance Throttling in Chrome DevTools

      If the component is too slow with the Wikipedia entry, remove some text. You want to receive a noticable delay, but you do not want to make it unusably slow or to crash your browser.

      Preventing Re-Rendering of Child Components

      The itemize function is the root of the delay identified in the last section. The function does a lot of work on each entry by looping over the contents several times. There are optimizations you can perform directly in the function itself, but the focus of this tutorial is how to handle component re-rendering when the text does not change. In other words, you will treat the itemize function as a function that you do not have access to change. The goal will be to run it only when necessary. This will show how to handle performance for APIs or third-party libraries that you can’t control.

      To start, you will explore a situation where the parent changes, but the child component does not change.

      Inside of App.js, add a paragraph explaining how the component works and a button to toggle the information:

      performance-tutorial/src/components/App/App.js

      import React, { useReducer, useState } from 'react';
      import './App.css';
      
      import CharacterMap from '../CharacterMap/CharacterMap';
      
      function App() {
        const [text, setText] = useState('');
        const [showExplanation, toggleExplanation] = useReducer(state => !state, false)
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
            <div>
              <button onClick={toggleExplanation}>Show Explanation</button>
            </div>
            {showExplanation &&
              <p>
                This displays a list of the most common characters.
              </p>
            }
            <CharacterMap text={text} />
          </div>
        )
      }
      
      export default App;
      

      Call the useReducer Hook with a reducer function to reverse the current state. Save the output to showExplanation and toggleExplanation. After the <label>, add a button to toggle the explanation and a paragraph that will render when showExplanation is truthy.

      Save and exit the file. When the browser refreshes, click on the button to toggle the explanation. Notice how there is a delay.

      Delay when toggling the Explanation

      This presents a problem. Your users shouldn’t encounter a delay when they are toggling a small amount of JSX. The delay occurs because when the parent component changes—App.js in this situation—the CharacterMap component is re-rendering and re-calculating the character data. The text prop is identical, but the component still re-renders because React will re-render the entire component tree when a parent changes.

      If you profile the application with the browser’s developer tools, you’ll discover that the component re-renders because the parent changes. For a review of profiling using the developer tools, check out How To Debug React Components Using React Developer Tools.

      Parent component re-renders in browser developer tools

      Since CharacterMap contains an expensive function, it should only re-render it when the props change.

      Open CharacterMap.js:

      • nano src/components/CharacterMap/CharacterMap.js

      Next, import memo, then pass the component to memo and export the result as the default:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        ...
      }
      
      function CharacterMap({ text }) {
        return(
          <div>
            Character Map:
            {itemize(text).map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        text: PropTypes.string.isRequired
      }
      
      export default memo(CharacterMap);
      

      Save the file. When you do, the browser will reload and there will no longer be a delay after you click the button before you get the result:

      No delay when toggling the explanation in the test app

      If you look at the developer tools, you’ll find that the component no longer re-renders:

      Component did not re-render

      The memo function will perform a shallow comparison of props and will re-render only when the props change. A shallow comparison will use the === operator to compare the previous prop to the current prop.

      It’s important to remember that the comparison is not free. There is a performance cost to check the props, but when you have a clear performance impact such as an expensive calculation, it is worth it to prevent re-renders. Further, since React performs a shallow comparison, the component will still re-render when a prop is an object or a function. You will read more about handling functions as props in Step 3.

      In this step, you created an application with a long, slow calculation. You learned how parent re-rendering will cause a child component to re-render and how to prevent the re-render using memo. In the next step, you’ll memoize actions in a component so that you only perform actions when specific properties change.

      Step 2 — Caching Expensive Data Calculations with useMemo

      In this step, you’ll store the results of slow data calculations with the useMemo Hook. You’ll then incorporate the useMemo Hook in an existing component and set conditions for data re-calculations. By the end of this step, you’ll be able to cache expensive functions so that they will only run when specific pieces of data change.

      In the previous step, the toggled explanation of the component was part of the parent. However, you could instead add it to the CharacterMap component itself. When you do, CharacterMap will have two properties, the text and showExplanation, and it will display the explanation when showExplanation is truthy.

      To start, open CharacterMap.js:

      • nano src/components/CharacterMap/CharacterMap.js

      Inside of CharacterMap, add a new property of showExplanation. Display the explanation text when the value of showExplanation is truthy:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        ...
      }
      
      function CharacterMap({ showExplanation, text }) {
        return(
          <div>
            {showExplanation &&
              <p>
                This display a list of the most common characters.
              </p>
            }
            Character Map:
            {itemize(text).map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        showExplanation: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired
      }
      
      export default memo(CharacterMap);
      

      Save and close the file.

      Next, open App.js:

      • nano src/components/App/App.js

      Remove the paragraph of explanation and pass showExplanation as a prop to CharacterMap:

      performance-tutorial/src/components/App/App.js

      import React, { useReducer, useState } from 'react';
      import './App.css';
      
      import CharacterMap from '../CharacterMap/CharacterMap';
      
      function App() {
        const [text, setText] = useState('');
        const [showExplanation, toggleExplanation] = useReducer(state => !state, false)
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
            <div>
              <button onClick={toggleExplanation}>Show Explanation</button>
            </div>
            <CharacterMap showExplanation={showExplanation} text={text} />
          </div>
        )
      }
      
      export default App;
      

      Save and close the file. When you do, the browser will refresh. If you toggle the explanation, you will again receive the delay.

      Delay when toggling explanation

      If you look at the profiler, you’ll discover that the component re-rendered because the showExplanation prop changed:

      Re-render because prop changed

      The memo function will compare props and prevent re-renders if no props change, but in this case the showExplanation prop does change, so the whole component will re-render and the component will re-run the itemize function.

      In this case, you need to memoize specific parts of the component, not the whole component. React provides a special Hook called useMemo that you can use to preserve parts of your component across re-renders. The Hook takes two arguments. The first argument is a function that will return the value you want to memoize. The second argument is an array of dependencies. If a dependency changes, useMemo will re-run the function and return a value.

      To implement useMemo, first open CharacterMap.js:

      • nano src/components/CharacterMap/CharacterMap.js

      Declare a new variable called characters. Then call useMemo and pass an anonymous function that returns the value of itemize(text) as the first argument and an array containing text as the second argument. When useMemo runs, it will return the result of itemize(text) to the characters variable.

      Replace the call to itemize in the JSX with characters:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo, useMemo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        ...
      }
      
      function CharacterMap({ showExplanation, text }) {
        const characters = useMemo(() => itemize(text), [text]);
        return(
          <div>
            {showExplanation &&
              <p>
                This display a list of the most common characters.
              </p>
            }
            Character Map:
            {characters.map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        showExplanation: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired
      }
      
      export default memo(CharacterMap);
      

      Save the file. When you do, the browser will reload and there will be no delay when you toggle the explanation.

      Animation showing no delay when toggling explanation

      If you profile the component, you will still find that it re-renders, but the time it takes to render will be much shorter. In this example it took .7 milliseconds compared to 916.4 milliseconds without the useMemo Hook. That’s because React is re-rendering the component, but it is not re-running the function contained in the useMemo Hook. You’re able to preserve the result while still allowing other parts of the component to update:

      Developer tools profile showing that the component renders in .7ms

      If you change the text in the textbox, there will still be a delay because the dependency—text—changed, so useMemo will re-run the function. If it did not re-run, you would have old data. The key point is that it only runs when the data it needs changes.

      In this step, you memoized parts of your component. You isolated an expensive function from the rest of the component and used the useMemo Hook to run the function only when certain dependencies change. In the next step, you’ll memoize functions to prevent shallow comparison re-renders.

      Step 3 — Managing Function Equality Checks with useCallback

      In this step, you’ll handle props that are difficult to compare in JavaScript. React uses strict equality checking when props change. This check determines when to re-run Hooks and when to re-render components. Since JavaScript functions and objects are difficult to compare, there are situations where a prop would be effectively the same, but still trigger a re-render.

      You can use the useCallback Hook to preserve a function across re-renders. This will prevent unnecessary re-renders when a parent component recreates a function. By the end of this step, you’ll be able to prevent re-renders using the useCallback Hook.

      As you build your CharacterMap component, you may have a situation where you need it to be more flexible. In the itemize function, you always convert the character to lower case, but some consumers of the component may not want that functionality. They may want to compare upper and lowercase characters or want to convert all characters to upper case.

      To facilitate this, add a new prop called transformer that will change the character. The transformer function will be anything that takes a character as an argument and returns a string of some sort.

      Inside of CharacterMap, add transformer as a prop. Give it a PropType of function with a default of null:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo, useMemo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text){
        const letters = text.split('')
          .filter(l => l !== ' ')
          .reduce((collection, item) => {
            const letter = item.toLowerCase();
            return {
              ...collection,
              [letter]: (collection[letter] || 0) + 1
            }
          }, {})
        return Object.entries(letters)
          .sort((a, b) => b[1] - a[1]);
      }
      
      function CharacterMap({ showExplanation, text, transformer }) {
        const characters = useMemo(() => itemize(text), [text]);
        return(
          <div>
            {showExplanation &&
              <p>
                This display a list of the most common characters.
              </p>
            }
            Character Map:
            {characters.map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        showExplanation: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired,
        transformer: PropTypes.func
      }
      
      CharacterMap.defaultProps = {
        transformer: null
      }
      
      export default memo(CharacterMap);
      

      Next, update itemize to take transformer as an argument. Replace the .toLowerCase method with the transformer. If transformer is truthy, call the function with the item as an argument. Otherwise, return the item:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo, useMemo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text, transformer){
        const letters = text.split('')
          .filter(l => l !== ' ')
          .reduce((collection, item) => {
            const letter = transformer ? transformer(item) : item;
            return {
              ...collection,
              [letter]: (collection[letter] || 0) + 1
            }
          }, {})
        return Object.entries(letters)
          .sort((a, b) => b[1] - a[1]);
      }
      
      function CharacterMap({ showExplanation, text, transformer }) {
          ...
      }
      
      CharacterMap.propTypes = {
        showExplanation: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired,
        transformer: PropTypes.func
      }
      
      CharacterMap.defaultProps = {
        transformer: null
      }
      
      export default memo(CharacterMap);
      

      Finally, update the useMemo Hook. Add transformer as a dependency and pass it to the itemize function. You want to be sure that your dependencies are exhaustive. That means you need to add anything that might change as a dependency. If a user changes the transformer by toggling between different options, you’d need to re-run the function to get the correct value.

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { memo, useMemo } from 'react';
      import PropTypes from 'prop-types';
      
      function itemize(text, transformer){
        ...
      }
      
      function CharacterMap({ showExplanation, text, transformer }) {
        const characters = useMemo(() => itemize(text, transformer), [text, transformer]);
        return(
          <div>
            {showExplanation &&
              <p>
                This display a list of the most common characters.
              </p>
            }
            Character Map:
            {characters.map(character => (
              <div key={character[0]}>
                {character[0]}: {character[1]}
              </div>
            ))}
          </div>
        )
      }
      
      CharacterMap.propTypes = {
        showExplanation: PropTypes.bool.isRequired,
        text: PropTypes.string.isRequired,
        transformer: PropTypes.func
      }
      
      CharacterMap.defaultProps = {
        transformer: null
      }
      
      export default memo(CharacterMap);
      

      Save and close the file.

      In this application, you don’t want to give users the ability to toggle between different functions. But you do want the characters to be lower case. Define a transformer in App.js that will convert the character to lower case. This function will never change, but you do need to pass it to the CharacterMap.

      Open App.js:

      • nano src/components/App/App.js

      Then define a function called transformer that converts a character to lower case. Pass the function as a prop to CharacterMap:

      performance-tutorial/src/components/CharacterMap/CharacterMap.js

      import React, { useReducer, useState } from 'react';
      import './App.css';
      
      import CharacterMap from '../CharacterMap/CharacterMap';
      
      function App() {
        const [text, setText] = useState('');
        const [showExplanation, toggleExplanation] = useReducer(state => !state, false)
        const transformer = item => item.toLowerCase();
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
            <div>
              <button onClick={toggleExplanation}>Show Explanation</button>
            </div>
            <CharacterMap showExplanation={showExplanation} text={text} transformer={transformer} />
          </div>
        )
      }
      
      export default App;
      

      Save the file. When you do, you will find that the delay has returned when you toggle the explanation.

      Animation showing a delay when toggling explanation

      If you profile the component, you will find that the component re-renders because the props change and the Hooks changed:

      Profile for transformer

      If you look closely, you’ll find that the showExplanation prop changed, which makes sense because you clicked the button, but the transformer prop also changed.

      When you made a state change in App by clicking on the button, the App component re-rendered and redeclared the transformer. Even though the function is the same, it is not referentially identical to the previous function. That means it’s not strictly identical to the previous function.

      If you open the browser console and compared identical functions, you’d find that the comparison is false, as shown in the following code block:

      const a = () = {};
      const b = () = {};
      
      a === a
      // This will evaluate to true
      
      a === b
      // This will evaluate to false
      

      Using the === comparison operator, this code shows that two functions are not considered equal, even if they have the same values.

      To avoid this problem, React provides a Hook called useCallback. The Hook is similar to useMemo: it takes a function as the first argument and an array of dependencies as the second argument. The difference is that useCallback returns the function and not the result of the function. Like the useMemo Hook, it will not recreate the function unless a dependency changes. That means that the useMemo Hook in CharacterMap.js will compare the same value and the Hook will not re-run.

      Inside of App.js, import useCallback and pass the anonymous function as the first argument and an empty array as the second argument. You never want App to recreate this function:

      performance-tutorial/src/components/App/App.js

      import React, { useCallback, useReducer, useState } from 'react';
      import './App.css';
      
      import CharacterMap from '../CharacterMap/CharacterMap';
      
      function App() {
        const [text, setText] = useState('');
        const [showExplanation, toggleExplanation] = useReducer(state => !state, false)
        const transformer = useCallback(item => item.toLowerCase(), []);
      
        return(
          <div className="wrapper">
            <label htmlFor="text">
              <p>Your Text</p>
              <textarea
                id="text"
                name="text"
                rows="10"
                cols="100"
                onChange={event => setText(event.target.value)}
              >
              </textarea>
            </label>
            <div>
              <button onClick={toggleExplanation}>Show Explanation</button>
            </div>
            <CharacterMap showExplanation={showExplanation} text={text} transformer={transformer} />
          </div>
        )
      }
      
      export default App;
      

      Save and close the file. When you do, you’ll be able to toggle the explanation without re-running the function.

      Animation showing no delay when toggling the explanation component

      If you profile the component, you’ll find that the Hook no longer runs:

      Image of the browser developer tools showing that the Hook does not run

      In this particular component, you do not actually need the useCallback Hook. You could declare the function outside of the component and it would never re-render. You should only declare functions inside of your component if they require some sort of prop or stateful data. But there are times when you need to create functions based on internal state or props and in those situations you can use the useCallback Hook to minimize re-renders.

      In this step, you preserved functions across re-renders using the useCallback Hook. You also learned how those functions will retain equality when compared as props or dependencies in a Hook.

      Conclusion

      You now have the tools to improve performance on expensive components. You can use memo, useMemo, and useCallback to avoid costly component re-renders. But all these strategies include a performance cost of their own. memo will take extra work to compare properties, and the Hooks will need to run extra comparisons on each render. Only use these tools when there is a clear need in your project, otherwise you risk adding in your own latency.

      Finally, not all performance problems require a technical fix. There are times when the performance cost is unavoidable—such as slow APIs or large data conversions—and in those situations you can solve the problem using design by either rendering loading components, showing placeholders while asynchronous functions are running, or other enhancements to the user experience.

      If you would like to read more React tutorials, check out our React Topic page, or return to the How To Code in React.js series page.



      Source link

      Networks and Online Gaming: 3 Ways to Improve Performance and Retain Your Audience


      What makes or breaks the technical success of a new multiplayer video game? Or for that matter, the success of any given online gaming session or match? There are a lot of reasons, to be sure, but success typically boils down to factors outside of the end users’ control. At the top of the list, arguably, is network performance.

      In June, 2018 Fornite experienced a network interruption that caused world-famous streamer, Ninja, to swap mid-stream to Hi-Rez’s Realm Royale. Ninja gave the game rave reviews, resulting in a huge userbase jumping over to play Realm Royale. And just this month, the launch of Wolcen: Lords of Mayhem was darkened by infrastructure issues as the servers couldn’t handle the number of users flocking to the game. While both popular games might not have experienced long-term damage, ongoing issues like these can turn users toward a competitor’s game or drive them away for good.

      Low latency is so vital, that in a 2019 survey, seven in 10 gamers said they will play a laggy game for less than 10 minutes before quitting. And nearly three in 10 say what matters most about an online game is having a seamless gaming experience without lag. What can game publishers do to prevent lag, increase network performance and increase the chances that their users won’t “rage quit”?

      Taking Control of the Network to Avoid Log Offs

      There are a few different ways to answer the question and avoid scenario outlined above, but some solutions are stronger than others.

      Increase Network Presence with Edge Deployments

      One option is to spread nodes across multiple geographical presences to reduce the distance a user must traverse to connect. Latency starts as a physics problem, so the shorter the distance between data centers and users, the lower the latency.

      This approach isn’t always the best answer, however, as everyday there can be both physical and logical network issues just miles apart from a user and a host. Some of these problems can be the difference between tens to thousands of milliseconds across a single carrier.

      Games are also increasingly global. You can put a server in Los Angeles to be close to users on the West Coast, but they’re going to want to play with their friends on the East Coast, or somewhere even further away.

      Connect Through the Same Carriers as the End Users

      Another answer is to purchase connectivity to some of the same networks end users will connect from, such as Comcast, AT&T, Time Warner, Telecom, Verizon, etc.

      A drawback of this option, though, stems from the abolishment of Net Neutrality. Carriers don’t necessarily need to honor best-route methodology anymore, meaning they can prioritize cost efficiency over performance on network configurations. I’ve personally observed traffic going from Miami to Tampa being routed all the way to Houston and back, as show in the images below.

      Network routing
      The traffic on the left follows best-route methodology, while the traffic on the right going from Miami to Tampa is being routed through Houston. This is one consequence of the abolishment of Net Neutrality.

      Purchasing connectivity that gets you directly into the homes of end-users may seem like the best method to reduce latency, but bottlenecks or indirect routing inside these large carriers’ networks can cause issues. A major metro market in the United States can also have three to four incumbent consumer carriers providing residential services to gamers, necessitating and IP blend to effectively reach end users. However, startups or gaming companies don’t want to build their own blended IP solution in every market they want to build out in.

      Choose a Host with a Blended Carrier Agreement

      The best possible solution to the initial scenario is to host with a carrier that has a blended carrier agreement, with a network route optimization technology to algorithmically traverse all of those carriers.

      Take for example, INAP’s Performance IP® solution. This technology makes a daily average of nearly 500 million optimizations across INAP’s global network to automatically put a customer’s outbound traffic on the best-performing route. This type of technology reduces latency upwards of 44 percent and prevents packet loss, preventing users from experiencing the lag that can change the fate of a game’s commercial success. You can explore our IP solution by running your own performance test.

      Taking Control When Uncontrollable Factors are at Play

      There will be times that game play is affected by end user hardware. It makes a difference, and it always will, but unfortunately publishers can’t control the type of access their users have to the internet. In some regions of the world, high speed internet is just a dream, while in others it would be unfathomable to go without high-speed internet access.

      Inline end user networking equipment can also play a role in network behavior. Modems, switches, routers and carrier equipment can cause poor performance. Connectivity being switched through an entire neighborhood, throughput issues during peak neighborhood activities, satellite dishes angled in an unoptimized position limiting throughput—there’s a myriad of reasons that user experience can be impacted.

      With these scenarios, end users often understand what they are working with and make mental allowances to cope with any limitations. Or they’ll upgrade their internet service and gaming hardware accordingly.

      The impact of network performance on streaming services and game play can’t be underscored enough. Most end users will make the corrections they can in order to optimize game play and connectivity. The rest is up to the publisher.

      Explore INAP’s Global Network.

      LEARN MORE

      Dan Lotterman


      READ MORE



      Source link

      Check This Overlooked Setting to Troubleshoot ‘Strange’ Microsoft SQL Server Performance Issues


      As a SQL DBA or a system admin of highly transactional, performance demanding SQL databases, you may often find yourself perplexed by “strange” performance issues reported by your user base. By strange, I mean any issue where you are out of ideas, having exhausted standard troubleshooting tactics and when spending money on all-flash storage is just not in the budget.

      Working under pressure from customers or clients to resolve performance issues is not easy, especially when C-Level, sales and end users are breathing down your neck to solve the problem immediately. Contrary to popular belief from many end users, we all know that these types of issues are not resolved with a magic button or the flip of a switch.

      But what if there was a solution that came close?

      Let’s review the typical troubleshooting process, and an often-overlooked setting that may just be your new “magic button” for resolving unusual SQL server performance issues.

      Resolving SQL Server Performance Issues: The Typical Process

      Personally, I find troubleshooting SQL related performance issues very interesting. In my previous consulting gigs, I participated in many white boarding sessions and troubleshooting engagements as a highly paid last-resort option for many clients. When I dug into their troubleshooting process, I found a familiar set of events happening inside an IT department specific to SQL Server performance issues.

      Here are the typical steps:

      • Review monitoring tools for CPU, RAM, IO, Blocks and so on
      • Start a SQL Profiler to collect possible offending queries and get a live view of the slowness
      • Check underlying storage for latency per IO, and possible bottle necks
      • Check if anyone else is running any performance intensive processes during production hours
      • Find possible offending queries and stop them from executing
      • DBAs check their SQL indexes and other settings

      When nothing is found from the above process, the finger pointing starts. “It’s the query.” “No, it’s the index.” “It’s the storage.” “Nope. It’s the settings in your SQL server.” And so it goes.

      Sound familiar?

      An Often-Forgotten Setting to Improve SQL Server Performance

      Based on the typical troubleshooting process, IT either implements a solution to prevent identical issues from coming back or hope to fix the issue by adding all flash and other expensive resources. These solutions have their place and are all equally important to consider.

      There is, however, an often-forgotten setting that you should check first—the block allocation size of your NTFS partition in the Microsoft Windows Server.

      The block allocation setting of the NTFS partition is set at formatting time, which happens very early in the process and is often performed by a sysadmin building the VM or bare metal server well before Microsoft SQL is installed. In my experience, this setting is left as the default (4K) during the server build process and is never looked at again.

      Why is 4K a bad setting? A Microsoft SQL page is 8KB in size. With a 4K block, you are creating two IO operations for every page request. This is a big deal. The Microsoft recommended block size for SQL server is 64K. This way, the page is collected in one IO operation.

      In bench tests of highly transactional databases on 64K block allocation in the NTFS partition, I frequently observe improved database performance by as much as 50 percent or more. The more IO intensive your DB is, the more this setting helps. Assuming your SQL server’s drive layout is perfect, for many “strange performance” issues, this setting was the magic button. So, if you are experiencing unexplained performance issues, this simple formatting setting maybe just what you are looking for.

      A word of caution: We don’t want to confuse this NTFS block allocation with your underlying storage blocks. This storage should be set to the manufacturer’s recommended block size. For example, as of recently, Nimble storage bock allocation at 8k provided best results with medium and large database sizes. This could change depending on the storage vendor and other factors, so be sure to check this with your storage vendor prior to creating LUNs for SQL servers.

      How to Check the NTFS Block Allocation Setting

      Here is a simple way to check what block allocation is being used by your Window Server NTFS partition:

      Open the command prompt as administrator and run the following command replacing the C: drive with a drive letter of your database data files. Repeat this step for your drives containing the logs and TempDB files:

      • fsutil fsinfo ntfsinfo c:

      Look for the reading “Bytes Per Cluster.”  If it’s set to 4096, that is the undesirable 4K setting.

      The fix is easy but could be time consuming with large database sizes. If you have an AlwaysOn SQL cluster, this can be done with no downtime. If you don’t have an AlwaysOn MSSQL cluster, then a downtime window will be required. Or, perhaps it’s time to build an AlwaysOn SQL cluster and kill two birds with one stone.

      To address the issue, you will want to re-format the disks containing SQL data with 64K blocks.

      Concluding Thoughts

      If your NTFS block setting is at 4K right now, moving the DB files to 64K formatted disks will immediately improve performance. Don’t wait to check into this one.

      Explore INAP Cloud.

      LEARN MORE

      Rob Lerner


      READ MORE



      Source link