I am trying to wrap getServersideProps with auth handler function, but keep getting this error:
TypeError: getServerSideProps is not a function
my wrapper looks like this:
export async function protect(gssp) {
return async (context) => {
const {req, res} = context;
const auth = await authHandler(req);
if (!auth.authenticated) {
res.statusCode = 302;
res.setHeader('Location', '/');
return;
}
context.auth = auth;
return await gssp(context);
}
}
and on the page, getServerSideProps looks like this:
export const getServerSideProps = protect(async function(context) {
return {
props: {
auth: context.auth
}
}
})
The call protect(...) actually returns a promise, rather than a function, since you explicitly declared it as async. To fix the issue you can simply remove the async from that function.
export function protect(gssp) {
// Remaining code untouched
}
Related
I am doing a simple app and I am using mock-json-server to simulate http request.
I have defined a function to get the info I need :
import { ref } from 'vue'
const getScores = () => {
const scoringPass = ref([])
const error = ref(null)
const load = async () => {
try {
let data = await fetch('http://localhost:8000/scores', {
method: 'get',
headers: {
'content-type': 'application/json'
}})
if (!data.ok) {
throw Error('no data available')
}
scoringPass.value = await data.json()
console.log(scoringPass.value)
} catch (err) {
error.value = err.message
console.log(error.value)
}
}
return { scoringPass, error, load }
}
export default getScores
And I call it in the setup function of my component :
<script lang="ts">
import { defineComponent } from 'vue'
import Pass from '#/components/Pass.vue'
import getScores from '../composables/getScores.js'
export default defineComponent({
setup() {
const numeroDossier = '25020230955000004'
const { scoringPass, error, load } = getScores()
load()
return { numeroDossier, scoringPass, error }
},
components: {
Pass,
},
})
</script>
In the console.log(scoringPass.value) in the function, I can see the data. but the load() function in the setup part does not work and I can't figure out why. It is called though, but I can't get the data.
When I do console.log(load()), I get :
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined
Any help appreciated.
Cheers.
load() is async, so its return value is a Promise. You have to await the call to get the underlying data. However, load() doesn't actually return anything, so you still wouldn't see any data. If you want load() to provide the initial value of scoringPass, load() should return that:
const load = async () => {
try {
⋮
return scoringPass.value
} catch (err) {
⋮
return null
}
}
To get the result of load(), you can wrap the call in an async function to await the call; or chain a .then() callback:
export default defineComponent({
setup() {
⋮
const logLoadResults = async () => console.log(await load())
logLoadResults()
// or
load().then(results => console.log(results))
}
})
Don't mark setup() as async because that would make your component an async component, requiring a <Suspense> in a parent component to render it.
I have a problem where useQuery is always running in my application and I don't why
In my component
import { GET_DATA } from 'apiCalls';
const { loading, error, data } = useQuery('getData', GET_DATA(token));
In my api call
export const GET_DATA = async (token) => {
try {
const res = await axios.get(`${process.env.REACT_APP_SERVER}/api/...`, {
headers: {'auth-token': token},
});
console.log(res);
return res.data;
} catch (err) {
console.log('Error getting data');
return err;
}
}
when I debug my app. The function GET_DATA is always running ALL the time. what is the issue here ?
You must provide the useQuery only the function it wants to run, you must not call it inside useQuery. Provide the token to GET_DATA this way:
EDIT
As #tkdodo said we don't need to use the async function.
const { loading, error, data } = useQuery('getData', ()=>{
return GET_DATA(token);
});
The first solution I provided was this:
const { loading, error, data } = useQuery('getData', async()=>{
const data = await GET_DATA(token);
return data;
});
The root cause is the same as in React-Query, useQuery returns undefined only after loading is complete
The queryFn needs to be a function that returns a promise. GET_DATA does that. But by doing
GET_DATA(token) you directly invoke the function. So you’ll likely want:
() => GET_DATA(token) instead.
Try the following:
// apiCalls.js
export const getData = async (token) => {
try {
const res = await axios.get(`${process.env.REACT_APP_SERVER}/api/...`, {
headers: {'auth-token': token},
});
return res.data;
} catch (err) {
console.log('Error getting data');
return err;
}
// Component.js
import { getData } from 'apiCalls';
function Component(){
const { loading, error, data } = useQuery(
'getData',
()=>GET_DATA(token)
);
return (
<div>...</div>
)
}
useQuery should run in the component and the second parameter should not be a promise, but a function that returns a promise.
I am trying to retrieve the authConfig from an API-endpoint. Inside my app component, I request the function from a service.
this.userDetailService.getAuthConfig().then(config => {
this.oauthService.configure(config);
this.oauthService.initAuthorizationCodeFlow();
});
Then in my service, the auth configs are set up and returned to the app component. I use .then on getAuthConfig, so the config-object is existing, when I need it to configure the oauthService. When I debug it, I see that .configure is called with an empty object. Why is configure called, before getAuthConfig returns a vaule?
getEnvs(): Promise<any> {
return this.http.get('/backend').toPromise();
}
async getAuthConfig(): Promise<any> {
this.getEnvs().then((data) => {
const env = data.env;
const authConfig: AuthConfig = {
loginUrl: env.authorizationEndpoint,
redirectUri: env.redirectUris,
clientId: env.clientId,
scope: '',
oidc: false
};
return (authConfig);
});
}
You need to return the created promise from getAuthConfig so the caller of getAuthConfig can correctly await the promises chain generated within getAuthConfig:
async getAuthConfig(): Promise<any> {
return this.getEnvs().then((data) => {
//^^^^^^
// ...
})
You would use it in another async method within the same class as:
async whatever() {
// this will now await for the promise chain
// within getAuthConfig and return the result
const authConfig = await this.getAuthConfig();
}
Since getAuthConfig is an async function, you could optionally clean it up by taking advantage of that:
async getAuthConfig(): Promise<AuthConfig> {
const { env } = await this.getEnvs();
return {
loginUrl: env.authorizationEndpoint,
redirectUri: env.redirectUris,
clientId: env.clientId,
scope: '',
oidc: false
};
}
I'm testing apiMiddleware that calls its helper function callApi. To prevent the call to actual callApi which will issue the API call, I mocked the function. However, it still gets called.
apiMiddleware.js
import axios from 'axios';
export const CALL_API = 'Call API';
export const callApi = (...arg) => {
return axios(...arg)
.then( /*handle success*/ )
.catch( /*handle error*/ );
};
export default store => next => action => {
// determine whether to execute this middleware
const callAPI = action[CALL_API];
if (typeof callAPI === 'undefined') {
return next(action)
}
return callAPI(...callAPI)
.then( /*handle success*/ )
.catch( /*handle error*/ );
}
apiMiddleware.spec.js
import * as apiMiddleware from './apiMiddleware';
const { CALL_API, default: middleware, callApi } = apiMiddleware;
describe('Api Middleware', () => {
const store = {getState: jest.fn()};
const next = jest.fn();
let action;
beforeEach(() => {
// clear the result of the previous calls
next.mockClear();
// action that trigger apiMiddleware
action = {
[CALL_API]: {
// list of properties that change from test to test
}
};
});
it('calls mocked version of `callApi', () => {
const callApi = jest.spyOn(apiMiddleware, 'callApi').mockReturnValue(Promise.resolve());
// error point: middleware() calls the actual `callApi()`
middleware(store)(next)(action);
// assertion
});
});
Please ignore the action's properties and argument of callApi function. I don't think they are the concern of the point I'm trying to make.
Tell me if you need further elaboration.
The jest mocking only works on imported functions. In your apiMiddleware.js the default function is calling callApi variable, not the "exported" callApi function. To make the mock work, move callApi into its own module, and import it in apiMiddleware.js
Good question!
I solved my issues converting my code to a Class, example:
// Implementation
export class Location {
getLocation() {
const environment = this.getEnvironmentVariable();
return environment === "1" ? "USA" : "GLOBAL";
}
getEnvironmentVariable() {
return process.env.REACT_APP_LOCATION;
}
}
// Test
import { Location } from "./config";
test('location', () => {
const config = new Location();
jest.spyOn(config, "getEnvironmentVariable").mockReturnValue("1")
const location = config.getLocation();
expect(location).toBe("USA");
});
I get the error "variable uploadImageAsync cannot be found"
uploadImageAsync = async (uri) => {
console.log("In upload image asnyc!");
}
And this is where I call it from.
_handleImagePicked = async pickerResult => {
let uploadResponse, uploadResult;
this.setState({ uploading: true });
if (!pickerResult.cancelled) {
uploadResponse = await uploadImageAsync(pickerResult.uri);
uploadResult = await uploadResponse.json();
this.setState({ image: uploadResult.location });
}
this.setState({ uploading: false });
};
How can I get around this?
So far I've tried:
async function uploadImageAsync(uri) {
I've also tried:
async uploadImageAsync(uri) {
If the uploadImageAsync function defined in the same component, you need call this.uploadImageAsync.
Otherwise, you must import it from the other module
or define the function outside the component in the same file.