react next Js create JSON url - reactjs

i want to use grafana platform that display data
i need to create route patch that contain the data as json and use is on grafana
on react i can do it with app.get request and send it res.json()
but with next js it wont work as it should be
adding code
*** i am in the middle of project so all this request/database configuration work
so there is no problem with kind of configuration
i just want when i go to the correct url i could get the users json and use this url something like in the picture that i upload
import user from "../../models/userModel";
import mongoose from "mongoose";
const handler = async(req, res) => {
await mongoose.connect(
database url that i deleted
);
const fetchAllUser = async () => {
const users = await user.find();
res.json(users);
};
if( req.method === "GET"){
fetchAllUser();
}
};
export default handler;
//send request to get all the users from the db and i get it!
const allUsers = async()=>{
const users = await axios.get(`${herokuUrl}user4`)
return users.data
}
// users components that run on the routes of "users"
import React, { useEffect } from "react";
import { useDataProvider } from "../context/Data";
const Users = () => {
const { allUsers } = useDataProvider();
useEffect(() => {
allUsers();
}, []);
return <></>;
};
export default Users;

Related

Avoiding multiple API calls due to rerenders in React with Firebase auth

My web app uses Firebase Auth to handle user authentication along with a backend API, these are provided to the React app as a provider. The idea is that the backend API will verify the user's token when they sign in and deal with any custom claims / data that needs to be sent to the client.
The problem I'm having is that the provider is rerendering multiple times during the login flow, and each rerender is making an API call. I've managed to get the amount of rerenders down to two, but if I add other 'features' to the provider (e.g update the user's state if their access should change) then this adds to the amount of rerenders, sometimes exponentially, which leads me to suspect that the provider is rerendering as a result of setUserState being called, perhaps unnecessarily. Either way, it is clearly indicative of a problem somewhere in my code, which I've included below:
import {useState, useContext, createContext, useEffect} from 'react'
import {auth, provider} from './firebase'
import {getAuth, onAuthStateChanged, signInWithPopup, signOut} from 'firebase/auth'
import {api} from './axios'
export const UserContext = createContext(null)
export const useAuth = () => useContext(UserContext)
const verifyToken = token => {
return api({
method: 'post',
url: '/verifyToken',
headers: {token}
})
}
const UserProvider = props => {
const [userState, setUserState] = useState(null)
const [loading, setLoading] = useState(true)
const userSignedOut = async () => {
setLoading(true)
return await signOut(auth).then(() => {
setUserState(null)
}).catch(e => {
console.error(e)
}).finally(() => {
setLoading(false)
})
}
const userSignIn = async () => {
console.log('userSignIn')
setLoading(true)
try {
return await signInWithPopup(auth, provider)
} catch (e) {
console.error(e)
} finally {
if (!userState) {
setLoading(false)
}
}
}
const handleUserSignIn = async user => {
console.log('handleUserSignIn', user)
if (user && getAuth().currentUser) {
setLoading(true)
const idToken = await getAuth().currentUser.getIdToken(true)
const firebaseJWT = await getAuth().currentUser.getIdTokenResult()
if (!firebaseJWT) {throw(new Error('no jwt'))}
verifyToken(idToken).then(res => {
if (res.data.role !== firebaseJWT.claims.role) {
throw(new Error('role level claims mismatch'))
} else {
user.verifiedToken = res.data
console.log(`user ${user.uid} valid and token verified`, user)
setUserState(user)
setLoading(false)
}
}).catch(e => {
userSignedOut()
console.error('handleUserSignIn', e)
}).finally(() => {
setLoading(false)
})
} else {
console.log('no user')
userSignedOut()
}
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async user => {
console.log('onAuthStateChanged')
if (user?.uid && user.accessToken) {
await handleUserSignIn(user)
} else {
setUserState(null)
setLoading(false)
}
})
return () => unsubscribe()
}, [])
const value = {
signOut: userSignedOut, // for sign out button
signIn: userSignIn, // for sign in button
user: userState
}
return (
<UserContext.Provider value={value}>
{props.children}
</UserContext.Provider>
)
}
export default UserProvider
I tried to create a codesandbox for this, but unfortunately I was unable to simulate the Firebase auth functions.
The login flow is supposed to look like this:
The user signs in using their Google account.
The app is now loading, and the user cannot interact with it yet (they just get a spinning wheel).
The user's data and accessToken are sent to the backend API server. (function verifyToken)
The API server sets any custom claims and returns the verified token in its response, as well as the access that the user is supposed to have.
If the user's role / custom claims do not match what the API says they should be, the user is signed out.
The user's data is set using setUserState()
The app has finished loading, and the user is signed in.
I would like to avoid unnecessary rerenders and API calls and I suspect that some refactoring may be in order, but I'm not really sure what is best to do here.

How Can I exist Queries When the User refresh the browser in react-query?

When refresh the Browser, inactive state queries have gone.
How Can I maintain queries When I refresh the Browser?
and also, I want to maintain userData when the pages go out
code like this.. (with zustand, react-query)
const {userId} = useParams();
const userData = useStore((state) => state.userData);
const {isLoading, data} = useQuery('user', () => getUser(userId), {
onSuccess: (res) => {
useStore.setState({userData: res.data});
},
onError: (err) => errorMsg(err),
});
getUser func
export const getUser = (userId)=>{
if(!userId) return;
return axios.get(`${API.user(userId)}`};
}
React Query provides experimental (as of June 2022) support for storing data in Local Storage:
import { persistQueryClient } from 'react-query/persistQueryClient-experimental'
import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental'
// probably also supports session storage as the API is the same
const localStoragePersistor = createWebStoragePersistor({storage: window.localStorage})
// the queryClient you pass to QueryClientProvider
persistQueryClient({
queryClient,
persistor: localStoragePersistor,
})
Source: https://react-query.tanstack.com/plugins/persistQueryClient

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();

React PWA - How do you detect if the requested data to be served from cache or from server

I am building a PWA app, where I download data by a user action call, "Go to offline". So once the button is clicked the data is fetched and saved to the browser indexdb storage. I am using workbox and CRA2. I folled this blog post to customize workbox config with cra app.
Now say there is no PWA, and when page requests data, react call actions for example:
export async function fetchProjectStatuses(
dispatch,
params = {},
raiseError = false
) {
try {
dispatch(requestProjectStatuses());
const data = await fetchProjectStatusesApi();
dispatch(receiveProjectStatuses(data));
return data;
} catch (err) {
if (raiseError) {
throw err;
} else {
dispatch(requestProjectStatusesFailed(err));
}
}
}
and fetchProjectStatusesApi is defined as:
import axios from "axios";
const fetchAllUrl = () => `/project_statuses.json`;
export async function fetchProjectStatusesApi(config, params = {}) {
const url = fetchAllUrl();
const { data } = await axios.get(url, {
headers: { Accept: "application/json" }
});
return data;
}
This works. Now when offline, I am trying to write something like:
import { registerRoute } from "workbox-routing";
registerRoute(
new RegExp("/project_statuses\\.json"),
async ({ url, event, params }) => {
try {
const response = await fetch(event.request);
const responseBody = await response.text();
return new Response(responseBody);
} catch (err) {
// get from DB
}
}
);
So how do I write handler so that it forwards the data to the fetchProjectStatusesApi if network is present , else get data from DB. I know how to pull the date from local IDB. Only thing I am not able to figure out:
How do I detect the app is offline so the data has to come from local db
If app is online how do I forward the response from fetch to axios which is called from the api function.
I am writing it first time so I have no idea yet. Any help will be appreciated.
Thank you.

Firestore: enablePersistence() and then using redux with offline database?

So, essentially, I'm using Create-React-App and I want to allow users to add data to redux either offline or online. I also want to sync redux with Firestore.
In my main attempt, I initialize my firebase settings:
// ./firebase/firebase.js
var firestoreDatabase;
firebase.initializeApp(config.firebase.config);
firebase.firestore().enablePersistence().then(() => {
firestoreDatabase = firebase.firestore();
});
export { firebase, firestoreDatabase };
Then, to make sure this has fired properly (this is definitely wrong, but I can't figure out the best place to catch the enablePersistence() return... ):
// src/index.js
import { firebase, firestoreDatabase } from "./firebase/firebase";
firebase.auth().onAuthStateChanged(user => {
store.dispatch(setReduxData()).then(() => {
if (firestoreDatabase) {
ReactDOM.render(application, document.getElementById("root"));
}
});
});
ACTIONS FILE
import { firestoreDatabase } from "../firebase/firebase";
export const setReduxData = () => {
return (dispatch, getState) => {
const uid = getState().auth.uid;
const data = { newData: '123' };
return firestoreDatabase
.collection("Users")
.doc(uid)
.collection("data")
.add(data)
.then(ref => {
// so, this never gets fired
dispatch(
addData({
id: ref.id,
...data
})
);
})
So the dispatch never gets fired, however, when I refresh the application, the data I entered { newData: '123' } is added to the store.
I think my entire way of handling this is wrong. I don't like exporting firestoreDatabase as undefined and then updating it when enablePersistence() returns...
I would like to just enablePersistence() once and then use the cache or the server depending on if the user is online or not... Redux should operate the same regardless...
Any thoughts and feedback are welcome!
So, I figured out how to load Firestore properly in my application:
In my firebase.js file:
import * as firebase from "firebase";
import config from "../config";
// https://firebase.google.com/docs/reference/js/
firebase.initializeApp(config.firebase.config);
const database = firebase.database();
const auth = firebase.auth();
const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
export { firebase, googleAuthProvider, auth, database };
Then, I added a firestore.js file:
import { firebase } from "./firebase";
import "firebase/firestore";
import { notification } from "antd";
firebase.firestore().settings({ timestampsInSnapshots: true });
const handleError = error => {
if (error === "failed-precondition") {
notification.open({
message: "Error",
description:
"Multiple tabs open, offline data only works in one tab at a a time."
});
} else if (error === "unimplemented") {
notification.open({
message: "Error",
description: "Cannot save offline on this browser."
});
}
};
export default firebase
.firestore()
.enablePersistence()
.then(() => firebase.firestore())
.catch(err => {
handleError(err.code);
return firebase.firestore();
});
And then I call firestore in my actions file:
import firestore from "../firebase/firestore";
return firestore
.then(db => {
var newData = db
.collection("Users")
.doc(uid)
.collection("userData")
.doc();
newData.set(data);
var id = newData.id;
dispatch(addData({ id, ...data }));
})
.catch(err => {
// notification
});
Essentially, I separated out my redux and Firestore, but ultimately they are connected through the Firestore id.

Resources