useProtrakAPI
Reference
useProtrakApi hook simplifies the code by encapsulating the boilerplate and authentication required to make a Protrak REST API call.
const { protrakUtils, protrakComponents }
= React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;
const { state, run } = useProtrakApi({
requestConfig: getInstancesPromise,
instanceId
});
Parameters
- First parameters should be a function that returns an object with following properties:
requestConfig: Provides a declarative way to specify the API to be called. Theendpointshould be a valid Protrak API endpoint.- The
configcan be used to provider other parameters that will be passed in request. Anyargswill be passed as a parameter object to theconfigfunction. Notice howinstanceIdis passed in below example.const getInstancesPromise = ({ instanceId }) => {
return {
endpoint: `instances/${instanceId}/relatedInstances`,
config: {
method: 'GET',
params: {
'attributes[0]': 'Efforts',
skip: 0,
take: 9999,
},
},
};
};
const { protrakUtils, protrakComponents }
= React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;
const { state: response } = useProtrakApi({
requestConfig: getInstancesPromise,
instanceId,
}); promiseFn: If you want to create a custom Promise combining or chaining multiple API calls, then usepromiseFn.- To invoke Protrak APIs, another util
protrakApiClientneeds to be used. See example code below:const { state, run } = useProtrakApi({
promiseFn: getPatentsPromise,
instanceId,
params: {
instanceTypeName: instanceType,
attributes: ['Name', 'TrackingId'],
}
});
const getPatentsPromise = async ({ instanceId, params }) => {
let endpoint = `instances/${instanceId}`;
let res = await providedProtrakUtils.protrakApiClient(endpoint, {
method: 'GET',
params: params,
});
return res;
}; args: Any additional properties orargswill be passed as a parameter object to theconfigfunction.
- Second parameter allows user to pass options object
defer: boolean value that decides whether or not the promise should be immediately invoked on render. True value indicates that the promise will not be immediately invoked. To invoke a deferred promise, use therunmethod in the returned object.initialData: Any object passed asinitialDatawill be returned asstate.dataimmediately after theuseProtrakAPIis called. In subsequent renders, thestate.datawill be overwritten with the API response.
onSuccess: A callback function can be passed when the promise is executed successfully. Theresponse.datawill be passed to this callback.onError: A callback function can be passed when the promise is executed successfully. The responseerrorwill be passed to this callback.
Returns
useProtrakApi returns an Object with following properties:
state: State is an object that contains:isLoading: Boolean to indicate whether the promise execution is in progress.isError: Boolean to indicate whether the promise execution has failed.data: Object containing promise return value (API response, typically)isFulfilled: Boolean to indicate whether the promise execution is completed successfully.
run: A function that can be called to trigger execution of the promise. Typically used along with thedefer: trueoption.resetState: A function that can be called to reset the execution state.abort: A function that can be called to abort an ongoing promise execution.
Caveats
- Ensure that the functions
promiseFnandrequestConfigare defined outside of the custom widget function, to ensure that their identity does not change on re-renders. If the function identity changes on every render, it will trigger an infinite rendering loop. - Use only one of
requestConfigandpromiseFn. IfrequestConfigis passed, thepromiseFneven if passed is ignored. - Ensure that loading and error states are handled correctly, showing appropriate feedback to the user.
Usage
const getInstancesPromise = ({ instanceId }) => {
return {
endpoint: `instances/${instanceId}/relatedInstances`,
config: {
method: 'GET',
params: {
'attributes[0]': 'Efforts',
'RelationFilters[0].TypeName': 'ProjectAllocation',
skip: 0,
take: 9999
}
}
};
};
function CumulativeEffortWidget(pageContext) {
const { instanceType, instanceId } = pageContext;
const { protrakUtils, protrakComponents }
= React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;
const { state: response } = useProtrakApi({
requestConfig: getInstancesPromise,
instanceId
});
function getCumulativeEffort(items) {
//business logic here
}
const { Spinner, Box } = protrakComponents;
if (response.isLoading) {
return (
<Box padding="1rem">
<Spinner />
</Box>
);
}
if (response.isError) {
return (
<Box padding="1rem">
<h3>Server error!</h3>
</Box>
);
}
if (response.data) {
if (!response.data.items || !response.data.items.length) {
return (
<Box padding="1rem">
<h3>No tasks found!</h3>
</Box>
);
}
const [submittedEffort, approvedEffort]
= getCumulativeEffort(response.data.items);
return (
<Box padding="1rem" direction="Column">
<h3>Count of tasks: {response.data.totalCount}</h3>
<h3>Submitted effort (hours): {submittedEffort}</h3>
<h3>Approved effort (hours): {approvedEffort}</h3>
<h3>Total effort (hours): {submittedEffort + approvedEffort}</h3>
</Box>
);
}
return null;
}
Troubleshooting
- The API calls are authenticated with the logged-in user's context. Ensure that the user has necessary access and permissions for the instance or instances being processed in the custom widget.
- Ensure that the promise functions are not recreated on every render to avoid infinite render loops.
- Examine the request parameters in browser devtools to debug that correct request is being made to the server.