How to get data from react query "useQuery" hook in a specific type - react-query

When we get data from useQuery hook, I need to parse the data a specific type before it return to user. I want data which return from useQuery hook should be of "MyType" using the parsing function i created below. I am unable to find method to use my parsing function. Is there any way to do it? I don't want to rely on schema structure for data type.
type MyType = {
id: number;
//some more properties
}
function parseData(arr: any[]): MyType[]{
return arr.map((obj, index)=>{
return {
id: arr.id,
//some more properties
}
})
}
const {data} = await useQuery('fetchMyData', async ()=>{
return await axios.get('https://fake-domain.com')
}
)

I would take the response from the api and transform it inside the queryFn, before you return it to react-query. Whatever you return winds up in the query cache, so:
const { data } = await useQuery('fetchMyData', async () => {
const response = await axios.get('https://fake-domain.com')
return parseData(response.data)
}
)
data returned from useQuery should then be of type MyType[] | undefined
There are a bunch of other options to do data transformation as well, and I've written about them here:
https://tkdodo.eu/blog/react-query-data-transformations

I think you should create your own hook and perform normalisation there:
const useParseData = () => {
const { data } = await useQuery('fetchMyData', async () => {
return await axios.get('https://fake-domain.com')
}
return parseData(data)
}
And where you need this data you could just call const parsedData = useParseData()

Related

Curious why we can't get at the args in a query, in the onSuccess?

So, I have some ancilliary behaviors in the onSuccess, like analytics and such. And I need to pass in to the tracking, not only the result of the query/mutation (mutation in this case), BUT also an arg I passed in. Seems I can only do it if I attach it to the return "data"?
export default function useProductToWishList () {
const queryClient = useQueryClient();
return useMutation(
async ({ product, email }) => {
const data = await product.addWishList({ product, email });
if (data.status === 500 || data.err) throw new Error(data.err);
return data;
},
{
onSuccess:(data) => {
const { product: response = {} } = data?.data ?? {};
queryClient.setQueryData(['products'], {...response });
analytics(response, email); // HERE. How can I get at email?
}
}
)
}
seems odd to do, when I don't need it for the response, but for a side effect. Any thoughts?
return { ...data, email }
for useMutation, the variables are passed as the second argument to onSuccess. This is documented in the api docs. So in your example, it's simply:
onSuccess: (data, { product, email }) =>

How do I make 2 (or more) calls with Adobe PDF Services and skip using the file system (in between?)

It's fairly simple to make one call to Adobe PDF Services, get the result, and save it, for example:
// more stuff above
exportPdfOperation.execute(executionContext)
.then(result => result.saveAsFile(output))
But if I want to do two, or more, operations, do I need to keep saving the result to the file system and re-providing it (is that even a word ;) to the API?
So this tripped me up as well. In most demos, you'll see:
result => result.saveAsFile()
towards the end. However, the object passes to the completed promise, result, is a FileRef object that can then be used as the input to another call.
Here's a sample that takes an input Word doc and calls the API method to create a PDF. It then takes that and runs OCR on it. Both methods that wrap the API calls return FileRefs, so at the end I saveAsFile on it. (Note, this demo is using v1 of the SDK, it would work the same w/ v2.)
const PDFToolsSdk = require('#adobe/documentservices-pdftools-node-sdk');
const fs = require('fs');
//clean up previous
(async ()=> {
// hamlet.docx was too big for conversion
const input = './hamlet2.docx';
const output = './multi.pdf';
const creds = './pdftools-api-credentials.json';
if(fs.existsSync(output)) fs.unlinkSync(output);
let result = await createPDF(input, creds);
console.log('got a result');
result = await ocrPDF(result, creds);
console.log('got second result');
await result.saveAsFile(output);
})();
async function createPDF(source, creds) {
return new Promise((resolve, reject) => {
const credentials = PDFToolsSdk.Credentials
.serviceAccountCredentialsBuilder()
.fromFile(creds)
.build();
const executionContext = PDFToolsSdk.ExecutionContext.create(credentials),
createPdfOperation = PDFToolsSdk.CreatePDF.Operation.createNew();
// Set operation input from a source file
const input = PDFToolsSdk.FileRef.createFromLocalFile(source);
createPdfOperation.setInput(input);
let stream = new Stream.Writable();
stream.write = function() {
}
stream.end = function() {
console.log('end called');
resolve(stream);
}
// Execute the operation and Save the result to the specified location.
createPdfOperation.execute(executionContext)
.then(result => resolve(result))
.catch(err => {
if(err instanceof PDFToolsSdk.Error.ServiceApiError
|| err instanceof PDFToolsSdk.Error.ServiceUsageError) {
reject(err);
} else {
reject(err);
}
});
});
}
async function ocrPDF(source, creds) {
return new Promise((resolve, reject) => {
const credentials = PDFToolsSdk.Credentials
.serviceAccountCredentialsBuilder()
.fromFile(creds)
.build();
const executionContext = PDFToolsSdk.ExecutionContext.create(credentials),
ocrOperation = PDFToolsSdk.OCR.Operation.createNew();
// Set operation input from a source file.
//const input = PDFToolsSdk.FileRef.createFromStream(source);
ocrOperation.setInput(source);
let stream = new Stream.Writable();
stream.end = function() {
console.log('end called');
resolve(stream);
}
// Execute the operation and Save the result to the specified location.
ocrOperation.execute(executionContext)
.then(result => resolve(result))
.catch(err => reject(err));
});
}

Flow(InferError): Cannot call await with 'axios.get(...)' bound to 'p'

I'm getting some Flow errors using axios.
Cannot call await with 'axios.get(...)' bound to 'p' because:
Either property 'error_message' is missing in 'AxiosXHR'.
Or property 'data' is missing in 'Promise'
Here is my code, with an attempted type annotation. (Same error without the AxiosPromise<Object> annotation.) The error is on axios.get(url).
async handleAddressChange(): AxiosPromise<Object> {
const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${GoogleMapsApiKey}&input=${this.state.address}`;
try {
const { data, error_message } = await axios.get(url);
if (error_message) throw Error(error_message);
this.setState({
addressPredictions: data.predictions,
showPredictions: true
});
} catch (err) {
console.warn(err);
}
}
Funny thing is that in another file axios gives no Flow problems:
export async function loginWithApi(creds: AuthParams) {
const res = await axios.get(ApiUrls.login, { params: creds });
return res.data;
}
I have import type { AxiosPromise, $AxiosXHR } from "axios"; in my file.
Anyone know how to fix this?
In case of error there will be no error_message in returned payload, but the error goes into the catch block.
Also, the handleAddressChange does not returns AxiosPromise, instead it returns implicit promise, as it defined with async
So, something like this:
async handleAddressChange(): Promise<void> {
const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${GoogleMapsApiKey}&input=${this.state.address}`;
try {
const { data } = await axios.get(url);
this.setState({
addressPredictions: data.predictions,
showPredictions: true
});
} catch (err: AxiosError) {
new Error(err);
}
}
Might work for you. Note the AxiosError definition.
One extra note is that you can add returned payload into the AxiosPromise generic, i.e.:
type TExpectedLoginResponse = {
ok: boolean,
token: string
}
export async function loginWithApi(creds: AuthParams): AxiosPromise<TExpectedLoginResponse> {
const res = await axios.get(ApiUrls.login, { params: creds });
return res.data; // so now flow knows that res.data is type of TExpectedLoginResponse
}
Hope it helps.

Why TextDocumentContentProvider dont call provideTextDocumentContent on update when query params changes?

as title says, when i wanna update TextDocumentContentProvider with different query params by calling update method provideTextDocumentContent is not called...
only way i managed to get it working was with same URI as in calling
vscode.commands.executeCommand('vscode.previewHtml', URI, 2, 'Storybook');
relevant part of code:
// calculates uri based on editor state - depends on actual caret position
// all uris will start with 'storybook://preview'
function getPreviewUri(editor: vscode.TextEditor): vscode.Uri;
// transforms uri, so web server will understand
// ex: 'storybook://preview?name=fred' -> 'http://localhost:12345/preview/fred?full=1'
function transformUri(uri: vscode.Uri): vscode.Uri;
class StorybookContentProvider implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string {
var httpUri = transformUri(uri);
return `<iframe src="${httpUri}" />`;
}
onDidChange = new vscode.EventEmitter<vscode.Uri>();
update(uri: vscode.Uri) {
this.onDidChange(uri);
}
}
export function activate(context: vscode.ExtensionContext)
{
vscode.workspace.onDidChangeTextDocument(
(e: vscode.TextDocumentChangeEvent) => {
if (e.document === vscode.window.activeTextEditor.document) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
vscode.window.onDidChangeTextEditorSelection(
(e: vscode.TextEditorSelectionChangeEvent) => {
if (e.textEditor === vscode.window.activeTextEditor) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
const provider = new StorybookContentProvider();
context.subscriptions.push(
vscode.commands.registerCommand('extension.showStorybook', () => {
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.parse('storybook://preview'), 2, 'Storybook')
}),
vscode.workspace.registerTextDocumentContentProvider('storybook', provider)
);
}

axios get - multiple api calls with response from first api to subsequent calls

How to make multiple api calls with axios - when I have to pass a value from my first api response to all subsequent calls. I have 2 other calls to be made inside getData function where I have to pass a value from y first api response {data} - how to chain multipl requests with axios? 2 next calls are dependent on first call - they are not dependant on each other - they can happen in parallel - the only issue I have is - I am not able to pass the response data to the subsequent end-points.
import Request from 'axios';
export function getData() {
return async function getData(dispatch) {
const { data } = await getDatafromService();
dispatch({ type: 'Data_fetch', payload: data });
};
}
async function getDatafromService() {
const endpoint = "api-url";
return Request.get(endpoint);
}
Something like this should work for an overall structure.
The async function getData will ultimately return an array of the responses from the last two requests.
import Request from 'axios';
export function getData() {
return async function getData(dispatch) {
const { data } = await getDatafromService();
return Promise.all([
sendDataToFirstService(data),
sendDataToSecondService(data),
])
};
}
function getDatafromService() {
const endpoint = "api-url";
return Request.get(endpoint);
}
function sendDataToFirstService(data) {
const endpont = "first-url";
return Request.post(endpoint, data)
}
function sendDataToSecondService(data) {
const endpont = "second-url";
return Request.post(endpoint, data)
}
Note that you may need to modify the data received from the original get request before passing it to the next two.
You can do this by chaining .then onto the Promise like so...
function getDatafromService() {
const endpoint = "api-url";
return Request.get(endpoint).then(({data}) => modify(data));
}