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.