DevExtreme React - Optimize Performance

DevExtreme React Components may have reduced performance due to unnecessary component re-renders that can also cause the following side effects:

  • The component responds to a click incorrectly.
  • The component discards its value on input.
  • The component displays data incorrectly (for example, the DataGrid shows gray placeholders).
  • Element selection does not work.
  • The render or component functions work unexpectedly.

This help topic describes best practices that ensure the component updates only when necessary.

Do not pass inline functions and objects as component props

React includes Virtual DOM (VDOM) which is a "virtual" representation of a UI that is kept in sync with the "real" DOM. When you change a VDOM element, React compares the new and previous VDOMs and re-renders the updated part. The main way you can change a DevExtreme React Component VDOM element is via component props. If you pass an object literal or function as props inline, it becomes a new object in memory that differs from the previous object. The VDOM comparison shows that the component has changed and should be re-rendered even though the actual value remains the same.

For example, implement the code below:

App.js
const MyComponent = (props) => {
    return <Button>{props.hello.text}</Button>
}

function App() {
    return (
        <MyComponent hello={{text: 'Hello World'}} />
    );
}

export default App;

In this case, the MyComponent component is always re-rendered, even if a value does not change.

Object Literals

Most properties of DevExtreme React Components are implemented as nested configuration components. Do not use objects to specify these properties:

App.js
// Incorrect:
function App() {
    // ...
    return (
        <DataGrid ...
            selection={{ mode: "single" }} 
        >
    );
}

export default App;

// Correct:
function App() {
    // ...
    return (
        <DataGrid>
            <!-- ... -->
            <Selection mode="single" />
        </DataGrid>
    );
}

export default App;    

View in CodeSandBox

If you need to use objects, define them outside the component class or function body or wrap them in the useMemo hook as shown in the following code. In class components, use the constructor to assign the objects to class properties.

App.js
// Incorrect: Inline object
function App() {
    // ...
    return (
        <!-- ... -->
        <Item ...
            editorType="dxSelectBox"
            editorOptions={{
                items: positions,
                searchEnabled: true,
                value: "CEO"
            }}
        />
    );
}

export default App;

// Correct: Object declared outside the component
const options = {
    items: positions,
    searchEnabled: true,
    value: "CEO"
};

function App() {
    // ...
    return (
        <!-- ... -->
        <Item ...
            editorType="dxSelectBox"
            editorOptions={options}
        />
    );
}

export default App;

// Correct: Object wrapped in `useMemo`
function App() {
    // ...
    const options = React.useMemo(() => ({
        items: positions,
        searchEnabled: true,
        value: "CEO",
    }), []);

    return (
        <!-- ... -->
        <Item ...
            editorType="dxSelectBox"
            editorOptions={options}
        />
    );
}

export default App;

View in CodeSandBox

Functions

Extract inline handlers from the component class or function body. As an alternative, you can leave them within the component, but in this case wrap them in the useCallback hook (in function components) or extract them from the render function (in class components).

App.js
// Incorrect: Inline function
function App() {
    // ...
    return (
        <DataGrid ...
            keyExpr="ID"
            selectedRowKeys={keys}
            onCellPrepared={(e) => {
                if (e.row && e.row.isSelected) {
                    e.cellElement.style.backgroundColor = "green";
                }
            }}
        >
        </DataGrid>
    );
}

export default App;

// Correct: Function declared outside the component
const cellPrepared = (e) => {
    if (e.row && e.row.isSelected) {
        e.cellElement.style.backgroundColor = "green";
    }
};

function App() {
    // ...
    return (
        <DataGrid ...
            keyExpr="ID"
            selectedRowKeys={keys}
            onCellPrepared={cellPrepared}
        >
        </DataGrid>
    );
}

export default App;

// Correct: Function wrapped in `useCallback`
function App() {
    // ...
    const cellPrepared = React.useCallback((e) => {
        if (e.row && e.row.isSelected) {
            e.cellElement.style.backgroundColor = "green";
        }
    }, []);

    return (
        <DataGrid ...
            keyExpr="ID"
            selectedRowKeys={keys}
            onCellPrepared={cellPrepared}
        >
        </DataGrid>
    );
}

export default App;

View in CodeSandBox

Define Keys For Element Collections

When child elements specify key attributes, React uses the keys to compare the child elements of current and previous VDOM nodes. Keys allow child components to remain stable between renders.

App.js
// Incorrect:
const columns = ["CompanyName", "City", "State", "Phone", "Fax"];

function App() {
    // ...
    return (
        <DataGrid ... >
            {columns.map((col) => {
                return <Column dataField={col} />;
            })}
        </DataGrid>
    );
}

export default App;

// Correct:
const columns = ["CompanyName", "City", "State", "Phone", "Fax"];

function App() {
    // ...
    return (
        <DataGrid ... >
            {columns.map((col) => {
                return <Column dataField={col} key={uniqueKey} />;
            })}
        </DataGrid>
    );
}

export default App;

View in CodeSandBox

See Also