Skip to content

Commit

Permalink
fix(react-query): correctly handling mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
anymaniax committed Oct 7, 2020
1 parent 30de4a8 commit ce8acd9
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 97 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Your client can then be generated by running `npm run generate-fetcher`. Optiona
You can find below some samples on codesandbox

- [react app](https://codesandbox.io/s/orval-sample-react-app-2ytyx)
- [react app with react query](https://codesandbox.io/s/react-app-with-react-query-977qi)
- [react app with react query](https://codesandbox.io/s/react-app-with-react-query-j9f8h)
- [angular app](https://codesandbox.io/s/orval-angular-app-tug6e)

#### Validation of the OpenAPI specification
Expand Down
15 changes: 12 additions & 3 deletions samples/react-app-with-react-query/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import React from 'react';
import React, { useEffect } from 'react';
import { useListPets } from './api/endpoints/petstoreFromFileSpecWithTransformer';
import './App.css';
import { useAuthDispatch } from './auth.context';
import logo from './logo.png';

function App() {
const { data: pets } = useListPets();
const dispatch = useAuthDispatch();
const { data: pets, refetch } = useListPets();

useEffect(() => {
dispatch('token');
setTimeout(() => {
refetch();
}, 2000);
}, []);

return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{pets?.map((pet: any) => (
{pets?.data.map((pet) => (
<p key={pet.id}>{pet.name}</p>
))}
</header>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
*/
import { rest } from 'msw'
import faker from 'faker'
import {
ListPetsParams,
Pet,
Pets,
} from '../model';
import faker from 'faker';
import { rest } from 'msw';

export const getSwaggerPetstoreMSW = () => [
rest.get('*/v:version/pets', (req, res, ctx) => {
rest.get('*/v:version/pets', (req, res, ctx) => {
return res(
ctx.delay(1000),
ctx.status(200, 'Mocked status'),
ctx.json([...Array(faker.random.number({min: 1, max: 10}))].map(() => ({id: (() => faker.random.number({ min: 1, max: 99999 }))(), name: (() => faker.name.lastName())(), tag: (() => faker.name.lastName())()}))),
)
}),rest.post('*/v:version/pets', (req, res, ctx) => {
ctx.json(
[...Array(faker.random.number({ min: 1, max: 10 }))].map(() => ({
id: (() => faker.random.number({ min: 1, max: 99999 }))(),
name: (() => faker.name.lastName())(),
tag: (() => faker.name.lastName())(),
})),
),
);
}),
rest.post('*/v:version/pets', (req, res, ctx) => {
return res(ctx.delay(1000), ctx.status(200, 'Mocked status'));
}),
rest.get('*/v:version/pets/:petId/test/:testId', (req, res, ctx) => {
return res(
ctx.delay(1000),
ctx.status(200, 'Mocked status'),
)
}),rest.get('*/v:version/pets/:petId/test/:testId', (req, res, ctx) => {
return res(
ctx.delay(1000),
ctx.status(200, 'Mocked status'),
ctx.json((() => ({
id: faker.random.number({ min: 1, max: 99 }),
name: faker.name.firstName(),
tag: faker.helpers.randomize([faker.random.word(), undefined]),
}))()),
)
}),]
ctx.json(
(() => ({
id: faker.random.number({ min: 1, max: 99 }),
name: faker.name.firstName(),
tag: faker.helpers.randomize([faker.random.word(), undefined]),
}))(),
),
);
}),
];
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
*/
import { QueryConfig, useQuery } from 'react-query';
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import {
MutationConfig,
QueryConfig,
useMutation,
useQuery,
} from 'react-query';
import { ListPetsParams, Pet, Pets } from '../model';

export const useListPets = <TResult = Pets, TError = unknown>(
export const useListPets = (
params?: ListPetsParams,
version = 1,
queryConfig?: QueryConfig<TResult, TError>,
queryConfig?: QueryConfig<AxiosResponse<Pets>, AxiosError>,
) => {
type Mutator = (url: string, config?: object) => [string, object | undefined];

Expand All @@ -19,33 +25,34 @@ export const useListPets = <TResult = Pets, TError = unknown>(
{ ...config, responseType: 'json' },
];

return useQuery<TResult, TError>(
[
'get',
...mutator(`/v${version}/pets`, {
params,
}),
],
return useQuery<AxiosResponse<Pets>, AxiosError>(
mutator(`/v${version}/pets`, {
params,
}),
(path: string, options: Partial<AxiosRequestConfig>) =>
axios.get<Pets>(path, options),
{ enabled: version, ...queryConfig },
);
};
export const useCreatePets = <TResult = unknown, TError = unknown>(
export const useCreatePets = (
version = 1,
queryConfig?: QueryConfig<TResult, TError>,
mutationConfig?: MutationConfig<AxiosResponse<unknown>, AxiosError>,
) => {
return useQuery<TResult, TError>(['post', `/v${version}/pets`, undefined], {
enabled: version,
...queryConfig,
});
return useMutation<AxiosResponse<unknown>, AxiosError>(
() => axios.post<unknown>(`/v${version}/pets`),
mutationConfig,
);
};
export const useShowPetById = <TResult = Pet, TError = unknown>(
export const useShowPetById = (
petId: string,
testId: string,
version = 1,
queryConfig?: QueryConfig<TResult, TError>,
queryConfig?: QueryConfig<AxiosResponse<Pet>, AxiosError>,
) => {
return useQuery<TResult, TError>(
['get', `/v${version}/pets/${petId}/test/${testId}`],
return useQuery<AxiosResponse<Pet>, AxiosError>(
[`/v${version}/pets/${petId}/test/${testId}`],
(path: string, options: Partial<AxiosRequestConfig>) =>
axios.get<Pet>(path, options),
{ enabled: version && petId && testId, ...queryConfig },
);
};
2 changes: 1 addition & 1 deletion samples/react-app-with-react-query/src/api/model/error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
Expand Down
2 changes: 1 addition & 1 deletion samples/react-app-with-react-query/src/api/model/pet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
Expand Down
2 changes: 1 addition & 1 deletion samples/react-app-with-react-query/src/api/model/pets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Generated by orval v2.6.0 🍺
* Generated by orval v3.0.0 🍺
* Do not edit manually.
* Swagger Petstore
* OpenAPI spec version: 1.0.0
Expand Down
66 changes: 66 additions & 0 deletions samples/react-app-with-react-query/src/auth.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import axios from 'axios';
import React, {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from 'react';
type Dispatch = (Auth: string) => void;

type AuthProviderProps = { children: ReactNode; initialState?: string | null };

const AuthContext = createContext<string | null>(null);
const AuthDispatchContext = createContext<Dispatch | null>(null);

const AuthProvider = ({ children, initialState = null }: AuthProviderProps) => {
// it's a quick demo with useState but you can also have a more complexe state with a useReducer
const [token, setToken] = useState(initialState);

useEffect(() => {
const interceptorId = axios.interceptors.request.use(
(config) => {
return {
...config,
baseURL: '', // use an env or your api url
headers: token
? {
...config.headers,
Authorization: `Bearer ${token}`,
}
: config.headers,
};
},
(error) => {
Promise.reject(error);
},
);

return () => {
axios.interceptors.request.eject(interceptorId);
};
}, [token]);

return (
<AuthContext.Provider value={token}>
<AuthDispatchContext.Provider value={setToken}>
{children}
</AuthDispatchContext.Provider>
</AuthContext.Provider>
);
};

const useAuth = (): string | null => {
return useContext<string | null>(AuthContext);
};

const useAuthDispatch = (): Dispatch => {
const context = useContext<Dispatch | null>(AuthDispatchContext);

if (context === null) {
throw new Error('useAuthDispatch must be used within a AuthProvider');
}
return context;
};

export { AuthProvider, useAuth, useAuthDispatch };
33 changes: 7 additions & 26 deletions samples/react-app-with-react-query/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,24 @@
import axios, { AxiosRequestConfig, Method } from 'axios';
import React from 'react';
import ReactDOM from 'react-dom';
import { QueryCache, ReactQueryCacheProvider } from 'react-query';
import App from './App';
import { AuthProvider } from './auth.context';
import './index.css';
import * as serviceWorker from './serviceWorker';

const defaultQueryFn = async (
method: Method,
url: string,
options: Partial<AxiosRequestConfig>,
) => {
const { data } = await axios({
url,
method,
...options,
});

return data;
};

// provide the default query function to your app with defaultConfig
const queryCache = new QueryCache({
defaultConfig: {
queries: {
queryFn: defaultQueryFn,
},
},
});
const queryCache = new QueryCache();

if (process.env.NODE_ENV === 'development') {
require('./mock');
}

ReactDOM.render(
<React.StrictMode>
<ReactQueryCacheProvider queryCache={queryCache}>
<App />
</ReactQueryCacheProvider>
<AuthProvider>
<ReactQueryCacheProvider queryCache={queryCache}>
<App />
</ReactQueryCacheProvider>
</AuthProvider>
</React.StrictMode>,
document.getElementById('root'),
);
Expand Down
4 changes: 2 additions & 2 deletions src/core/generators/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import {
} from '../../types/getters';

const generateBodyOptions = (body: GetterBody, verb: Verbs) => {
if (!VERBS_WITH_BODY.includes(verb)) {
if (!VERBS_WITH_BODY.includes(verb) || !body.implementation) {
return '';
}

if (body.isBlob) {
return '\n formData,';
}

return `\n ${body.implementation || 'undefined'},`;
return `\n ${body.implementation},`;
};

const generateQueryParamsOptions = (
Expand Down
Loading

0 comments on commit ce8acd9

Please sign in to comment.