Authenticated user shows up in console.log but can't access the object - reactjs

I'm using Supabase for authentication and I get a user object when logging in.
This is how it looks like:
{
"id": "cb43b195-22cc-48c8-946a-d323f70165bd",
"aud": "authenticated",
"role": "authenticated",
"email": "joe#*******.com",
"email_confirmed_at": "2022-01-26T18:34:31.105402Z",
"phone": "",
"confirmed_at": "2022-01-26T18:34:31.105402Z",
"last_sign_in_at": "2022-02-01T18:00:27.998776Z",
"app_metadata": {
"provider": "github",
"providers": [
"github"
]
},
"user_metadata": {
"avatar_url": "https://avatars.githubusercontent.com/u/93337091?v=4",
"email": "joe#*******.com",
"email_verified": true,
"full_name": "Joe",
"iss": "https://api.github.com",
"name": "Joe",
"preferred_username": "joe",
"provider_id": "93337091",
"sub": "93337091",
"user_name": "joe"
},
"identities": [
{
"id": "93337091",
"user_id": "cb43b195-22cc-48c8-946a-d323f70165bd",
"identity_data": {
"avatar_url": "https://avatars.githubusercontent.com/u/93337091?v=4",
"email": "joe#*******.com",
"email_verified": true,
"full_name": "Joe",
"iss": "https://api.github.com",
"name": "Joe",
"preferred_username": "joe",
"provider_id": "93337091",
"sub": "93337091",
"user_name": "joe"
},
"provider": "github",
"last_sign_in_at": "2022-01-26T18:34:31.102361Z",
"created_at": "2022-01-26T18:34:31.102403Z",
"updated_at": "2022-01-26T18:34:31.102403Z"
}
],
"created_at": "2022-01-26T18:34:31.098348Z",
"updated_at": "2022-01-26T18:37:12.766+00:00",
"username": "joe",
"avatar_url": "0.181358731179603.png",
"website": null }
I'm trying to access any property but for instance, if I try to render {user.username} I get a "Cannot read username property of null" error.
Any idea why that happens?
This is the context that gives the user info - I'm using it to provide the auth data:
import { createContext, useState, useEffect, useContext } from "react";
import { supabase } from "../utils/supabase";
import { useRouter } from "next/router";
const Context = createContext();
const Provider = ({ children }) => {
const router = useRouter();
const [user, setUser] = useState(supabase.auth.user());
useEffect(() => {
const getUserProfile = async () => {
const sessionUser = supabase.auth.user();
if (sessionUser) {
const { data: profile } = await supabase
.from("profiles")
.select("*")
.eq("id", sessionUser.id)
.single();
setUser({
...sessionUser,
...profile,
});
}
};
getUserProfile();
supabase.auth.onAuthStateChange(() => {
getUserProfile();
});
}, []);
const login = async () => {
await supabase.auth.signIn({
provider: "github",
});
};
const logout = async () => {
await supabase.auth.signOut();
setUser(null);
router.push("/");
};
const exposed = {
user,
login,
logout,
};
return <Context.Provider value={exposed}>{children}</Context.Provider>;
};
export const useUser = () => useContext(Context);
export default Provider;
Thanks!

Could be because you are trying to access the user information before the API returns the data. In the component you are rendering the user data, check if the user exists first before returning your component.
if(!user) {
return <p>Loading...</p>
}

I had a similar issue and changing {user.username} to {user?.username} fixed the error for me. I am not exactly sure why, but hopefully this might help somebody.

Related

React rest call map result to selectbox with avatar and label per option

Hi here is rest response:
[
{
"self": "https://your-domain.atlassian.net/rest/api/3/project/EX",
"id": "10000",
"key": "EX",
"name": "Example",
"avatarUrls": {
"48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10000",
"24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10000",
"16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10000",
"32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10000"
},
"projectCategory": {
"self": "https://your-domain.atlassian.net/rest/api/3/projectCategory/10000",
"id": "10000",
"name": "FIRST",
"description": "First Project Category"
},
"simplified": false,
"style": "classic",
"insight": {
"totalIssueCount": 100,
"lastIssueUpdateTime": "2022-12-08T07:09:19.702+0000"
}
},
{
"self": "https://your-domain.atlassian.net/rest/api/3/project/ABC",
"id": "10001",
"key": "ABC",
"name": "Alphabetical",
"avatarUrls": {
"48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10001",
"24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10001",
"16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10001",
"32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10001"
},
"projectCategory": {
"self": "https://your-domain.atlassian.net/rest/api/3/projectCategory/10000",
"id": "10000",
"name": "FIRST",
"description": "First Project Category"
},
"simplified": false,
"style": "classic",
"insight": {
"totalIssueCount": 100,
"lastIssueUpdateTime": "2022-12-08T07:09:19.702+0000"
}
}
]
I want to make select bobx having
<option value={data.id}><Img {data.16x16}/>data.label</option>
But result would be all projects if company has multiple projects so select box values have to map or loop into react
<Select options="result">
Im stuck as my code displays only label not any image there.
Another problem is that using data.avatarUrls.16x16 does not compile. VSCode says expecting "," and puts red underline to 16x16
Here is my code a lot is broken here because I have tested a lot ways but no luck
import React, { useState } from 'react';
import Select from 'react-select'
import { components } from 'react-select';
//Kun selectbox
const handleChange = event => {
//console.log(event.target.value);
setSelected(event.target.value);
};
//Palauttaa projectit json taulukon
const getProjects = async () => {
//Matti tähän sitten atlasion cmpany projection haku
const response = await api.asUser().requestJira(route`/rest/api/3/project`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
//Mapataa hausta tarvittavat tiedot
const result = data.map(function (item, i) {
console.log('test');
return [
{
label: item.name,
value: item.id,
avatar: item.avatarUrls.16x16
}
]
})
return result
}
function Projects() {
//haetaan atlasiansita projectit array
const p = getProjects
//asetetaan state selectbox muutokselle
const [selected, setSelected] = useState(p.id);
return (
<div className='projects'>
<Select
className='select-projects'
options={p}
onChange={handleChange}
/>
</div>
);
}
export default Projects

next-auth custom auth window not defined

I am trying to use next-auth with my backend but it doesn't work. I use version 4 with typescript. The error is
{error: 'window is not defined', status: 200, ok: true, url: null}
Why?????. Thanks a lot.
My custom API /login result is
{
"data": {
"username": "test",
"users": {
"id": 2,
"username": "test",
"email": "test#test.com",
"createdAt": "2021-05-24",
"updatedAt": "2021-05-24",
"name": "John Smith",
"id_groups": 99,
"groups": "guest",
"avatar": null
},
"timestamp": 1646808511,
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiG9.eyJpc3MiOiJodHRwOlwvXC90d2luYXBwLml0IiwiYXVkIjoiaHR0cDpcL1wvdHdpbmFwcC5pdCIsImlhdCI6MTM1Njk5OTUyNCwibmJmIjoxMzU3MDAwMDAwLCJleHAiOjE2NDY4MTIxMTEsImRhdGEiOiJtYXJjb2JvbmNpIn0.R1aAX99GHmoSPRKv4Vnzso8iRjUhrDWhPEdq4oql_r0"
},
"status": "",
"code": 200
}
Now, I'm try to configure next auth
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import gApi from "../../../api/gApi";
export default NextAuth({
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {label: "Username",type: "text", placeholder: "username"},
password: { label: "Passwort", type: "password" },
},
async authorize(credentials) {
const resp = await gApi.post("/login", JSON.stringify(credentials));
const user = resp.data;
console.log('CALL MY API');
console.log(resp);
if ( resp.status && user) {
return user;
}
return null;
},
}),
],
callbacks: {
async jwt({ token, user, account, isNewUser }) {
if (user) {
if (user.jwt) {
token = { accessToken: user.jwt };
}
}
return token;
},
async session({ session, token }) { // this token return above jwt()
session.accessToken = token.accessToken;
return session;
},
},
pages: {
signIn: "/auth/Login",
},
});
In my login page I have e simple form and i call with:
const onSubmit: SubmitHandler<FormData> = async data => {
const resp: any = await signIn("credentials", {
username: data.username,
password: data.password,
redirect: false,
});
console.log('RESPO signin');
console.log(resp);
if (resp && !resp.error) {
router.replace('/')
} else return;
}

React + fetch: adding extra characters and backslashes to my url

I have this code in React 17
useEffect(() => {
getLocalJson('../json/login/login.json', props.headers)
.then(resp => {
setFields(resp);
});
}, [props.headers]);
And the getLocalJson method is in a different file:
export const getLocalJson = async (url, headers) => {
console.log(url)
const resp = await fetch(url, {'headers': headers});
const json = await resp.json();
return json;
}
However the call to load the local JSON file from the public folder is:
Request URL: http://localhost:3000/json/login/%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5C%5Cdx%5Cjson%5Clogin%5Clogin.json
Ths is the JSON
[
{
"order": 0,
"systemName": "title",
"friendlyName": "Login",
"dataType": {
"type": "TITLE"
}
},
{
"order": 1,
"required": true,
"systemName": "username",
"friendlyName": "Username",
"errorMsg": "Invalid username",
"dataType": {
"type": "TEXT"
}
},
{
"order": 2,
"required": true,
"systemName": "password",
"friendlyName": "Password",
"errorMsg": "Invalid password",
"dataType": {
"type": "PASSWORD"
}
},
{
"order": 3,
"systemName": "title",
"friendlyName": "Login",
"dataType": {
"type": "BUTTON",
"submit": true
}
}
]
And it makes the call over and over and over
This exact code works on my ubuntu dev box, but is failing as abovw on my windows box
I think there is some issue with the way you are passing down the headers, look into the documentation to have a better idea.
Put your function in the body of your component where you're using useEffect and wrap it with useCallback like this:
const getLocalJson = useCallback( async (url, headers) => {
console.log(url)
const resp = await fetch(url, {'headers': headers});
const json = await resp.json();
return json;
},[])

Display data from database via ID from nested array

I am trying to get data (balance and employee name) to show to the user. I have a nested array, with multiple objects that hold basic employee information. The id I need to access is in the nested objects - see below data structure example from Mongo DB.
The problem I am facing, the only ID I can access is the first id, I have done some research and mongo only displays from the highest ID. How can I access the nested IDs to display the data I need? Is it even possible? I have tried to map in the Axios request as shown in the code, nothing works.
{
"message": "Post found",
"data": {
"company": "HIJ",
"_id": "60fb75123ca85f76447d2b58",
"details": [
{
"employee": "aa",
"date": "aa",
"tax": "aa",
"balance": "3",
"fee": 11,
"notes": "aa",
"_id": "60fb75123ca85f76447d2b59"
},
{
"employee": "bb",
"date": "bb",
"tax": "bb",
"balance": "3",
"fee": 12,
"notes": "bb",
"_id": "60fb75123ca85f76447d2b5a"
}
],
"__v": 0
}
}
here is the code:
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
const Success = () => {
const [company, setCompany] = useState("")
const [balance, setBalance] = useState("");
const [employee, setEmployee] = useState("");
const { id } = useParams();
useEffect(() => {
axios
.get(`http://localhost:5000/get-snapshot-id-test/${id}`)
.then((res) => {
console.log("res", res.data.data);
setBalance(res.data.data.map((b) => b.details.map((innerary) => innerary.balance)));
setEmployee(res.data.data.map((e) => e.details.map((innerary) => innerary.employee)));
})
.catch((err) => {
console.log(err);
});
}, [setBalance, setEmployee, id]);
return (
{ balance === "0.00" && <h4>{employee} is paid in full.</h4> }
{ balance !== "0.00" && <h4>{employee} new balance: ${balance}.</h4>}
);
};
export default Success;
the answer was to fix the map in the Axios request. Drew Reese lead me in the right direction pointing out the res.data.data.details. Then I needed to take that result and point to the desired state
useEffect(() => {
axios
.get(`http://localhost:5000/get-snapshot-id-test/${id}`)
.then((res) => {
console.log("RESPONCE", res.data.data.details);
setBalance(res.data.data.details.map((r) => r.balance));
setEmployee(res.data.data.details.map((r) => r.employee));
})
.catch((err) => {
console.log(err);
});
}, [setBalance, setEmployee, setNotes, id]);

Connect uploaded file with related model with Strapi

I am using Strapi, Sqlite3 and React.
I want to send a form with a file attached.
I have a Job model, which looks like this:
{
"connection": "default",
"collectionName": "jobs",
"info": {
"name": "job",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"comment": ""
},
"attributes": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"resume": {
"model": "file",
"via": "related",
"plugin": "upload"
},
"jobcategory": {
"model": "jobcategory",
"via": "jobs"
}
}
}
I am sending text input with submitCareer method, and uploadFile for uploading:
export async function submitCareer(url, formValues) {
try {
const entries = await rootUrl.createEntry(url, formValues);
return entries;
} catch (err) {
console.log(err);
}
}
export async function uploadFile(formValues) {
try {
const upload = await rootUrl.upload(formValues);
return upload;
} catch (err) {
console.log(err);
}
}
This is the usage in my Career component:
const handleSubmit = (event) => {
const formData = new FormData();
formData.append("files", fileInput.current.files[0]);
submitCareer('jobs', values);
uploadFile(formData);
setValues({
firstName: '',
lastName: '',
email: '',
resume: null
})
event.preventDefault();
}
I get this response:
{
"id": 66,
"firstName": "John",
"lastName": "Doe",
"email": "john#gmail.com",
"jobcategory": null,
"lname": null,
"created_at": 1561988031279,
"updated_at": 1561988031279,
"resume": {}
}
So, how can i connect resume with Job model?
Linking model to a file
You must create process with to two steps:
Create File -> POST /upload.
Create Jobs with id from FileResponse -> POST /jobs.
Example:
const handleSubmit = (event) => {
const formData = new FormData();
formData.append("files", fileInput.current.files[0]);
resumeUploadFile = await uploadFile(formData);
const jobsInput = {...jobs, ...{resume: resumeUploadFile.id}}
await submitCareer('jobs', jobsInput);
setValues({
firstName: '',
lastName: '',
email: '',
resume: null
})
event.preventDefault();
}
https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#file-upload
Linking files to an entry
You can linking too file to an entry wich is created, then you create first Jobs and then link upload ResumeFile with added new fields like refId from Jobs (Jobs -> id), ref in your case jobs and field=resume
https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#examples
When you want to upload a file and link it to an entry.
You have to first create the entry if it's not already done.
And then upload your file by sending the image information of the entry.
All the documentation is here https://strapi.io/documentation/3.0.0-beta.x/guides/upload.html#examples

Resources