Tools
Ledger is a core component in the Carta ecosystem. It is used all over the platform to display and interact with complex data.
Though Ledger offers many amazing features (including searching, sorting, pagination, selecting/bulk actions, and filtering), it is also a flawed component - it tightly couples behavioral logic with display logic, which means that even small changes to its visuals may be complicated and result in breaking changes. Moreover, the component is built using the deprecated Table component, rather than NewTable, which means it is missing key functionality that users have come to expect with Ink table components.
Rather than upgrade Ledger to use NewTable under the hood, Ink opted to create the useLedger
hook, which will provide a flexible way to leverage Ledger functionality without tightly coupling those features to a specific UI.
The useLedger
hook uses a headless API design pattern to separate Ledger logic from it's visual representation. This means that Ink users can leverage Ledger sorting, searching, selecting, pagination, and filtering, without being tied to the UI of the Ledger component.
useLedger
manages all of the stateful logic that is necessary to make a functional ledger, without tying it directly to the Ledger component.
Additionally, while the Ledger component was built specifically to work with carta-web-style Django Rest Framework endpoints, the useLedger
hook offers the flexibility to call other kinds of APIs.
The useLedger
hook is instantiated with a configuration object. Getter props and state variables can be destructured from the hook's result and passed into Ink components in order to recreate Ledger functionalities without the Ledger component.
The props passed into the configuration object help the hook understand how to parse the API response for your ledger to do sorting, filtering, searching, and pagination. Many of these props are not necessary while using a standard carta-web style DRF endpoint.
The following props can be passed into the configuration object:
name | description | required |
---|---|---|
url | The url to which the hook will make API calls | true |
getNumPages | A function used by the hook to parse the API response to find the total number of pages | false |
getCurrentPage | A function used by the hook to parse the API response to find the current page number | false |
getTotalItems | A function used by the hook to parse the API response to find the total items returned, i.e. count | false |
pageSize | The number of results to fetch per page | false |
sortConfig | Object used by the hook to manage sorting | false |
dataKey | The key in the API response that correlates to the data that will populate the ledger. Defaults to results | false |
selectableConfig | The same config object passed into the useSelectable hook | false |
debounceTimeout | Debounce timeout for the API call | false |
defaultQueryParams | Object determining the default query params for the API request | false |
stateToSearchParam | Function mapping the state to a search parameter in the API call | false |
stateToOrderingParam | Function mapping the state to an ordering parameter in the API call | false |
stateToFilterParams | Function mapping the state to filter parameters in the API call | false |
makeRequest | Function that returns a promise, allowing the user to completely override the hook's built-in API call logic with custom logic | false |
externals | Object containing values to be added to the API call URL as additional query parameters | false |
In addition to the internal state of the hook, the following getter props and state variables are returned:
name | description |
---|---|
getFilterProps | Props used for filtering |
getSearchProps | Props used for searching. Unpack into LedgerWrapper.Search for the simplest implementation |
getPaginationProps | Props used for pagination. Unpack into LedgerWrapper.Pagination for the simplest implementation |
getSortingProps | Props used for sorting. Can be unpacked into NewTable.Pin for the simplest implementation |
selected | Object containing information about which rows are selected. Correlates to selected in the useSelectable hook |
selectableProps | Object containing selectable actions. Correlates to actions in the useSelectable hook |
getTopBarProps | Props that can be unpacked into the LedgerWrapper.TopBar component |
getBottomBarProps | Props that can be unpacked into the LedgerWrapper.BottomBar component |
Ink offers two sets of subcomponents that can be used in conjunction with the useLedger
hook for a simple ledger implementation. Low-level subcomponents such as Filter, Search, Pagination, and Actions give users the flexibility and building blocks they need to create a customized ledger as per their unique specifications. However, these low-level subcomponents do not account for certain behaviors that are baked into the original Ledger component - for example, hiding pagination when rows are selected.
To make it easier to maintain visual and behavioral parity as closely as possible with the original Ledger component, Ink also offers two high-level components: TopBar and BottomBar. These components handle not only visuals, but also the behavioral logic that most people associate with being intrinsic to Ledger.
In its simplest implementation, useLedger
is used with a standard carta-web style DRF endpoint. In such a case, the code used to instantiate the useLedger
hook would look something like the following:
const { data, loading, getTopBarProps, getBottomBarProps, selected, selectableProps } = useLedger({url: "http:///www.mycoolapi.com",})
The destructured objects can then be used as follows for a fully functional ledger using the high-level subcomponents LedgerWrapper.TopBar
and LedgerWrapper.BottomBar
:
return (<Ink.Page><Ink.Page.Body><Ink.Page.Content><Ink.LedgerWrapper.TopBaractions={<><Ink.Button>Draft shares</Ink.Button><Ink.Button>Manage stakeholders</Ink.Button></>}bulkActions={<Ink.Button>Resend emails</Ink.Button>}filter={{groups: [{label: 'Type',key: 'type',items: [{id: 'corporation',value: 'Corporation',label: 'Corporation',},{id: 'personal',value: 'Personal',label: 'Personal',},],},],}}{...getTopBarProps()}/><Ink.NewTable><Ink.NewTable.Head><Ink.NewTable.Row><Ink.NewTable.HeadCell><Ink.NewCheckbox id="select-all" checked={selected.allRows} onChange={selectableProps.toggleAllRows} /></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey('legal_name')}>Stakeholder</Ink.NewTable.Pin></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey('id')}>ID</Ink.NewTable.Pin></Ink.NewTable.HeadCell></Ink.NewTable.Row></Ink.NewTable.Head><Ink.NewTable.Body>{data.map(d => (<Ink.NewTable.Row key={d.id} selected={selected.rows[d.id]}><Ink.NewTable.Cell preset="checkbox"><Ink.NewCheckbox id={d.id} onChange={selectableProps.toggleRow} checked={selected.rows[d.id]} /></Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.legal_name}</Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.id}</Ink.NewTable.Cell></Ink.NewTable.Row>))}</Ink.NewTable.Body></Ink.NewTable><Ink.LedgerWrapper.BottomBar {...getBottomBarProps()} /></Ink.Page.Content></Ink.Page.Body></Ink.Page>)
Alternatively, the same result can be achieved with the low-level subcomponents LedgerWrapper.Filter
, LedgerWrapper.Search
, LedgerWrapper.Pagination
, and LedgerWrapper.Actions
:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",})
const rowsAreSelected = Boolean(Object.entries(selected.rows).filter(([key, val]) => val).length);const filters = {groups: [{label: 'Type',key: 'type',items: [{id: 'corporation',value: 'Corporation',label: 'Corporation',},{id: 'personal',value: 'Personal',label: 'Personal',},],},],};return (<Ink.Page><Ink.Page.Body><Ink.Page.Content><Ink.Box bottom="gutter"><Ink.HStack spacing="medium">{rowsAreSelected ? (<Ink.LedgerWrapper.Actions><Ink.Button>Resend emails</Ink.Button></Ink.LedgerWrapper.Actions>) : (<Ink.LedgerWrapper.Actions><Ink.Button>Resend emails</Ink.Button><Ink.LedgerWrapper.Search {...getSearchProps()} /><Ink.LedgerWrapper.Filterid="filter"items={filters}trigger={triggerProps => <Ink.Button type="actions" {...triggerProps} />}{...getFilterProps()}/><Ink.Button>Draft shares</Ink.Button><Ink.Button>Manage stakeholders</Ink.Button></Ink.LedgerWrapper.Actions>)}</Ink.HStack></Ink.Box><Ink.NewTable><Ink.NewTable.Head><Ink.NewTable.Row><Ink.NewTable.HeadCell><Ink.NewCheckbox id="select-all" checked={selected.allRows} onChange={selectableProps.toggleAllRows} /></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey('legal_name')}>Stakeholder</Ink.NewTable.Pin></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey('id')}>ID</Ink.NewTable.Pin></Ink.NewTable.HeadCell></Ink.NewTable.Row></Ink.NewTable.Head><Ink.NewTable.Body>{data.map(d => (<Ink.NewTable.Row key={d.id} selected={selected.rows[d.id]}><Ink.NewTable.Cell preset="checkbox"><Ink.NewCheckbox id={d.id} onChange={selectableProps.toggleRow} checked={selected.rows[d.id]} /></Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.legal_name}</Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.id}</Ink.NewTable.Cell></Ink.NewTable.Row>))}</Ink.NewTable.Body></Ink.NewTable>{!rowsAreSelected && <Ink.LedgerWrapper.Pagination {...getPaginationProps()} />}</Ink.Page.Content></Ink.Page.Body></Ink.Page>)
For more complex implementations, see the following sections.
By default, useLedger
makes requests meant for a standard carta-web DRF-style endpoint. Based on the configuration object, it assembles query arguments such as page
, search
, and ordering
, as well as any filter parameters, and makes a request to the url provided by the user.
The hook sets the following default query parameters to be passed into the url:
{ page: '1', search: '', ordering: '' }
However, you can change those by using the defaultQueryParams
prop. For example, if you want the initial state of the dataset to be sorted by the investor_name
property, the defaultQueryParams
would look like this:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url,defaultQueryParams: { page: "1", search: "", ordering: "investor_name" },})
Note that page size is not controlled by defaultQueryParams
- it is controlled by the pageSize
argument. pageSize
defaults to 50, but can be overridden:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",pageSize: 10,})
You can pass additional query parameters to the hook with the externals
prop, for example:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url,defaultQueryParams: { page: "1", search: "", ordering: "investor_name" },externals: { from_portfolios: "4,5,6,7", request_initiated_before: "2022-01-01" },})
Though useLedger is built to expect DRF endpoints, the hook does give users the flexibility to make other kinds of API requests as well. If the user doesn't want to use a standard DRF-style API, they can use the makeRequest
prop to override the hook's internal request logic and write their own function that returns a promise. One important note about this is that the request function must be defined outside of the component body, or else the hook will hit an infinite recursive loop due to the value of the function changing between renders.
The following is an example that uses the makeRequest
prop:
const response = {results: [{ legal_name: "Momo", pk: 1 },{ legal_name: "Pepita", pk: 2 },{ legal_name: "Bunny", pk: 3 },],};const makeRequest = (url, queryString) => {return new Promise()<typeof response>((resolve, reject) => setTimeout(() => resolve(response), 1000));};const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",makeRequest,})
The hook expects the API response to be an object with a key called "results". If you are using the makeRequest
prop and your expected API response doesn't contain a results
key which correlates to the data that will populate the ledger, use the dataKey
prop to specify which key in your response maps to that data set. That value will be attributed to the data
state variable.
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url,dataKey: "data",makeRequest,})
Alternatively, if you are working with an API that returns an array (as opposed to an object that can be mapped with the dataKey
prop), you can map that returned value into an object in your custom makeRequest
function. Simply wrap the response in an object, with a key called results
:
const makeRequest = async (url, querystring) => {const data = await Axios.get(url);return {results: data,};}
By default, all requests (whether they use the hook's internal logic or are custom requests with the makeRequest prop
) are debounced at 250ms. However, you can override that amount with the debounceTimeout
prop in the configuration object.
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",debounceTimeout: 1000,})
Sometimes API requests don't go according to plan. If your API request returns an error status, you can use the error
state variable to conditionally render something other than the Ledger content:
const { data, error, message } = useLedger({url: 'http:///www.mycoolapi.com',});return (error ? <EmptyState text={message} /> : <NewTable>{ledger content goes here}</NewTable>)
This variable comes directly from the hook's internal state. Message
is the request payload - you can choose whether or not to expose this to users.
useLedger
is built to expect carta-web DRF-style endpoints, which send query arguments through url parameters. For example, if you want to search by the keyword "Meetly," DRF endpoints expect &search=Meetly
to be appended to the url. This behavior is baked into useLedger
with three functions: stateToSearchParam
, stateToOrderingParam
, and stateToFilterParams
, which respectively map out search, ordering, and filter query arguments into the url params.
The default stateToSearchParam
takes in a search query string and returns an object, with a value mapped to the key search
:
const defaultStateToSearchParam = (searchQuery: string) => {return { search: searchQuery };}
The object this function returns is later unpacked into the query string.
Likewise, stateToOrderingParam
takes in an orderingString
(either ascending
or descending
) as well as an orderingKey
string (the key by which you want to order) and returns an object with a value mapped to the key ordering
:
const defaultstateToOrderingParam = (orderingDirection: string, orderingKey: string): OrderingParamType | undefined => {if (!orderingKey || !orderingDirection) {return { ordering: "" };}if (orderingDirection === "descending") {return { ordering: `-${orderingKey}` };}return { ordering: orderingKey };}
And stateToFilterParams
simply returns an object with key/value pairs corresponding to the query filters.
const defaultStateToFilterParam = (filters: GetFilterPropsType) => {return filters;}
If you are using a non-DRF endpoint, you may need to override the way that query arguments are passed into the url. You can override any of these default props in the useLedger
configuration object to create your own logic to map query arguments to the url.
useLedger
handles requests internally, however, you may want to build UI that refetches data after an external event (such as clicking a button or submitting a form).
In order to accomplish this, you can use the refetch
parameter from the hook:
const { refetch } = useLedger({url: "http:///www.mycoolapi.com",});<Ink.Button onClick={() => refetch()}>Reload</Ink.Button>
Much like the useSort hook, the useLeger hook can control sorting - however, unlike the useSort, which is used for front-end sorting, useLedger
handles sorting on the backend through the API request (as is standard with DRF endpoints).
To implement sorting in the initial state of the ledger, pass a sortConfig
into the main configuration object. If you do not want the data to be sorted in the initial state, no sortConfig
is necessary - sorting functionality will still work without it.
The sortConfig
prop has the following type definition:
type SortConfigType = {/* The column by which the data should be sorted */key: string,/* Sort direction */direction: string,}
Therefore, if you want your data to initially be sorted ascending by name, the hook would be configured as follows:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",sortConfig: { key: "name", direction: "ascending" },})
Regardless of whether or not the ledger's initial state is sorted, you can utilize getSortingProps
returned by the hook. Unpack the getter function into a NewTable.Pin
as follows:
<Ink.NewTable.Head><Ink.NewTable.Row><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey("legal_name")}>Stakeholder</Ink.NewTable.Pin></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin {...getSortingProps().sortByKey("id")}>ID</Ink.NewTable.Pin></Ink.NewTable.HeadCell></Ink.NewTable.Row></Ink.NewTable.Head>
sortByKey
can be used to sort the contents of the ledger. Its default behavior alternates the sorting direction between ascending
and descending
programmatically, similar to how the sorting direction alternates when clicking the same column header multiple times.
// Given a ledger currently sorted by 'name' in ascending ordergetSortingProps().sortByKey("name").onClick();// As the ledger was already sorted by 'name' in ascending order, this sorts the ledger by 'name' in descending order.
However, if your project requires a specific sorting direction to be applied, for example, if you have the need to store and restore the state of the ledger, you can pass that order directly into sortByKey
as an additional argument. The possible values are 'ascending' or 'descending'.
// Given a ledger currently sorted by 'name' in ascending ordergetSortingProps().sortByKey("id", "descending").onClick();// The ledger will be sorted by 'id' in descending order.
useLedger
uses the useSelectable hook under the hood.
In order to implement selecting, you must pass a selectableConfig
through to the hook configuration object. Additionally, selecting relies on the dataKey
prop - if you want selecting abilities on a returned value from your data set other than results
, you must specify it with the selectKey
prop.
The selectableConfig
prop is the same config object that is passed into the useSelectable
hook. It has the following type definition:
type UseSelectableConfigType = {/* A unique attribute of the elements in the data array. Default: 'id' */key?: string,/* An array of unique identifiers that will be initially selected */initialState?: string[],}
In the following example, the user would want to initially select rows with the IDs '9', '12', and '13', from the API response key "rows":
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",dataKey: "rows",selectableConfig: { key: "id", initalState: ["9", "12", "13"] },})
Once selecting has been instantiated with the selectableConfig
, you can use the selected
and selectableProps
state variables with NewTable.Row and NewCheckbox component as follows:
<Ink.NewTable data-testid="newtable-selectable"><Ink.NewTable.Head><Ink.NewTable.Row><Ink.NewTable.HeadCell><Ink.NewCheckbox id="select-all" checked={selected.allRows} onChange={selectableProps.toggleAllRows} /></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin>Stakeholder</Ink.NewTable.Pin></Ink.NewTable.HeadCell><Ink.NewTable.HeadCell><Ink.NewTable.Pin>ID</Ink.NewTable.Pin></Ink.NewTable.HeadCell></Ink.NewTable.Row></Ink.NewTable.Head><Ink.NewTable.Body>{data.map((d) => (<Ink.NewTable.Row key={d.id} selected={selected.rows[d.id]}><Ink.NewTable.Cell preset="checkbox"><Ink.NewCheckbox id={d.id} onChange={selectableProps.toggleRow} checked={selected.rows[d.id]} /></Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.legal_name}</Ink.NewTable.Cell><Ink.NewTable.Cell>{loading ? <Ink.Shimmer /> : d.id}</Ink.NewTable.Cell></Ink.NewTable.Row>))}</Ink.NewTable.Body></Ink.NewTable>
LedgerWrapper.Search
getSearchProps
, returned by the useLedger
hook, can be unpacked into the Search component for an easy search implementation.
<Ink.LedgerWrapper.Search {...getSearchProps()} />
getSearchProps
also provides a clearSearch
function that can be used to provide an easy way to clear the search parameter:
<Ink.Button onClick={getSearchProps().clearSearch}>Clear search</Ink.Button>
Because useLedger
defaults to DRF style endpoints, it assumes that the query string argument that correlates to a search string is search.
However, if your API uses another querystring argument for searching, you can denote that with the stateToSearchParam
prop in the configuration object.
For example, if the necessary query argument was called q
, the configuration object would look as follows:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",stateToSearchParam: (searchQuery) => ({ q: searchQuery }),})
The useLedger
hook supports DRF-style page-based pagination. It does not currently support cursor-based pagination. If you have a need for cursor-based pagination, please reach out in the #ink channel.
LedgerWrapper.Pagination
componentgetPaginationProps
, returned by the useLedger
hook, can be unpacked into the LedgerWrapper Pagination component for an easy pagination implementation.
<Ink.LedgerWrapper.Pagination {...getPaginationProps()} />
The Pagination component expects two props: currentPage
and totalPages
. The hook's default implementation knows how to extrapolate those values from a standard DRF-style endpoint response. However, if you are using a custom endpoint, you will need to customize how to extract those values from the API response in the hook's configuaration object.
On a DRF-style endpoint, currentPage
maps out to the page
key on the API response, totalPages
maps out to the num_pages
key on the API response, and totalItems
maps out to the count
key on the API response. If, for example, your API uses currentPage
, totalPageCount
, and totalItems
, instead of page
, num_pages
, and count
your configuration object would look as follows:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",getNumPages: (results) => results.totalPageCount,getCurrentPage: (results) => results.currentPage,getTotalItems: (results) => results.totalItems,})
Likewise, useLedger
defaults pagination to 50 items per page, but you can pass a pageSize
prop into the config object to denote otherwise.
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",pageSize: 10,})
The useLedger
hook provides a getFilterProps
getter that can be used to set up Ledger-style filter functionality. It can be easily implemented with the LedgerWrapper Filter component.
LedgerWrapper.Filter
Unpack getFilterProps
into the LedgerWrapper.Filter
component, along with a filter definition:
const filters = {groups: [{label: "Type",key: "type",items: [{id: "corporation",value: "Corporation",label: "Corporation",},{id: "personal",value: "Personal",label: "Personal",},],},],}
<Ink.LedgerWrapper.Filter id="filter" items={filters} {...getFilterProps()} />
The useLedger
hook operates under the assumption that the user is calling a DRF-style endpoint, in which case filters are appended to an API call as query arguments. If the user is filtering by the type
of Corporation
, their API call would go from looking like:
http://www.mycoolapi.com/?ordering=&page=1&page_size=50&search=
To:
http://www.mycoolapi.com/?ordering=&page=1&page_size=50&search=&type=Corporation
When assembling the API call, the hook unpacks the filter arguments into the query string before making the request.
However, if you are using a custom endpoint, you can specify how to map the state to the filter parameters in the hook's configuration object. For example, if your API expects filters to be attributed to a query argument called filterBy
, the configuration object could be defined as follows:
const {data,loading,getSearchProps,getPaginationProps,getSortingProps,getFilterProps,selected,selectableProps,} = useLedger({url: "http:///www.mycoolapi.com",stateToFilterParams: (filters) => ({ filterBy: filters }),})
In which case the request query string would look like:
http://www.mycoolapi.com/?ordering=&page=1&page_size=50&search=&filterBy={type:Corporation}
getFilterProps
returns two functions to set filter values programatically: setFilterValue
and replaceFilters
.
setFilterValue
sets one filter value at a time:
getFilterProps().setFilterValue("state", "CA")
If you intend to set multiple filter values at once, successive calls setFilterValue
will not reflect in the final state of the ledger, leading to an incomplete filtering state:
getFilterProps().setFilterValue("city", "San Francisco");getFilterProps().setFilterValue("state", "CA");getFilterProps().setFilterValue("country", "US");// The final filtering state will be: { country: 'US' }
To resolve this, use the replaceFilters
function to replace the current filtering state with the one provided as a parameter. This will trigger a re-render that will include all the filter values as expected.
getFilterProps().replaceFilters({ city: "San Francisco", state: "CA", country: "US" });// The final filtering state will be: { city: 'San Francisco', state: 'CA', country: 'US' }
The useLedger
hook has built-in analytics integration with the carta-frontend-platform useAnalytics hook. The analytics provider will be called when the Ledger makes a request.
If your application already utilizes the useAnalytics
hook, you can activate the analytics integration in the useLedger
by passing useAnalyticsConfig
as true
:
const { data } = useLedger({url: "http://www.mycoolapi.com/",useAnalyticsConfig: true,})
By default, we send an Object with the following format to the analytics provider:
{apiResponse: [YOUR_API_RESPONSE],queryString: [THE_CURRENT_STATE_OF_THE_QUERY_STRING]}
For more granular implementations, the useAnalyticsConfig
configuration object also receives the same parameters as the useAnalytics
hook, so you can append specific data to your analytics calls and send custom dispatch
functions of Middlewares:
const { data } = useLedger({url: "http://www.mycoolapi.com/",useAnalyticsConfig: {config: { dispatch: () => console.log("My custom analytics call!") },eventData: { appendedData: "My cool appended data!" },},})
Is this page helpful?