I am creating a react app with full crud functionality. It allows users to create job postings and i wanted to click on a specific job to view more details.
I am having trouble as everytime i try to click a "job" it says that ID is undefined specifically:
show function called with id: undefined
SyntaxError: Unexpected end of JSON input
My app currently displays the list of all jobs and creates.
I already confirmed the following:
Made sure the backend server is running and listening on port 3001.
Verified that the endpoint i am trying to fetch actually exists. Tried on postman
Made sure that my frontend code is using the correct URL to make requests to the backend.
I am using hooks and functions.
DetailsPage.js
import styles from './DetailsPage.module.css';
import React, { useState, useEffect } from 'react';
import jobsService from '../../utils/jobsService';
export default function DetailPage(props) {
const [job, setJob] = useState({});
const [isLoading, setIsLoading] = useState(true);
const { id } = props.match?.params || {};
useEffect(() => {
const fetchData = async () => {
try {
const { data } = await jobsService.show(id);
setJob(data);
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [id]);
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (
<div className={styles.list}>
<div className={styles.Grid}>
<h3>{job.title}</h3>
<p>{job.description}</p>
</div>
</div>
)}
</>
);
}
jobsService.js
async function getAll(){
const response = await fetch('http://localhost:3001/api/jobs')
const data = await response.json()
return data
}
async function create(item) {
try {
const response = await fetch('http://localhost:3001/api/jobs/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(item)
});
return await response.json();
} catch (error) {
console.error(error);
}
}
async function show(id) {
console.log("show function called with id:", id);
return fetch(`http://localhost:3001/api/jobs/${id}`, {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
})
.then(res => res.json());
}
export default {
getAll,
create,
show,
}
destruct id form empty object ?
instead this
const { id } = props.match?.params || {};
test this
const { id } = props.match?.params || {id: 0};
or use ternary operator in function calling
or
async function show(id = 0) {
console.log("show function called with id:", id);
return fetch(`http://localhost:3001/api/jobs/${id}`, {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
})
.then(res => res.json());
}
Related
I'm trying to export an axios call from an external file to my component, in useEffect. Im exporting the function and importing in the said component. The response is "undefined".
api_call.js:
import axios from 'axios';
const accessToken = window.localStorage.getItem('accessToken')
export const getPublicCircles = async () => {
const headers = {
'Content-Type': 'application/json',
'Accept-Language': 'fr',
'Authorization': `Bearer ${accessToken}`,
}
await axios.get('https://myurl.com/api/this-info', { headers })
.then(response => console.log(response))
.catch(error => console.log('error', error))
};
( I also tried with .then((response) => return response.data.data)
component.js
import * as API from '../../api/api_call';
export default function PublicCircles() {
const [circles, getCircles] = useState('');
useEffect(() => {
const fetchData = async () => {
const response = await API.getPublicCircles();
const json = await response.json();
console.log(response)
getCircles(response);
}
fetchData()
.catch(console.error);;
}, []);
return (
<Box>
{circles === '' ? null :
<PublicCircle circles={circles} />}
</Box>
)
}
Here are the results (getting the info from the api_call.js file, not the PublicCirlces.js one.
Thank you.
The real problem here is that the function getPublicCircles returns nothing, which is why any variable to which the result of this function call is assigned as a value, will be undefined per JavaScript rules, because a function that doesn't return any value will return undefined.
It's not a good idea to use async/await and then/catch in handling a promise together. Below is the example of handling it correctly with try/catch and async/await:
export const getPublicCircles = async () => {
const headers = {
'Content-Type': 'application/json',
'Accept-Language': 'fr',
'Authorization': `Bearer ${accessToken}`,
}
try {
const data = await axios.get('https://myurl.com/api/this-info', { headers });
return data;
} catch(error) {
console.error('error',error);
}
}
So I architected frontend in the way which encapsulates every API operation tied to a single resource inside custom hook like this:
export default function useSubjects() {
const queryClient: QueryClient = useQueryClient();
const token: string | null = useStore((state) => state.user.token);
const { yearCourseId } = useParams<{ yearCourseId: string }>();
const getSubjects = async () => {
const response = await axios.get(`yearCourses/${yearCourseId}/subjects`, {
headers: { Authorization: `Bearer ${token}` },
});
return response.data;
};
const postSubject = async (subject: SubjectType) => {
const response = await axios.post(`yearCourses/${yearCourseId}/subjects`, subject, {
headers: { Authorization: `Bearer ${token}` },
});
return response.data;
};
const query = useQuery(SUBJECTS_QUERY_KEY, getSubjects);
const postMutation = useMutation(postSubject, {
onSuccess: (subject: SubjectType) => {
queryClient.setQueryData(SUBJECTS_QUERY_KEY, (old: any) => [...old, subject]);
},
});
return { query, postMutation };
}
Now what is the way to globally handle 401 unauthorized? I would like to navigate user to /login on every unauthorized request. Note that I have more hooks like this tied to other resources.
use the onError callback. You can also do this globally as a callback on the queryCache
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: error => {
// check for 401 and redirect here
}
})
})
I am try to fetch users information using github API
import React, { useEffect } from "react";
function UserResults() {
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
const response = await fetch(`${process.env.REACT_APP_GITHUB_URL}/users`, {
headers: {
Authorization: `token ${process.env.REACT_APP_GITHUB_TOKEN}`,
},
});
const data = response.json();
};
return <div>Hello</div>;
}
export default UserResults;
And here is what I put in my env:
REACT_APP_GITHUB_TOKEN="<token>"
REACT_APP_GITHUB_URL = "https://api.github.com"
I am sure the token is correctly generated and copied.
But it seems I can't fetch the data due to some "JSON" error as it shows in the console like this.
Can anyone offers any help with this?
You need to await response.json() and update your header request
import React, { useEffect } from "react";
function UserResults() {
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
const response = await fetch(`${process.env.REACT_APP_GITHUB_URL}/users`, {
headers: {
'Authorization': `token ${process.env.REACT_APP_GITHUB_TOKEN}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
});
const data = await response.json();
};
return <div>Hello</div>;
}
export default UserResults;
I want to organize my react native app such that all state and business logic related to one module remains encapsulated at one place and components can focus mainly on UI. I really liked angular services in this regard as they keep the state and all helper, data fetching methods at one place and components can easily use and share them.
I have seen this
But this way of defining context and then defining custom hook to use that context in fetch API's etc seems unnecessarily complicated. I want a separate service/helper for my fetch api's that is aware of the state. Any suggestions please
For example almost every app has auth module. I have defined auth context:
import React, {
useEffect,
useState,
useContext,
useMemo,
createContext,
} from "react";
import { externalBaseURI2 } from "../config/constants";
import StorageService from "../services/storageService";
const AuthContext = createContext({
token: null,
});
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(null);
useEffect(() => {
getAuthToken()
.then(token => {
if(token) setToken(token)
})
}, []);
const getAuthToken = () => {
if (token) return Promise.resolve(token);
else return StorageService.getDataFromStorage("authToken");
};
const setAuthToken = (accessToken) => {
setToken(accessToken);
StorageService.setDatatoStorage("authToken", accessToken);
};
const revokeAuthToken = () => {
setToken(null);
StorageService.removeItemFromStorage("authToken");
};
**///////////////////// BELOW ARE MY STATE FUNCTIONS i.e: login, getProfile, logOut ////////////////**
const actions = {
login: (email, pass) => {
console.log(email, pass);
return fetch(`${externalBaseURI2}/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email: email, password: pass }),
})
.then((response) => {
if (!response.ok) {
return response.json().then(error => {
throw new Error(error.message || error.errorMsg)
})
// throw new Error("Something went wrong");
}
return response.json()
.then((jsonResponse) => {
setAuthToken(jsonResponse.token);
return jsonResponse;
})
})
// .catch((err) => console.log(err));
},
getProfile: () => {
return getAuthToken()
.then(token => {
if(token) {
return fetch(`${externalBaseURI2}/api/profile`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
})
}
else throw new Error('User not logged in')
})
.then((response) => {
if (!response.ok) {
return response.json().then(error => {
throw new Error(error.message || error.errorMsg)
})
}
return response.json();
})
.then((jsonResponse) => {
return jsonResponse;
})
},
logOut: () => {
revokeAuthToken()
}
}
return (
<AuthContext.Provider value={{ token, ...actions }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be inside a Provider with a value");
}
return context;
};
Here I have login and getprofile methods as state functions. Which I don't think should be responsible for fetching api data instead should only be responsible for setting state like logOut method. So where should I put the API fetch methods (in some helper/service file not in component) where I can access the state token too (to send in private api calls).
I'm trying to get user details after user looged in but user is getting 401 error even user is looged in with 200 ok.
Explanation of process:
i have logged in user using fetch post request.
stored username,role,staffid to async storage
now i want to list all user (with /api/staff endpoint response throws user firstname and last name )with fetch get request but whenever i make GET request it
throws 401 error.
It will be lifesaver to crack this step for me,thank you!
here is my code
import AsyncStorage from "#react-native-community/async-storage";
import React, { useState, useEffect } from "react";
import { SafeAreaView, Text, StyleSheet, Alert } from "react-native";
import AuthService from "../api/auth-service";
import BASE_URL from "../api/baseUrl";
export default function HomeScreen(props) {
const [firstName, setFirstName] = useState({});
const [lastName, setLastName] = useState({});
const [userValue, setUserValue] = useState({});
useEffect(() => {
let mounted = true;
if (mounted) {
getDataFromStorage();
getUserInfo();
}
return () => {
mounted = false;
};
}, []);
const getDataFromStorage = async () => {
let user = await AsyncStorage.getItem("LoggedInUser");
setUserValue(JSON.parse(user));
};
const getUserInfo=async()=>{
return fetch(BASE_URL+"/api/staff")
.then((response) => {
if(response.ok){
console.log(response);
}else{
console.log(response.status);
}
})
.catch((error) => {
console.log(error);
this.setState({ errorMsg: "Error retreiving data" });
});
}
return (
<SafeAreaView>
<Text>
{"Good morning " + userValue.username + " "}
{"you role is " + userValue.role +"your staff id is " + userValue.staffId+" " + "your first name is "+ firstName +"this is your last name"+lastName}
</Text>
</SafeAreaView>
);
}
authservice.js
import AsyncStorage from "#react-native-community/async-storage";
import BASE_URL from "./baseUrl";
class AuthService {
login(Username, Password, role) {
console.log(Username, role);
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
});
}
logout() {
AsyncStorage.getAllKeys().then((keys) => AsyncStorage.multiRemove(keys));
}
}
export default new AuthService();
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
(data) => {
console.log(JSON.stringify(data));
AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
According to developer.mozilla.org
The HTTP 401 Unauthorized client error status response code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
It's seems the user doesn't have right to access the API. Make sure that the getUserInfo() API, /api/staff, don't need any authentication token in header of your HTTP request.
I have a feeling that you may need to resolve one more promise in AuthService.login.
res.json() is actually a promise which needs to be resolved as well, so you may need one more then block like so:
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
}).then(finalData=>finalData )// <---------- add this
.catch(err=> err)
It happens because getDataFromStorage is an async function so you have to resolve it first. Also in login.js we have to await before navigate to make sure that the data is saved in AsyncStorage. Please update the following part of your code:
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
async (data) => {
console.log(JSON.stringify(data));
await AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
next, initialize userValue with null
const [userValue, setUserValue] = useState(null);
Now have 2 useEffect,
useEffect(() => {
let mounted = true; //Why this required as it doesn't mean anything
if (!userValue) {
getDataFromStorage();
}
return () => {
mounted = false;
};
}, []);
useEffect(()=>{
if(userValue){
getUserInfo()
}
},[userValue])