Skip to content

NickMitrokhin/devextreme-react

 
 

Repository files navigation

DevExtreme React UI and Visualization Components

Build Status NPM

This project allows you to use DevExtreme React Components.

Getting Started

You can try this live example, feature-based examples or configure local development environment as described below.

Prerequisites

Node.js and npm are required

Install DevExtreme

Install the devextreme and devextreme-react npm packages:

npm install --save devextreme devextreme-react

Additional Configuration

The following configuration steps depend on the build tool, bundler or module loader you are using:

Use DevExtreme Components

import React from 'react';
import ReactDOM from 'react-dom';

import Button from 'devextreme-react/button';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

ReactDOM.render(
    <Button text='Example Button' />,
    document.getElementById('root')
);

Note that DevExtreme styles are required.

API Reference

The complete list of components and their APIs are described in the DevExtreme API Reference.

See Markup Customization for details on how to implement and apply templates.

State Management

DevExtreme React components support Controlled and Uncontrolled states.

Controlled Mode

In the controlled mode, a parent component passes a component's state using its props. This mode provides the following capabilities:

  • Control component state externally (stateless behavior)
  • Share state between components in your app
  • Persist and restore state

To manage the component's state, provide a value for a related property and handle the event that is fired when the value changes. Note that the event is fired only when the component changes its state internally (when a user interacts with the component). If you directly update the property value, the event is not fired.

import React from 'react';
import ReactDOM from 'react-dom';

import TextBox from 'devextreme-react/text-box';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

class Example extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            text: 'TEXT'
        };

        this.handleChange = this.handleChange.bind(this);
    }

    render() {
        return (
            <div>
                <TextBox
                    value={this.state.text}
                    onValueChanged={this.handleChange}
                    valueChangeEvent='input'
                />
                <br />
                <div>{this.state.text}</div>
            </div>
        );
    }

    handleChange(e) {
        this.setState({
            text: e.value.toUpperCase().replace('A','_')
        });
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Live example

Uncontrolled Mode

In the uncontrolled mode, DevExtreme components manage the state internally.

Note that if you want to specify an initial value for an option in the uncontrolled mode, use the property with the default prefix. In the example below, the defaultCurrentView property is used to define the currentView option's initial value.

import React from 'react';
import ReactDOM from 'react-dom';

import Scheduler from 'devextreme-react/scheduler';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';
const appointments = [
    {
        text: 'Website Re-Design Plan',
        startDate: new Date(2017, 4, 22, 9, 30),
        endDate: new Date(2017, 4, 22, 11, 30)
    }, {
        text: 'Book Flights to San Fran for Sales Trip',
        startDate: new Date(2017, 4, 22, 12, 0),
        endDate: new Date(2017, 4, 22, 13, 0),
        allDay: true
    }, {
        text: 'Install New Router in Dev Room',
        startDate: new Date(2017, 4, 23, 10, 30),
        endDate: new Date(2017, 4, 23, 16, 30)
    }
];


ReactDOM.render(
    <Scheduler
        dataSource={appointments}
        height={600}
        editing={false}
        defaultCurrentView={'week'}
        defaultCurrentDate={new Date(2017, 4, 22)}
        startDayHour={9}
    />,
    document.getElementById('root')
);

Getting Widget Instance

A widget instance is required to call methods. You can get it by assigning a callback function to the component's ref property. This function accepts the mounted DevExtreme Component as an argument whose instance field stores the widget instance.

import React from 'react';
import ReactDOM from 'react-dom';

import { Button, TextBox } from 'devextreme-react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

class Example extends React.Component {
    constructor(props) {
      super(props);
      
      this.textBox = null;

      this.setTextBox = (ref) => {
          this.textBox = ref.instance;
      };
      
      this.onClick = () => {
          this.textBox.focus()
      };
    }

    render() {
        return (
            <div>
                <TextBox ref={this.setTextBox}/>
                <Button text='Go to the TextBox' onClick={this.onClick} />
            </div>
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Markup Customization

You can customize widget elements' appearance via the corresponding template properties. In the DevExtreme API, the corresponding options have the Template suffix (e.g. dxList's itemTemplate option).

To specify a DevExtreme React Component template, use the Render or Component suffix instead. If a widget has an option called template (e.g. Button's template option) the corresponding React Component properties are called render and component.

Rendering Function

Use the render property or a property with the Render suffix to specify a function that renders a component's or component element's content.

If a widget has the template option, use the render property to specify the rendering function:

import React from 'react';
import ReactDOM from 'react-dom';

import Button from 'devextreme-react/button';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

class CounterButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0
        };

        this.handleClick = this.handleClick.bind(this);
    }

    render() {
        return (
            <Button
                text={this.props.text}
                render={(btn) => <div style={{ padding: 20 }}>{btn.text} (<b>{this.state.counter}</b>)</div>}
                onClick={this.handleClick}
            />
        );
    }

    handleClick() {
        this.setState({
            counter: this.state.counter + 1
        });
    }
}

ReactDOM.render(
    <CounterButton text='Click me!' />,
    document.getElementById('root')
);

For an option with the template suffix, use the corresponding property with the Render suffix to specify a rendering function:

import React from 'react';
import ReactDOM from 'react-dom';

import List from 'devextreme-react/list';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

const items = [
    { text: '123' },
    { text: '234' },
    { text: '567' }
];

ReactDOM.render(
    <List items={items} itemRender={(item) => <i>Function template for item <b>{item.text}</b></i>}/>,
    document.getElementById('root')
);

Component

You can also customize a widget's or widget element's content using a component instead of a rendering function. Pass the component or an inline function that creates the component to the component property or a property with the Component suffix. If you pass an inline function, a new component will be created each time the widget is rendered.

Functional Components can cause unnecessary render calls. In such cases, consider using the PureComponent or the shouldComponentUpdate method.

The following example demonstrates how to use the itemComponent property to customize the widget item's appearance:

import React from 'react';
import ReactDOM from 'react-dom';

import List from 'devextreme-react/list';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

const items = [
    { text: '123' },
    { text: '234' },
    { text: '567' }
];

class Item extends React.PureComponent {

    render() {
        return (
            <i onClick={this.handleClick}>
                Component template for item {this.props.text}.
            </i>
        );
    }
}

ReactDOM.render(
    <List items={items} itemComponent={Item} />,
    document.getElementById('root')
);

Note: You cannot use the key prop in template components because it is a special React prop. Use dxkey instead.

Template Component

To customize a widget element with a template component, specify the template name in the template component's name property and assign this name to the widget's template option. The template markup can be specified as a component (the component property) or a rendering function (the render property). Alternatively, you can put the markup directly into the template component as shown in the example below.

import React from 'react';
import ReactDOM from 'react-dom';

import DataGrid, { Column } from "devextreme-react/data-grid"; 
import { Template } from "devextreme-react/core/template";

import { data } from './data.js';

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            expandAll: true
        };

        this._handleToolbarPreparing = this._handleToolbarPreparing.bind(this);
    }

    render() {
        return (
            <React.Fragment>
                <DataGrid 
                    dataSource={ data }
                    onToolbarPreparing={this._handleToolbarPreparing}
                >
                    <GroupPanel visible={true} />
                    <Grouping autoExpandAll={this.state.expandAll} />
                    <Column dataField="firstName"/>
                    <Column dataField="lastName" caption="Last Name" defaultVisible={true}/>

                    <Template name={"toolbarLabel"}>
                        {this.state.expandAll ? <b>All data is expanded</b> : <b>All data is collapsed</b>}
                    </Template>
                </DataGrid>
            </React.Fragment>
        );
    }
    
    _handleToolbarPreparing(args) {
        args.toolbarOptions.items.unshift({
            location: "after",
            template: "toolbarLabel"
        });
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Components with Transcluded Content

The components that displays content in an overlaying window (for example, ScrollView), allow to specify the content as component children:

import React from 'react';
import ReactDOM from 'react-dom';

import { Button, ScrollView } from 'devextreme-react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

class Example extends React.Component {

    render() {
        return (
            <ScrollView height={200} width={200}>
                <Button text='Show alert' onClick={() => alert('shown')} />
                <br />
                <p>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sed lacus
                    egestas, facilisis urna nec, fringilla nibh. Maecenas enim felis, ultricies
                    pretium aliquet ut, aliquam id urna. Lorem ipsum dolor sit amet, consectetur
                    adipiscing elit. Nam viverra est at neque fringilla, non iaculis magna
                    ultrices. Nunc posuere tincidunt elit a molestie. Nulla aliquet metus ex. Nunc
                    aliquam volutpat libero, ac tincidunt felis consectetur id. Sed diam lectus,
                    dictum non tempus fringilla, semper in dui. Donec at hendrerit massa. Aenean
                    quis suscipit nisi. Cras sed eros tristique, venenatis diam in, rhoncus enim.
                </p>
                <p>
                    Orci varius natoque penatibus et magnis dis parturient montes, nascetur
                    ridiculus mus. Curabitur et ex sit amet odio efficitur fermentum.
                    Donec lobortis hendrerit massa. Praesent tempus cursus tempus. Maecenas at
                    dolor lacus. Vestibulum suscipit ac mi vitae posuere. Maecenas id urna eget
                    sapien volutpat laoreet. Sed nulla purus, aliquam nec augue vel, consequat
                    tincidunt erat. Phasellus hendrerit rhoncus erat, ut fermentum orci molestie a.
                </p>
            </ScrollView>
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Configuration Components

DevExtreme React Components provide configuration components for the underlying widget's complex nested options.

Use a named import to get a configuration component.

import Chart, { Tooltip } from "devextreme-react/chart"; 

Configuration components support markup customization props (with Render or Component suffix) and uncontrolled props.

Basic Usage

The following example demonstrates how to configure the dxChart widget's tooltip option:

import React from 'react';
import ReactDOM from 'react-dom';

import Button from "devextreme-react/button"; 
import Chart, { Tooltip } from "devextreme-react/chart"; 

import { complaintsData } from './data.js';

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            showTooltip: false
        };

        this.toggleTooltip = this.toggleTooltip.bind(this);
    }
    
    toggleTooltip() {
        this.setState({
            showTooltip: !this.state.showTooltip
        });
    }

    render() {
        return (
            <React.Fragment>
                <Chart
                    dataSource={ complaintsData }
                    title="Pizza Shop Complaints">
                    <Tooltip enabled={ this.state.showTooltip }/>
                </Chart>
                <Button text="Toggle tooltip" onClick={ this.toggleTooltip }/>
            </React.Fragment>
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Collection Options

You can also use configuration components for complex collection options. The following example demonstrates how to configure the the dxDataGrid widget's columns option:

import React from 'react';
import ReactDOM from 'react-dom';

import DataGrid, { Column } from "devextreme-react/data-grid"; 

import { data } from './data.js';

class Example extends React.Component {
    render() {
        return (
            <React.Fragment>
                <DataGrid dataSource={ data }>
                    <Column dataField="firstName"/>
                    <Column dataField="lastName" caption="Last Name" defaultVisible={true}/>
                </DataGrid>
            </React.Fragment>
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Note that configuration components are not provided for options that accept a type that depends on another option's value. For example, the DataGrid's editorOptions, Form's editorOptions, Toolbar's widget options.

Put Markup Into Configuration Components

If a widget's configuration component supports the template option, you can put markup into this component. The following example demonstrates how to specify a list item's markup.

import React from 'react';
import ReactDOM from 'react-dom';

import List, { Item } from 'devextreme-react/list';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

ReactDOM.render(
    (
    <List>
      <Item>orange</Item>
      <Item>white</Item>
      <Item>black</Item>
    </List>
    ),
    document.getElementById('root')
);

DevExtreme Validation

DevExtreme React editors support built-in data validation.

import React from 'react';
import ReactDOM from 'react-dom';

import { Button, TextBox, ValidationGroup, ValidationSummary, Validator } from 'devextreme-react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

class Example extends React.Component {

    validationRules = {
        email: [
            { type: 'required', message: 'Email is required.' },
            { type: 'email', message: 'Email is invalid.' }
        ],
        password: [
            { type: 'required', message: 'Password is required.' }
        ]
    };

    constructor(props) {
        super(props);

        this.validate = this.validate.bind(this);
    }

    render() {
        return (
            <ValidationGroup>
                <TextBox defaultValue={'email@mail.com'}>
                    <Validator validationRules={this.validationRules.email} />
                </TextBox>
                <TextBox defaultValue={'password'}>
                    <Validator validationRules={this.validationRules.password} />
                </TextBox>
                <ValidationSummary />
                <Button
                    text={'Submit'}
                    onClick={this.validate}
                />
            </ValidationGroup>
        );
    }

    validate(params) {
        const result = params.validationGroup.validate();
        if (result.isValid) {
            // form data is valid
            //params.validationGroup.reset();
        }
    }
}
ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Working with Data

DevExtreme includes the data layer that enables you to read and write data stored in different data sources.

The example below demonstrates how to use a DataSource with DevExtreme Components.

import React from 'react';
import ReactDOM from 'react-dom';

import DataSource from 'devextreme/data/data_source';
import List from 'devextreme-react/list';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.compact.css';

const items = [
    { text: '123' },
    { text: '234' },
    { text: '567' }
];

class Example extends React.Component {

    constructor(props) {
        super(props);

        this.dataSource = new DataSource({
            store: {
                type: 'array',
                data: items
            },
            sort: [
                { getter: 'text', desc: true }
            ],
            pageSize: 1
        });
    }

    render() {
        return (
            <List dataSource={this.dataSource} />
        );
    }

    componentWillUnmount() {
        this.dataSource.dispose();
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('root')
);

Note that a DataSource is considered as a 'service'. So, modifying its properties does not cause component rerendering.

Typescript Support

We provide TypeScript declarations for DevExtreme Components. Strict typing allows you to catch many bugs and improve your workflow by adding features like auto-completion and automated refactoring.

Below is an example of appearance customization using TypeScript:

import * as React from "react";
import * as ReactDOM from "react-dom";
import List from "devextreme-react/list";

import "devextreme/dist/css/dx.common.css";
import "devextreme/dist/css/dx.light.compact.css";

interface IListItemProps {
    text: string;
}

class Item extends React.Component<IListItemProps, { counter: number }> {

    constructor(props: IListItemProps) {
        super(props);
        this.state = {
            counter: 0
        };

        this.handleClick = this.handleClick.bind(this);
    }

    public render() {
        return (
            <i onClick={this.handleClick}>
                Component template for item {this.props.text}. <b>Clicks: {this.state.counter}</b>
            </i>
        );
    }

    private handleClick() {
        this.setState({
            counter: this.state.counter + 1
        });
    }
}

const items: IListItemProps[] = [
    { text: "123" },
    { text: "234" },
    { text: "567" }
];

ReactDOM.render(
    <List items={items} itemComponent={Item} />,
    document.getElementById("root")
);

License

DevExtreme React components are released as an MIT-licensed (free and open-source) add-on to DevExtreme.

Familiarize yourself with the DevExtreme License.

A free trial is available!

Support & Feedback

About

DevExtreme React UI and visualization components

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 71.7%
  • TypeScript 28.3%