Context framework
DXcharts manages state with Context, an open-source framework developed by Devexperts.
It serves two main purposes:
- State management (similar to
Redux) - Code organization
It defines dependencies and resolves them at compile time (not runtime) using TypeScript and the fp-ts library (functional programming methods).
This approach helps catch errors early and improves type safety.
If you have experience with Angular’s injector, Context works in a similar way, but with enhanced type safety.
If you are familiar with Spring (Java), it is also similar to compile-time dependency resolution.
Under the hood, Context is based on the Reader monad from fp-ts library.
When evaluated, it returns a Sink containing both a value and any associated effects.
Basic example
Here’s a simple example showing how to create and compose Context instances:
import { context } from '@dx-private/dxchart5-react/dist/context/context2';const one = context.of(1);const two = context.of(2);const three = context.combine(one, two, (o, t) => {return o + t;});console.log(three(null).value); // prints 3
This example demonstrates creating Context instances and combining them using combine.
Practical example
The following example shows a more practical use case:
import { context } from '@dx-private/dxchart5-react/dist/context/context2';import React from 'react';import { createRoot } from 'react-dom/client';import { DateFormatter, MathPackage, Order, OrderConfirmation, OrderProvider } from './context-example';const OrderConfirmationContainer = context.combine(context.key<MathPackage>()('mathPackage'), // specify dependency type and namecontext.key<DateFormatter>()('dateFormatter'),context.key<OrderProvider>()('orderProvider'),(mathPackage, dateFormatter, orderProvider) => {const currentOrder = orderProvider.getCurrentOrder();const stocksPriceFormatter = (price: number) => {return mathPackage.toFixedPrecision(price, 2) + '$';};return (<OrderConfirmationorder={currentOrder}priceFormatter={stocksPriceFormatter}dateFormatter={dateFormatter.format}/>);},);// provide dependency instance hereconst orderProvider = {//@ts-ignoregetCurrentOrder(): Order {// return undefined;},};const mathPackage = {sum(a: number, b: number): number {return a + b;},toFixedPrecision(n: number, base: number): string {return n.toFixed(base);},};const dateFormatter = {format(timestamp: number): string {return new Date(timestamp).toString();},};const OrderConfirmationComponent = OrderConfirmationContainer({dateFormatter, // will not compile until all 3 dependencies are provided with correct typeorderProvider,mathPackage,});const root = createRoot(document.body);root.render(OrderConfirmationComponent.value);
In this example, OrderConfirmationContainer is created using three dependencies defined with context.key.
Until all dependencies are resolved with the correct types, you cannot create an instance of OrderConfirmationContainer. This ensures type safety during development.
Context utility functions
Context is a monad and includes several utility functions to support functional programming patterns:
sequenceT: Combines multiple contexts into a single context.sequenceArray: Combines an array of contexts into a single context.chain: Chains contexts sequentially.flatten: Flattens nested contexts.map: Maps a value within a context.of: Wraps a value in a context.