Error when using the next.js API with RecoilJS - reactjs

I'm trying to initialise a Recoil atom using the Next API but encountering an error.
The default value is set to the function that makes the call to the Next API endpoint, which then retrieves some data from firebase.
When I then try to use the atom in a component using useRecoilState and log its value, I get this error:
error - TypeError [ERR_INVALID_URL]: Invalid URL
at new NodeError (node:internal/errors:371:5)
at onParseError (node:internal/url:552:9)
at new URL (node:internal/url:628:5)
at dispatchHttpRequest (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/adapters/http.js:169:20)
at new Promise (<anonymous>)
at httpAdapter (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/adapters/http.js:105:10)
at Axios.dispatchRequest (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/core/dispatchRequest.js:46:10)
at Axios.request (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/core/Axios.js:140:33)
at wrap (file:///C:/Users/JoelMcMahon/projects/amt/amtAdmin/amt-admin-utility-v2/node_modules/axios/lib/helpers/bind.js:5:15)
at eval (webpack-internal:///./src/Modules/getUsers.ts:12:58) {
input: '/api/users/getUsersFromDatabase',
code: 'ERR_INVALID_URL',
page: '/'
}
I've also tried setting the default value of the atom as a selector that makes the query using async await but still get the error.
Here are the relevant files:
atoms.js:
import { atom } from "recoil";
import { getUsers } from "../Modules/getUsers";
export const userListPromise = atom({
key: "userListPromise",
default: getUsers(),
});
getUsers.ts:
import axios from "axios";
export const getUsers = (): Promise<any> => {
return new Promise((resolve, reject) => {
axios({
method: "GET",
url: "/api/users/getUsersFromDatabase",
})
.then((response) => {
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
};
getUsersFromDatabase.ts
import axios from "axios";
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const url = //My Cloud Function URL//;
axios({
method: "GET",
url: url,
})
.then((response) => {
res.status(200).json(response.data);
})
.catch((error) => {
res.status(400).json({ message: `Failed to get users: ${error}` });
});
}
UserDisplay.tsx:
import React from "react";
import { useRecoilState } from "recoil";
import { userListPromise } from "../Atoms/atoms";
import { getUsers } from "../Modules/getUsers";
const UserDisplay = () => {
const [userList] = useRecoilState(userListPromise);
console.log(userList);
return (
<div>
</div>
);
};
export default UserDisplay;
If I comment out the lines using the state in UserDisplay.tsx:
const [userList] = useRecoilState(userListPromise);
console.log(userList);
then start the development server, uncomment them and save causing a live reload, then the error does not occur. However, if I then refresh the page or try to start the server initially with those lines uncommented, I get the error.
Any help or guidance would be greatly appreciated.
I'm using next v12.3.1 and recoil v0.7.6

Related

how to pass login token in headers in other pages in react redux

Here i have my upi action folder ,here how can i add my jwt token from login page api to this page. what is the procedure for displaying token genereated from login page to be used in other pages in react.
import axios from 'axios';
export const upiAction = {
upi,
};
function upi(user) {
return (dispatch) => {
var data = {
upiId: user.upiId,
accountNumber: user.accountNumber,
};
axios
.post('http://localhost:9091/upiidcreation', data)
.then((res) => {
console.log("res", (res));
alert(JSON.stringify(res.data.responseDesc));
// window.location.pathname = "./homes";
})
.catch(err => {
dispatch(setUserUpiError(err, true));
alert("Please Check With details");
});
};
}
export function setUserUpi(showError) {
return {
type: 'SET_UPI_SUCCESS',
showError: showError,
};
}
export function setUserUpiError(error, showError) {
return {
type: 'SET_UPI_ERROR',
error: error,
showError: showError,
};
}
You can import your redux store in any file and then use the getState() function to get the current redux state.
Example:
import store from './store'
console.log('Current state: ', store.getState())
// {tokens: [...], otherState: {...}}
https://redux.js.org/tutorials/fundamentals/part-4-store#redux-store

Problem with Invalid hook call for react-msal with Axios

I'm using react-msal to my application. I need to acquire the access token and attach it to the axios globally, but unfortunately, they only provide hooks to get the access token (as far as I know).
So far, here's my api.js file.
import axios from "axios";
import { useMsal } from "#azure/msal-react";
const axiosInstance = axios.create({
baseURL: "https://localhost:4211/api",
});
const { instance, accounts } = useMsal();
instance
.acquireTokenSilent({
...loginApiRequest,
account: accounts[0],
})
.then((response) => {
axiosInstance.defaults.headers.common[
"Authorization"
] = `Bearer ${response.accessToken}`;
})
.catch((error) => {
console("Error acquiring access token");
});
export default axiosInstance;
And here's I call my API in my component.
api.get('/foods').then(response => {
alert(response.data)
}).catch(error => {
console.log(error.response)
})
But I'm getting an issue that says: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. which is obvious but I need alternatives to get the access token and assign it to my axios globally as part of the header so I don't need to rewrite header each time I need to call an endpoints. Any help?
This is a React application, right?
You can't call hooks from outside of your React components, or other hooks.
https://reactjs.org/docs/hooks-rules.html
You could do something like this:
const App = () => {
const { instance, accounts } = useMsal();
useEffect(() => {
instance.acquireTokenSilent()
.then(() => {})
.catch(() => {})
},[]);
};
You can use PublicClientApplication instance passed into the MsalProvider.
To get the accounts call instance.getAllAccounts().
You can't access the inProgress value outside of a component or context, but since you're just using acquireTokenSilent you probably will not need it.
below is my working sample.
import axios from 'axios';
import * as App from '../index'
import * as utils from './utils'
const instance = axios.create({
baseURL: utils.getEndpoint(),
timeout: 15000
});
instance.interceptors.request.use(function (config) {
const instance = App.msalInstance;
const accounts = instance.getAllAccounts();
const accessTokenRequest = {
scopes: ["user.read"],
account: accounts[0],
};
return instance
.acquireTokenSilent(accessTokenRequest)
.then((accessTokenResponse) => {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
config.headers.Authorization = `Bearer ${accessToken}`;
return Promise.resolve(config)
})
}, function (error) {
return Promise.reject(error);
});
instance.interceptors.response.use((response) => {
if(response.status === 401) {
// Clear local storage, redirect back to login
window.location.href = "/logout"
}
return response;
}, (error) => {
return Promise.reject(error);
});
export default instance
and index.js below
import React from "react";
import ReactDOM from "react-dom";
import { PublicClientApplication, EventType } from "#azure/msal-browser";
import { msalConfig } from "./authConfig";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
export const msalInstance = new PublicClientApplication(msalConfig());
// Default to using the first account if no account is active on page load
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
// Account selection logic is app dependent. Adjust as needed for different use cases.
msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
}
// Optional - This will update account state if a user signs in from another tab or window
msalInstance.enableAccountStorageEvents();
msalInstance.addEventCallback((event) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
const account = event.payload.account;
msalInstance.setActiveAccount(account);
}
});
ReactDOM.render(<App pca={msalInstance} />,
document.getElementById("app"),
);
serviceWorker.unregister();

How to mock axios in React with using axios.create function

I'm working on React project where I'm using axios for http requests. I have a separate file with axios configuration like below:
import axios from 'axios'
export default axios.create({
baseURL: " http://localhost:3001",
params: {
}
})
I'm using this in action thunk creators like below:
import streams from "../apis/streams";
export const fetchStreams = () => {
return async(dispatch: ThunkDispatch<void, State, Action>) => {
const response: AxiosResponse<Stream[]> = await streams.get<Stream[]>('/streams');
dispatch({type: ActionType.FETCH_STREAMS, payload: response.data});
}
}
First I created "src/__mocks__/axios.ts" file like:
const mockedAxios: any = jest.createMockFromModule('axios');
mockedAxios.create = jest.fn(() => mockedAxios);
export default mockedAxios;
then I wrote test like below:
import mockedAxios, {AxiosResponse} from "axios";
import streamsApi from '../apis/streams'
import expectedStreams from "../mocks/expectedStreams";
jest.mock('axios')
describe('fetchStreams action', () => {
it('Store is updated correctly', async () => {
const mockedResponse: AxiosResponse = {
data: expectedStreams,
status: 200,
statusText: 'OK',
headers: {},
config: {}
}
mockedAxios.get.mockImplementationOnce(() => {
Promise.resolve(mockedResponse);
})
const results = await streamsApi.get('/streams');
expect(results.data).toBe(mockedResponse.data);
});
});
Unfortunately I've received an error like this:
Why is that? How can I correctly create facke API response in this case?
I would be grateful for help.
Ok, I know what was wrong. I forget to add return before Promise like so:
mockedAxios.get.mockImplementationOnce(() => {
return Promise.resolve(mockedResponse);
})

How to send request using customized Hooks in React Hooks

I was trying to create a custom Hooks for handling input HTTP request from any component by simply calling the useHttpPOSTHandler and want to use .then with Axios but its getting failed and error is
as i am new in react not able to debug this
what i have tried
import { useEffect, useState } from "react";
import axios from "axios";
const useHttpPOSTHandler = ({url , data}) => {
const [httpData, setHttpData] = useState();
const apiMethod = useCallback( ({url , data}) => {
axios
.post(url , data)
.then((response) => {
console.log(response)
console.log(response.data)
setHttpData(response.data);
})
.catch((error) => {
console.log(error);
});
}, [setHttpData])
return [ httpData , apiMethod];
};
export default useHttpPOSTHandler;
The arguments to useHTTPPostHandler are expected to be an object with keys url and data instead you are passing them individually causing a syntax error, wrap them within {}
const getData = useHttpPOSTHandler(
{ url: 'url', data: { "password": userPassword, "username": userName }
});
EDIT: As per your update, you won't see the updated data as soon as you make an API call. It will reflect in the next render
import useHttpPOSTHandler from "...."
const MyFunc = () => {
const [httpData, apiMethod] = useHttpPOSTHandlerdotthen()
const handleSubmit = () => {
apiMethod({url: 'url' , data: { "password": userPassword, "username": userName }})
}
if(httpData){
console.log("entered in api method")
console.log(httpData)
}
return (
<div>
...
</div>
)
}

"this.getClient(...).watchQuery is not a function" - remote schema stitching with Apollo 2 / Next.js

So I'm attempting to stitch multiple remote GraphCMS endpoints together on the clientside of a Next.js app, and after trying/combining about every example on the face of the internet, I've gotten it to a place that's worth asking about. My error:
TypeError: this.getClient(...).watchQuery is not a function at GraphQL.createQuery
github repo here, where you can see this initApollo.js in context:
import { ApolloClient } from 'apollo-client'
import {
makeRemoteExecutableSchema,
mergeSchemas,
introspectSchema
} from 'graphql-tools'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import fetch from 'node-fetch'
import { Observable, ApolloLink } from 'apollo-link'
import { graphql, print } from 'graphql'
import { createApolloFetch } from 'apollo-fetch'
let apolloClient = null
if (!process.browser) {
global.fetch = fetch
}
const PRIMARY_API = 'https://api.graphcms.com/simple/v1/cjfipt3m23x9i0190pgetwf8c'
const SECONDARY_API = 'https://api.graphcms.com/simple/v1/cjfipwwve7vl901427mf2vkog'
const ALL_ENDPOINTS = [PRIMARY_API, SECONDARY_API]
async function createClient (initialState) {
const AllLinks = ALL_ENDPOINTS.map(endpoint => {
return new HttpLink({
uri: endpoint,
fetch
})
})
const allSchemas = []
for (let link of AllLinks) {
try {
allSchemas.push(
makeRemoteExecutableSchema({
schema: await introspectSchema(link),
link
})
)
} catch (e) {
console.log(e)
}
}
const mergedSchema = mergeSchemas({
schemas: allSchemas
})
const mergedLink = operation => {
return new Observable(observer => {
const { query, variables, operationName } = operation
graphql(mergedSchema, print(query), {}, {}, variables, operationName)
.then(result => {
observer.next(result)
observer.complete()
})
.catch(e => observer.error(e))
})
}
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser,
link: mergedLink,
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo (initialState) {
if (!process.browser) {
return createClient(initialState)
}
if (!apolloClient) {
apolloClient = createClient(initialState)
}
console.log('\x1b[37m%s\x1b[0m', apolloClient)
return apolloClient
}
I'm getting useful data all the way up into the .then() inside the Observable, where I can log the result
This is a shot in the dark, but initApollo isn't async so it returns a promise (not an ApolloClient object) which is then being passed into client prop of the ApolloProvider. watchQuery doesn't exist as a function on the Promise type, hence the error.
I think if you make initApollo async and then await those calls or find a way to make client creation synchronous, you should be able to address this issue.

Resources