Skip to main content

2. Create state

We are going to create a list view for PurchaseOrderShipments and we will use the ListPurchaseOrderShipements service.

2.1 Setup service state#

First we create the service state we need:

components/purchase-order-shipments/overview/purchase-order-shipments.state.ts
import { builders } from '@springtree/eva-sdk-react-recoil';import { Core } from '@springtree/eva-services-core';import { selector } from 'recoil';
// Default request for the ListPurchaseOrderShipments//export const purchaseOrderShipmentsDefaultRequest: EVA.Core.ListPurchaseOrderShipments =  {    PageConfig: {      Limit: 25,      Start: 0,      Filter: {},    },  };
// Setup service state for the ListPurchaseOrderShipments service and pass the default request//export const purchaseOrderShipmentsServiceState = builders.buildServiceState({  service: Core.ListPurchaseOrderShipments,  allowEmptyRequest: false,  key: 'PurchaseOrderShipmentsList',  requireEmployee: true,  requireLoggedIn: true,  defaultRequest: purchaseOrderShipmentsDefaultRequest,});
// Select the paged result from the response, so it does not need to be unpacked in the component//export const purchaseOrderShipmentsResult = selector({  key: 'PurchaseOrderShipmentsList:Result',  get: ({ get }) => {    const response = get(purchaseOrderShipmentsServiceState.response);    return response?.Result.Page;  },});
// Select the Total of the results. Total of the results is mandatory in the Table component.//export const purchaseOrderShipmentsTotal = selector<number | undefined>({  key: 'PurchaseOrderShipmentsList:Total',  get: ({ get }) => {    const response = get(purchaseOrderShipmentsServiceState.response);    return response?.Result?.Total ?? 0;  },});

We intentionally add the service state with the component that uses it, as we do not expect that the same state part will be used in other places of the app. If it does, then we use the store folder.

Curious how builders work? You can find the eva-sdk-repo docs here.

2.2 Create pagination selectors#

In most of the cases we paginate the results. As you can see in the default request, we limit the results by 25 results at a time ,and we start at the first (0) result. Let's create helper state for this, so we use that in the table.

components/purchase-order-shipments/overview/purchase-order-shipments.state.ts
...
// State helper which can change the amount of results//export const purchaseOrderShipmentsLimit = selector<number | undefined>({  key: 'PurchaseOrderShipmentsList:Request:Limit',  get: ({ get }) => {    const request = get(purchaseOrderShipmentsServiceState.request);    return request?.PageConfig?.Limit ?? defaultOrderListRequest.PageConfig?.Limit!;  },  set: ({ set }, newValue) => {    if (newValue instanceof DefaultValue) {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: {          ...current?.PageConfig,          Limit: defaultOrderListRequest?.PageConfig?.Limit!,        },      }));    } else {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: { ...current?.PageConfig, Limit: newValue },      }));    }  },});
// State helper which is used in the pagination of the table//export const purchaseOrderShipmentsStart = selector<number | undefined>({  key: 'PurchaseOrderShipmentsList:Request:Start',  get: ({ get }) => {    const request = get(purchaseOrderShipmentsServiceState.request);    return request?.PageConfig?.Start ?? defaultOrderListRequest.PageConfig?.Start!;  },  set: ({ set }, newValue) => {    if (newValue instanceof DefaultValue) {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: {          ...current?.PageConfig,          Start: defaultOrderListRequest?.PageConfig?.Start!,        },      }));    } else {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: { ...current?.PageConfig, Start: newValue },      }));    }  },});

2.3 Creating Filter selectors#

We need a selector that we could use to update the Filter on the request.

components/purchase-order-shipments/overview/purchase-order-shipments.state.ts
...export const purchaseOrderShipmentsFilter = selector<  Partial<EVA.Core.ListPurchaseOrderShipmentsFilter> | undefined>({  key: 'PurchaseOrderShipmentsList:Request:Filter',  get: ({ get }) => {    const request = get(purchaseOrderShipmentsServiceState.request);    return request?.PageConfig?.Filter ?? defaultOrderListRequest.PageConfig?.Filter;  },  set: ({ set }, newValue) => {    if (newValue instanceof DefaultValue) {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: {          ...current?.PageConfig,          Filter: defaultOrderListRequest?.PageConfig?.Filter,          Start: 0,        },      }));    } else {      set(purchaseOrderShipmentsServiceState.request, (current) => ({        ...current,        PageConfig: { ...current?.PageConfig, Filter: newValue, Start: 0 },      }));    }  },});

Now everytime the filter gets updated, the Start will also be set to 0, so you always will see the first results when changing filters.

If you would like seperate selectors for properties that are inside the filter selector, that is also possible, but not required. You could do something like this:

components/purchase-order-shipments/overview/purchase-order-shipments.state.ts
...export const purchasOrderShipmentsFilterOrderID = selector<number | undefined>({  key: 'PurchaseOrderShipmentsList:Request:Filter:OrderID',  get: ({ get }) => {    const filter = get(purchaseOrderShipmentsFilter);    return filter?.OrderID;  },  set: ({ set }, newValue) => {    if (newValue instanceof DefaultValue) {      set(purchaseOrderShipmentsFilter, (current) => ({        ...current,        OrderID: purchaseOrderShipmentsDefaultRequest.PageConfig?.Filter?.OrderID,      }));    } else {      set(purchaseOrderShipmentsFilter, (current) => ({ ...current, OrderID: newValue }));    }  },});

Notice that we do not set the Start property in this selector, because it already uses the FilterSelector. The FilterSelector will take care of setting Start to 0.