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} />
}