Skip to main content

Using widgets with React

In this tutorial, you'll learn how to easily integrate widgets into React, using the WatchList widget as an example. Let's create a simple widget component.

Simple widget

Let's create a component of watch list widget as basic example:

import { newWatchListWidget } from '@dxfeed-widgets/watch-list'

const dataProviders = {
ipfPath: '/your-path/ipf',
ipfAuthHeader: 'your-header',
feedPath: 'wss://your-path/feed',
feedAuthHeader: 'your-header',
fundamentalsPath: '/api/fundamentals',
fundamentalsAuthHeader: 'your-header',
schedulePath: '/api/schedule',
}

export function WatchListWidget() {
const [widgetRootRef, setWidgetRootRef] = useState(null)
const [widget, setWidget] = useState(null)

useEffect(() => {
if (widgetRootRef !== null) {
// Here you can enable or disable different widget features
const initialConfig = { dataExportEnabled: true }

// Initial state of you widget
const initialState = {
symbols: [],
selectedSymbol: undefined,
columns: ['symbol', 'lastPrice'],
columnSizes: {},
filters: {},
sort: undefined,
}

const widget = newWatchListWidget({
element: widgetRootRef,
providers: dataProviders,
config: initialConfig,
state: initialState,
})
setWidget(widget)

return () => {
widget.unmount()
}
}
}, [widgetRootRef])

return <div style={{ height: 800, width: 600 }} ref={setWidgetRootRef} />
}

And now we can easily use this component in any place of our app just like that:

import { WatchListWidget } from '../path/to/our/widget'

export function App() {
return <WatchListWidget />
}

Connecting widgets

Using standalone widget is a possible way to use library, but let's make something more complex and create a connection between two widgets.

Firstly, we need to establish a shareable state. As an example, we can connect widgets by symbol. In this case, our app will appear as follows:

export function App() {
const [symbol, setSymbol] = useState('AAPL')

return (
<>
<WatchListWidget onSymbolChange={setSymbol} />
</>
)
}

We provide some state on the top level of our application. Let's now adjust our simple component to facilitate the passing of widget state.

import { newWatchListWidget } from '@dxfeed-widgets/watch-list'
import { newTimeAndSalesWidget } from '@dxfeed-widgets/time-and-sales'

export function WatchListWidget(props) {
const { onSymbolChange } = props

const [widgetRootRef, setWidgetRootRef] = useState(null)
const [widget, setWidget] = useState(null)

useEffect(() => {
if (widgetRootRef !== null) {
const initialConfig = { dataExportEnabled: true }

const initialState = {
symbols: [],
selectedSymbol: undefined,
columns: ['symbol', 'lastPrice'],
columnSizes: {},
filters: {},
sort: undefined,
}

const widget = newWatchListWidget({
element: widgetRootRef,
providers: dataProviders,
config: initialConfig,
state: initialState,
})
setWidget(widget)

return () => {
widget.unmount()
}
}
}, [widgetRootRef])

useEffect(() => {
if (widget !== null) {
widget.addStateListener('selectedSymbol', onSymbolChange)

return () => {
widget.removeStateListener('selectedSymbol', onSymbolChange)
}
}
}, [widget, onSymbolChange])

return <div style={{ height: 800, width: 600 }} ref={setWidgetRootRef} />
}

export function TimeAndSalesWidget(props) {
const { symbol } = props
const [widgetRootRef, setWidgetRootRef] = useState(null)
const [widget, setWidget] = useState(null)

useEffect(() => {
if (widgetRootRef !== null) {
const widget = newTimeAndSalesWidget({
element: widgetRootRef,
providers: dataProviders,
})
setWidget(widget)

return () => {
widget.unmount()
}
}
}, [widgetRootRef])

useEffect(() => {
widget?.updateState({ symbol })
}, [widget, symbol])

return <div style={{ height: 800, width: 600 }} ref={setWidgetRootRef} />
}

Now we can provide this data to another component or widget the following way:

export function App() {
const [symbol, setSymbol] = useState('AAPL')

return (
<>
<WatchListWidget onSymbolChange={setSymbol} />
<TimeAndSalesWidget symbol={symbol} />
<AnyAnotherComponent symbol={symbol} />
</>
)
}

Universal widget hook factory

If you want to avoid code duplication and add widgets faster, you can add hook factory as in example bellow:

const createUseWidgetHook = (factory, providers) => (props) => {
const { onStateChange, state, theme, config } = props

const [widgetRootRef, setWidgetRootRef] = useState(null)
const [widget, setWidget] = useState(null)

useEffect(() => {
if (widgetRootRef !== null) {
const widget = factory({
element: widgetRootRef,
providers,
config,
state,
theme,
})
setWidget(widget)

return () => {
widget.unmount()
}
}
}, [widgetRootRef])

useEffect(() => {
widget?.setConfig(config)
}, [widget, config])

useEffect(() => {
widget?.updateState(state)
}, [widget, state])

useEffect(() => {
widget?.setTheme(theme)
}, [widget, theme])

useEffect(() => {
if (widget !== null) {
widget.addStateListener(onStateChange)

return () => {
widget.removeStateListener(onStateChange)
}
}
}, [widget, onStateChange])

return setWidgetRootRef
}

Now, we can proceed to create a widget as follows:

const useWatchList = createUseWidgetHook(newWatchListWidget, dataProviders)

function WatchListWidget() {
const setRef = useWatchList({
config: { dataExportEnabled: true },
onStateChange: (state) => {
/*...*/
},
state: {
symbols: [],
selectedSymbol,
columns: ['symbol', 'lastPrice'],
columnSizes: {},
filters: {},
sort: undefined,
},
theme: 'light',
})

return <div style={{ height: 800, width: 600 }} ref={setRef} />
}