sessionStorage not available immediately after navigate - reactjs

I am trying to implement an React solution with Strapi as backend where authorization is done using JWT-keys. My login form is implemented using the function below:
const handleLogin = async (e) => {
let responsekey = null
e.preventDefault();
const data = {
identifier: LoginState.username,
password: LoginState.password
}
await http.post(`auth/local`, data).then((response) => {
setAuth({
userid: response.data.user.id,
loggedin: true
})
responsekey = response.data.jwt
setLoginState({...LoginState, success: true});
sessionStorage.setItem('product-authkey', responsekey);
navigate('/profile');
}).catch(function(error) {
let result = ErrorHandlerAPI(error);
setLoginState({...LoginState, errormessage: result, erroroccurred: true});
});
}
The API-handler should return an Axios item which can be used to query the API. That function is also shown below. If no API-key is present it should return an Axios object without one as for some functionality in the site no JWT-key is necessary.
const GetAPI = () => {
let result = null
console.log(sessionStorage.getItem("product-authkey"))
if (sessionStorage.getItem("product-authkey") === null) {
result = axios.create(
{
baseURL: localurl,
headers: {
'Content-type': 'application/json'
}
}
)
} else {
result = axios.create({
baseURL: localurl,
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${sessionStorage.getItem("product-authkey")}`
}
})
}
return result
}
export default GetAPI()
However, once the user is redirected to the profile page (on which an API-call is made which needs an JWT-key), the request fails as there is no key present in the sessionStorage. The console.log also shows 'null'. If I look at the DevTools I do see that the key is there... And if I refresh the profile page the request goes through with the key, so the key and backend are working as they should.
I tried making the GetAPI function to be synchronous and to move the navigate command out of the await part in the handleLogin function, but that didn't help.
Does someone have an idea?
Thanks!
Sincerely,
Jelle
UPDATE:
Seems to work now, but I need to introduce the getAPI in the useEffect hook, I am not sure if that is a good pattern. This is the code of the profile page:
useEffect(() => {
let testapi = GetAPI()
const getMatches = async () => {
const response = await testapi.get(`/profile/${auth.userid}`)
const rawdata = response.data.data
... etc
}, [setMatchState]

export default GetAPI() this is the problematic line. You are running the GetApi function when the module loads. Basically you only get the token when you visit the site and the js files are loaded. Then you keep working with null. When you reload the page it can load the token from the session storage.
The solution is to export the function and call it when you need to make an api call.

Related

context parameter is empty on getServerSideProps when page refresh

In one of my projects, I need to fill the meta keyword and the meta description by the data which is fetched from a REST API request.
so I used the getServerSideProps function, to fetch the response and pass it to the page.
Here's my getServerSideProps function
export async function getServerSideProps(context) {
function makeParam() {
let params = new URLSearchParams(context.params);
let keysForDel = [];
params.forEach((v, k) => {
if (v === 'undefined')
keysForDel.push(k)
});
keysForDel.forEach(k => {
params.delete(k)
});
return params.toString()
}
let response = await axios.post(
process.env.baseAddress + "getData.php",
qs.stringify({
data: "api/search/advance?" + makeParam()
}),
{
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
})
return {
props: {
dataFromServer: response.data,
params: makeParam()
},
}
}
everything works fine in development mode (localhost), but after deploying, by refreshing the page Context parameter is an empty object.
this function was written in one of the pages that has one parameter called the city, which is shown below
I have already checked getServerSideProps props empty object.
as Ankri said
Can you please check, that you pass the pageProps to your custom Component?
here is my Component tag, which contains pageProps.
<Layout>
<Component {...pageProps} ref={current}/>
</Layout>
First make the file structure like this:
pages:
city:
[...slug].js
Note: city is folder!
Now you can get the first param like this:
export async function getServerSideProps(context) {
const slug = context?.query?.slug[0] || "";
const req = await axios.post(`${url}?advance=${slug}`)
// ... rest of the code
}
now if url looks like this => http://localhost:300/city/japan
slug = japan

How to persist data using Apollo?

I have a Apollo client and server with a React app in which users can log in. This is the Apollo server mutation for the login:
loginUser: async (root, args) => {
const theUser = await prisma.user.findUnique({
where: {email: String(args.email)},
});
if (!theUser) throw new Error('Unable to Login');
const isMatch = bcrypt.compareSync(args.password, theUser.password);
if (!isMatch) throw new Error('Unable to Login');
return {token: jwt.sign(theUser, 'supersecret'), currentUser: theUser};
},
This returns a JWT and the user that's logging in.
In my React app I have a login component:
// Login.tsx
const [loginUserRes] = useMutation(resolvers.mutations.LoginUser);
const handleSubmit = async (e) => {
e.preventDefault();
const {data} = await loginUserRes({variables: {
email: formData.email,
password: formData.password,
}});
if (data) {
currentUserVar({
email: data.loginUser.currentUser.email,
id: data.loginUser.currentUser.id,
loggedIn: true,
});
window.localStorage.setItem('token', data.loginUser.token);
}
};
This function passes the form data to the LoginUser mutation which returns data if authentication is successful. Then I have a reactive variable called currentUserVar I store the email and id of the user in there so I can use it throughout the application. Finally I store the JWT in a LocalStorage so I can send it for authorization:
// index.tsx
const authLink = setContext((_, {headers}) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
Everything is working, except for the fact that if a user refreshes the user data is gone and they have to log in again, which is of course quite annoying.
So I was hoping to get some advice on how to persist the data, perhaps using Apollo? I suppose I could add a checkbox with a remember me function that stores the email and id in the LocalStorage and when the app initiates check if there's user data in the LocalStorage and than use that, but I was wondering if there's a better/other way to do this.
When it comes to the login problem , you have set the headers on your every single request , but did you pass a fuction to the ApolloServer constructor that checks the headers from every single request ? Something like this:
const server=new ApolloServer({
typeDefs,
resolvers,
context:async({req})=>{
const me=getMe(req)
return {
models,
me,
process.env.SECRET
}
}
})
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, process.env.SECRET);
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
As for the data persistence part of the question , you have to use setItem to persist the token in the locatStorage.

How to do POST request that requires dynamic variable in Next.js by Server Side Rendering?

I'm using Next.js framework and made a simple web application.
Using Firebase Auth, I tried to manage only authenticiated users to send requests. So basically, I need that user's id token. But as you know as user.getIdToken() doesn't work because user could be null. So I had to write user?.getIdToken().
As this result is Promise, I tried to use useContext hook. And I could get access to id token from all page files.
But here's the question.
Although I could get id token, as I need to get this inside the Component, it renders by Client Side Rendering. And I could see some delays.... and I want it to be rendered on Server Side. Could you guys help me with this? As I cannot use this id token inside getStaticProps() or other methods...
Here's my code:
export default function Boards() {
const BASE_URL = "http://localhost:5000/myproj/asia-northeast3/api";
const { idToken } = useContext(UserContext);
const [boardCategory, setBoardCategory] = useState("free");
const [posts, setPosts] = useState([]);
useEffect(() => {
try {
if (idToken != "") {
const res = axios.get(
BASE_URL + "/post/get", {
headers: { "Authorization": `Bearer ${idToken}`, "Access-Control-Allow-Origin": "*" },
params: { "boardCategory": `${boardCategory}` }
})
.then((response) => {
setPosts(JSON.parse(JSON.stringify(response.data)));
});
}
} catch (err) {
console.log(err);
}
}, [idToken, boardCategory]);
}
And this renders on Client Side Rendering so I see some delays...
Thank you guys in advance !

Go Api returning Unauthorized

i am new learner of GoLang+React. So that i started a project to learn. I made a RESTful Api with GoLang. Api Link. i made a login system with that api. And successfully i can login and set user data to sessionStorage. But the problem is when i am trying to logout a user by hit the logout endpoint of api with axios. First time it shows Unauthorized. and second time its showing Network Error.
Here is the Request code:
logout = () => {
const user = JSON.parse(sessionStorage.getItem('userData'));
const token = user.token;
const uid = user.id;
const url = "http://localhost:8000/logout"
axios.post(url,{"user_id":uid},{"Authorization":`Bearer ${token}`}).then((response) => response.json()).then((result) => {
let responseJson = result;
console.log(responseJson);
}).catch((error) => {
console.log(error);
})
}
Note: by client application i can logout successfully. But by axios i cant.
You need to provide auth headers under headers property of Axios config object:
axios.post(
url,
{ user_id: uid },
{ headers: { 'Authorization': `Bearer ${token}` } }
)
Explanation:
This is the axios post method signature, check docs:
axios.post(url[, data[, config]])
And the 3rd parameter config should have this interface:
{
...
baseURL?: string;
headers?: any; // this is for headers
params?: any;
....
}
I fixed that problem with adding this code to my package.json file
"proxy":"http://localhost:your_port"

how to post headers with axios in react

Hi i have this problem:
i made and API with an auth JWT runs perfect and my front is in react so i need only one component its a simple app, so in my App.jsx i have my axios post to get the token and then i pass my token through the component, then i receive the props in the component and i saw the token but when i pass to the header like i test in postman, so nothing happens, and if i try the front and in the API i disable the JWT in my route works perfectly so i let my code next
const Cards = (props) => {
//console.log(props.auth.token);
//axios connection
const apiCall = async () =>{
let config = {
headers: {
'Authorization': `Bearer ${props.auth.token}`
}
}
console.log(config);
try {
const res = await clientAxios.post('/api/games', config,
{
console: 'nintendo',
game: 'super mario',
duration: '60hs'
},)
console.log(props.auth.token);
} catch (error) {
console.log(error.status);
}
}
apiCall();
Instead of sending the token with each request, set the token in axios' default headers like so:
window.axios.defaults.headers.common['Authorization'] = `Bearer ${jwtToken}`;

Resources