React Integration
Learn how to integrate TradeX Chart into your React applications.
Installation
npm install tradex-chart
# or
yarn add tradex-chart
Basic Integration
Functional Component with Hooks
import React, { useEffect, useRef } from 'react'
function TradingChart({ symbol, data }) {
const chartRef = useRef(null)
const chartInstance = useRef(null)
useEffect(() => {
// Initialize chart
if (chartRef.current && !chartInstance.current) {
chartInstance.current = document.createElement('tradex-chart')
chartRef.current.appendChild(chartInstance.current)
chartInstance.current.start({
title: symbol,
state: {
ohlcv: data
}
})
}
// Cleanup
return () => {
if (chartInstance.current) {
chartInstance.current.destroy()
chartInstance.current = null
}
}
}, [])
// Update data when it changes
useEffect(() => {
if (chartInstance.current && data) {
chartInstance.current.mergeData(data)
}
}, [data])
return (
<div
ref={chartRef}
style={{ width: '100%', height: '600px' }}
/>
)
}
export default TradingChart
Usage
import TradingChart from './components/TradingChart'
function App() {
const [data, setData] = useState([])
useEffect(() => {
// Fetch data
fetch('https://api.example.com/ohlcv')
.then(res => res.json())
.then(setData)
}, [])
return (
<div className="App">
<h1>Trading Chart</h1>
<TradingChart symbol="BTC/USDT" data={data} />
</div>
)
}
Advanced Component
Reusable Chart Component
import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'
import * as talib from 'talib-web'
const TradeXChart = forwardRef(({
symbol,
data,
config = {},
onReady,
onError
}, ref) => {
const containerRef = useRef(null)
const chartRef = useRef(null)
// Expose chart methods to parent
useImperativeHandle(ref, () => ({
addIndicator: (name, params) => {
return chartRef.current?.addIndicator(name, params)
},
removeIndicator: (id) => {
chartRef.current?.removeIndicator(id)
},
setTimeframe: (timeframe) => {
// Custom timeframe logic
},
exportImage: () => {
return chartRef.current?.exportImage()
},
getChart: () => chartRef.current
}))
useEffect(() => {
const initChart = async () => {
try {
if (!containerRef.current) return
// Create chart element
const chart = document.createElement('tradex-chart')
containerRef.current.appendChild(chart)
chartRef.current = chart
// Configure chart
const chartConfig = {
title: symbol,
talib: talib,
width: containerRef.current.offsetWidth,
height: containerRef.current.offsetHeight,
...config,
state: {
ohlcv: data,
...config.state
}
}
// Start chart
await chart.start(chartConfig)
if (onReady) {
onReady(chart)
}
} catch (error) {
console.error('Failed to initialize chart:', error)
if (onError) {
onError(error)
}
}
}
initChart()
// Cleanup
return () => {
if (chartRef.current) {
chartRef.current.destroy()
chartRef.current = null
}
}
}, [])
// Update data
useEffect(() => {
if (chartRef.current && data) {
chartRef.current.mergeData(data)
}
}, [data])
// Update symbol
useEffect(() => {
if (chartRef.current && symbol) {
chartRef.current.setTitle(symbol)
}
}, [symbol])
return (
<div
ref={containerRef}
style={{ width: '100%', height: '100%' }}
/>
)
})
TradeXChart.displayName = 'TradeXChart'
export default TradeXChart
Using the Advanced Component
import { useRef, useState } from 'react'
import TradeXChart from './components/TradeXChart'
function TradingApp() {
const chartRef = useRef(null)
const [symbol, setSymbol] = useState('BTC/USDT')
const [data, setData] = useState([])
const handleAddRSI = () => {
chartRef.current?.addIndicator('RSI', { period: 14 })
}
const handleExport = () => {
const image = chartRef.current?.exportImage()
// Download or display image
}
return (
<div>
<div className="controls">
<button onClick={handleAddRSI}>Add RSI</button>
<button onClick={handleExport}>Export Image</button>
</div>
<TradeXChart
ref={chartRef}
symbol={symbol}
data={data}
config={{
theme: {
candle: {
UpBodyColour: '#26a69a',
DnBodyColour: '#ef5350'
}
}
}}
onReady={(chart) => console.log('Chart ready:', chart)}
onError={(error) => console.error('Chart error:', error)}
/>
</div>
)
}
Real-time Updates
WebSocket Integration
import { useEffect, useRef, useState } from 'react'
function RealtimeChart({ symbol }) {
const chartRef = useRef(null)
const wsRef = useRef(null)
const [data, setData] = useState([])
useEffect(() => {
// Connect WebSocket
wsRef.current = new WebSocket('wss://api.example.com/ws')
wsRef.current.onopen = () => {
wsRef.current.send(JSON.stringify({
type: 'subscribe',
symbol: symbol
}))
}
wsRef.current.onmessage = (event) => {
const update = JSON.parse(event.data)
if (chartRef.current) {
const candle = [
update.timestamp,
update.open,
update.high,
update.low,
update.close,
update.volume
]
if (update.isClosed) {
chartRef.current.addCandle(candle)
} else {
chartRef.current.updateStreamingCandle(candle)
}
}
}
wsRef.current.onerror = (error) => {
console.error('WebSocket error:', error)
}
// Cleanup
return () => {
if (wsRef.current) {
wsRef.current.close()
}
}
}, [symbol])
return <TradeXChart ref={chartRef} symbol={symbol} data={data} />
}
Custom Hooks
useTradeXChart Hook
import { useEffect, useRef, useState } from 'react'
function useTradeXChart(config = {}) {
const containerRef = useRef(null)
const chartRef = useRef(null)
const [isReady, setIsReady] = useState(false)
const [error, setError] = useState(null)
useEffect(() => {
const initChart = async () => {
try {
if (!containerRef.current) return
const chart = document.createElement('tradex-chart')
containerRef.current.appendChild(chart)
chartRef.current = chart
await chart.start(config)
setIsReady(true)
} catch (err) {
setError(err)
}
}
initChart()
return () => {
if (chartRef.current) {
chartRef.current.destroy()
}
}
}, [])
const addIndicator = (name, params) => {
return chartRef.current?.addIndicator(name, params)
}
const updateData = (data) => {
chartRef.current?.mergeData(data)
}
return {
containerRef,
chart: chartRef.current,
isReady,
error,
addIndicator,
updateData
}
}
// Usage
function ChartComponent({ data }) {
const { containerRef, isReady, addIndicator } = useTradeXChart({
title: 'BTC/USDT',
state: { ohlcv: data }
})
return (
<div>
{!isReady && <div>Loading chart...</div>}
<div ref={containerRef} style={{ width: '100%', height: '600px' }} />
{isReady && (
<button onClick={() => addIndicator('RSI')}>Add RSI</button>
)}
</div>
)
}
TypeScript Support
Type Definitions
import { RefObject } from 'react'
interface ChartConfig {
title?: string
width?: number
height?: number
theme?: any
state?: {
ohlcv?: number[][]
[key: string]: any
}
[key: string]: any
}
interface TradeXChartProps {
symbol: string
data: number[][]
config?: ChartConfig
onReady?: (chart: any) => void
onError?: (error: Error) => void
}
interface ChartRef {
addIndicator: (name: string, params?: any) => string
removeIndicator: (id: string) => void
setTimeframe: (timeframe: string) => void
exportImage: () => string
getChart: () => any
}
TypeScript Component
import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'
const TradeXChart = forwardRef<ChartRef, TradeXChartProps>((
{ symbol, data, config = {}, onReady, onError },
ref
) => {
const containerRef = useRef<HTMLDivElement>(null)
const chartRef = useRef<any>(null)
useImperativeHandle(ref, () => ({
addIndicator: (name: string, params?: any) => {
return chartRef.current?.addIndicator(name, params)
},
removeIndicator: (id: string) => {
chartRef.current?.removeIndicator(id)
},
setTimeframe: (timeframe: string) => {
// Implementation
},
exportImage: () => {
return chartRef.current?.exportImage()
},
getChart: () => chartRef.current
}))
// ... rest of implementation
return (
<div
ref={containerRef}
style={{ width: '100%', height: '100%' }}
/>
)
})
TradeXChart.displayName = 'TradeXChart'
export default TradeXChart
State Management
With Redux
import { useSelector, useDispatch } from 'react-redux'
import { useEffect } from 'react'
function ChartContainer() {
const dispatch = useDispatch()
const { symbol, data, indicators } = useSelector(state => state.chart)
const chartRef = useRef(null)
useEffect(() => {
// Sync indicators with chart
if (chartRef.current) {
indicators.forEach(indicator => {
chartRef.current.addIndicator(indicator.name, indicator.params)
})
}
}, [indicators])
return (
<TradeXChart
ref={chartRef}
symbol={symbol}
data={data}
/>
)
}
With Context API
import { createContext, useContext, useState } from 'react'
const ChartContext = createContext()
export function ChartProvider({ children }) {
const [symbol, setSymbol] = useState('BTC/USDT')
const [data, setData] = useState([])
const [indicators, setIndicators] = useState([])
const addIndicator = (name, params) => {
setIndicators([...indicators, { name, params }])
}
return (
<ChartContext.Provider value={{
symbol,
setSymbol,
data,
setData,
indicators,
addIndicator
}}>
{children}
</ChartContext.Provider>
)
}
export function useChart() {
return useContext(ChartContext)
}
// Usage
function ChartComponent() {
const { symbol, data, indicators } = useChart()
return <TradeXChart symbol={symbol} data={data} />
}
Performance Optimization
Memoization
import { memo, useMemo } from 'react'
const TradeXChart = memo(({ symbol, data, config }) => {
// Memoize config to prevent unnecessary re-renders
const chartConfig = useMemo(() => ({
title: symbol,
...config,
state: { ohlcv: data }
}), [symbol, config, data])
// ... chart implementation
}, (prevProps, nextProps) => {
// Custom comparison
return (
prevProps.symbol === nextProps.symbol &&
prevProps.data === nextProps.data
)
})
Lazy Loading
import { lazy, Suspense } from 'react'
const TradeXChart = lazy(() => import('./components/TradeXChart'))
function App() {
return (
<Suspense fallback={<div>Loading chart...</div>}>
<TradeXChart symbol="BTC/USDT" data={data} />
</Suspense>
)
}
Testing
Jest + React Testing Library
import { render, waitFor } from '@testing-library/react'
import TradeXChart from './TradeXChart'
describe('TradeXChart', () => {
it('renders without crashing', () => {
const { container } = render(
<TradeXChart symbol="BTC/USDT" data={[]} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('initializes chart with data', async () => {
const data = [
[1609459200000, 29000, 29500, 28800, 29200, 1234.56]
]
const { container } = render(
<TradeXChart symbol="BTC/USDT" data={data} />
)
await waitFor(() => {
const chart = container.querySelector('tradex-chart')
expect(chart).toBeInTheDocument()
})
})
it('calls onReady when chart is initialized', async () => {
const onReady = jest.fn()
render(
<TradeXChart
symbol="BTC/USDT"
data={[]}
onReady={onReady}
/>
)
await waitFor(() => {
expect(onReady).toHaveBeenCalled()
})
})
})
Common Patterns
Multi-Chart Layout
function MultiChartView() {
const symbols = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT']
return (
<div className="grid grid-cols-2 gap-4">
{symbols.map(symbol => (
<TradeXChart
key={symbol}
symbol={symbol}
data={data[symbol]}
config={{ height: 400 }}
/>
))}
</div>
)
}
Chart with Controls
function ChartWithControls() {
const chartRef = useRef(null)
const [timeframe, setTimeframe] = useState('1h')
const timeframes = ['1m', '5m', '15m', '1h', '4h', '1d']
return (
<div>
<div className="controls">
{timeframes.map(tf => (
<button
key={tf}
onClick={() => setTimeframe(tf)}
className={timeframe === tf ? 'active' : ''}
>
{tf}
</button>
))}
</div>
<TradeXChart
ref={chartRef}
symbol="BTC/USDT"
data={data}
/>
</div>
)
}
Troubleshooting
Chart Not Rendering
// Ensure container has size
const containerStyle = {
width: '100%',
height: '600px',
minHeight: '400px'
}
// Check if chart element is created
useEffect(() => {
console.log('Chart element:', chartRef.current)
}, [chartRef.current])
Memory Leaks
// Always cleanup in useEffect
useEffect(() => {
// ... initialization
return () => {
if (chartRef.current) {
chartRef.current.destroy()
chartRef.current = null
}
}
}, [])
Related Documentation
- Getting Started - Basic setup
- Configuration - Chart configuration
- WebSocket Integration - Real-time data
- API Reference - Complete API
- Vue Integration - Vue.js guide
- Angular Integration - Angular guide