Trying to manage undefined value fetched from API - React - javascript

I'm fetching an id from one api and using that as a variable to fetch from another api. Unfortunately I can't be sure that I always get an ID from the first fetch so I need to gracefully assign a static ID or handle the error in some other way (preferably not doing a return for that function at all).
This is how my code looks right now, any ideas?:
import Layout from "../components/Layout.js";
import React, { Component } from "react";
import fetch from "isomorphic-unfetch";
import Error from "next/error";
import PageWrapper from "../components/PageWrapper.js";
import Menu from "../components/Menu.js";
import { Config } from "../config.js";
class Tjanster extends Component {
static async getInitialProps(context) {
const { slug, apiRoute } = context.query;
const tjansterRes = await fetch(
`${Config.apiUrl}/wp-json/postlight/v1/${apiRoute}?slug=${slug}`
);
const tjanster = await tjansterRes.json();
const personId = tjanster.acf.person[0].ID;
const personRes = await fetch(
`${Config.apiUrl}/wp-json/wp/v2/person/${personId}`
);
const person = await personRes.json();
return { tjanster, person };
}
render() {
if (!this.props.tjanster.title) return <Error statusCode={404} />;
return (
<Layout>
<Menu menu={this.props.headerMenu} />
<div className="single">
{this.props.person.title.rendered}
{this.props.tjanster.title.rendered}
</div>
</Layout>
);
}
}
export default PageWrapper(Tjanster);
I though about solving it somehow like this, but I only get personId is not defined:
static async getInitialProps(context) {
const { slug, apiRoute } = context.query;
const tjansterRes = await fetch(
`${Config.apiUrl}/wp-json/postlight/v1/${apiRoute}?slug=${slug}`
);
const tjanster = await tjansterRes.json();
if (tjanster.acf.person[0] === undefined) {
const personId = 64;
} else {
const personId = tjanster.acf.person[0].ID;
}
const personRes = await fetch(
`${Config.apiUrl}/wp-json/wp/v2/person/${personId}`
);
const person = await personRes.json();
return { tjanster, person };
}
Preferably I would like it to not return "person" if the value is undefined, but I'm not sure how to.

To be absolutely sure that you are receiving a value, and you are receiving a correct value, you will have to validate the property of the object by validating every property like this:
let tjanster = await tjansterRes.json();
let personId = 64; // or whatever constant you want
if(typeof tjanster !== 'undefined'
&& tjanster.hasOwnProperty('acf')
&& tjanster.acf.hasOwnProperty('person')
&& tjanster.acf.person.length > 0
&& tjanster.acf.person[0].hasOwnProperty('ID')
&& !isNaN(tjanster.acf.person[0].ID)){
personId = tjanster.acf.person[0].ID;
}
Then you could validate if the value is different than your constant:
if(personId === 64){
return; // prevent call to your api
}
let personRes = await fetch(
`${Config.apiUrl}/wp-json/wp/v2/person/${personId}`
);
let person = await personRes.json();
return { tjanster, person };
The problem on doing this:
if (tjanster.acf.person[0] === undefined) {
let personId = 64;
} else {
let personId = tjanster.acf.person[0].ID;
}
Is that if tjanster.acf is undefined, then you will get an exception that is not being handled because you will be calling the property person from an undefined object. The same happens if tjanster.acf.person is undefined, then tjanster.acf.person[0] throws an exception.

You can do this with a short circuit or making a function to handle the id
for example, with short circuit:
${Config.apiUrl}/wp-json/wp/v2/person/${personId || "STATIC_ID"}`
or create a function so you can call it inside the string interpolation
${Config.apiUrl}/wp-json/wp/v2/person/${handleId(personId)}`

Related

How to call API as promise in REACT

I am getting the following error when I run myu application and click on a submit button,
React has detected a change in the order of Hooks called by SideBarItemTemplateWithData. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
Previous render Next render
------------------------------------------------------
1. useContext useContext
2. useMemo useState
My SideBarItemTemplateWithData,
RoomListRowProps): ReactElement {
const { sidebar } = useLayout();
let priority:any;
let userRoomId:any;
let data:any;
const href = roomCoordinator.getRouteLink(room.t, room) || '';
const title = roomCoordinator.getRoomName(room.t, room) || '';
if (room && room.v && room.v._id) {
userRoomId = room.v._id;
}
if (room && room.priorityId) {
data = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
}
if (data && data.value && data.value.name) {
priority = data.value.name.toLowerCase();
}
const {
lastMessage,
hideUnreadStatus,
hideMentionStatus,
unread = 0,
alert,
userMentions,
groupMentions,
tunread = [],
tunreadUser = [],
rid,
t: type,
cl,
} = room;
<SideBarItemTemplate
userId={userRoomId}
priority={priority}
is='a'
/>
I am getting the error when I run the above code but when I comment the following line it is working fine,
if (room && room.priorityId) {
data = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
}
The below line returns a promise,
useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
Can anyone suggest me to how can I modify it o that the issue will be resolved. Thanks
useEndPoint code,
export const useEndpoint = <TMethod extends Method, TPath extends PathFor<TMethod>>(
method: TMethod,
path: TPath,
): EndpointFunction<TMethod, MatchPathPattern<TPath>> => {
const { callEndpoint } = useContext(ServerContext);
return useCallback((params: any) => callEndpoint(method,
path, params), [callEndpoint, path, method]);
};
How to use useEndPoint,
const sendEmailCode = useEndpoint('POST', '/v1/users.2fa.sendEmailCode');
await sendEmailCode({ emailOrUsername });
My Code,
const priorityData = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
if (room && room.v && room.v._id) {
userRoomId = room.v._id;
}
const onClickResendCode = async (): Promise<any> => {
try {
let priorityVal = '';
let data = await priorityData();
if (data && data.name) {
priorityVal = data.name.toLowerCase();
}
return priorityVal;
} catch (error) {
throw error;
}
};
priority = onClickResendCode();
Can anyone please suggest how can I modify the api call
The error is not about the promise, is about the order of the hook. In React you must keep the order of all your hook. You should not use your hook in a if or a loop.
This is the error.
if (room && room.priorityId) {
data = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
}
it should be
data = useEndpoint('GET', `/v1/livechat/priorities/${room.priorityId}`);
You can use if in the callEndpoint function, which is not a react hook.

Next.js Dynamic Path - Can't get context.params query

I am trying to set up a dynamic page in Next.js with getStaticPaths(). I have it 90% of the way there, but I can't get the getStaticProps() function to reference the URL query so that it loads the proper info.
My code is:
//Get Static Paths
export async function getStaticPaths() {
const paths = await (
await youtube.get("search?")
).data.items.map((video: any) => {
const id = video.id.videoId;
return { params: { id } };
});
return {
paths: paths.map((path: any) => path),
fallback: false,
};
}
//Get Static Props
export async function getStaticProps(context: any) {
const { query = "" } = context.params.query;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === query;
});
return {
props: {
video,
},
revalidate: 10,
};
}
I am receiving the error:
Error: Error serializing `.video` returned from `getStaticProps` in "/media/videos/[id]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
If I replace the 'query' variable in the getStaticProps() function with the actual ID of the video I am trying to load, everything goes through fine and the page loads. But then this is not dynamic, and is not using the url query to match with the proper video like I want.
Here is the working, non-dynamic code with a manually typed in ID to force a match.
//Get Static Props
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { query = "" } = context.params;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === "_TACZtT1irI";
});
return {
props: {
video,
},
revalidate: 10,
};
}
How can I make sure that the 'video' variable is accessing the url query? It should be grabbing it with context.params, but that is not working, what am I doing wrong?
I figured it out. This is working.
//Get Static Props
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { query = "" } = context.params;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === context.params.id;
});
return {
props: {
video,
},
revalidate: 10,
};
}

Cannot retrieve Title from Strapi

I have collection type called posts and it has the following values:
Now to get the api I have a file in my lib folder which contains the following code:
export async function getPosts() {
var api_base_url = process.env.API_BASE_URL;
const response = await fetch(api_base_url + "/posts");
const posts = await response.json();
return posts;
}
export async function getPost(postId) {
var api_base_url = process.env.API_BASE_URL;
const response = await fetch(api_base_url + "/posts/" + postId);
const post = await response.json();
return post;
}
export async function getPostFromTitle(postTitle, lang) {
var api_base_url = process.env.API_BASE_URL;
const response = await fetch(api_base_url + "/posts");
const posts = await response.json();
var postObject = {};
posts.forEach(post => {
if (post['Title (' + lang.toUpperCase() + ')'] == postTitle) {
postObject = post;
}
});
return postObject;
}
Now to display this I have used the following code:
import { getPosts, getPostFromTitle } from '../lib/apiGet'
export async function getStaticProps() {
const allPosts = JSON.parse(JSON.stringify(await getPosts()));
const postsTitle = JSON.parse(JSON.stringify(await getPostFromTitle(postTitle, lang)));
//Parse and Stringify done since nextJs was having weird errors accepting the standard json from API
return {
props: {
allPosts,
postsTitle
}
}
}
export default function Home({ allPosts, postsTitle }) {
return (
<div>
<body>
<ul>
{allPosts.map(post => (
<h1><u>
{console.log(post.id)}
{post.id}
</u>
</h1>
))}
</ul>
<ul>
{postsTitle.map((postTitle, lang) => (
<h1><u>
{console.log(postTitle.Title)}
{postTitle.Title}
</u>
</h1>
))}
</ul>
</body>
</div>
);
}
I am able to get the id correctly, but when I go to print the Title I get this error.
So how do I retrieve my title correctly?
I believe the issue might be in your frontend function;
export async function getPostFromTitle(postTitle, lang) {
var api_base_url = process.env.API_BASE_URL;
const response = await fetch(api_base_url + "/posts");
const posts = await response.json();
var postObject = {};
posts.forEach(post => {
if (post['Title (' + lang.toUpperCase() + ')'] == postTitle) {
postObject = post;
}
});
return postObject;
}
It might not yield any post - i would debug the function. Is the "posts" const containing valid objects? does the return value seem correct or is it undefined?
Generally speaking, it would be much simpler for you to re-write the filter part of your function (everything beyond await response.json();) to use the js filter function.
Something like;
return posts.filter(post => post['...'].includes(postTitle));

keeping a local variable in react

I want to add a pagination to my app for this reason i coded below code but there is a problem.
Here is my useEffect:
useEffect(() => {
let x = null;
const unsubscribe = chatsRef
.orderBy("createdAt", "desc")
.limit(10)
.onSnapshot((querySnapshot) => {
const messagesFirestore = querySnapshot
.docChanges()
.filter(({ type }) => type === "added")
.map(({ doc }) => {
const message = doc.data();
x = message;
return { ...message, createdAt: message.createdAt.toDate() };
});
appendMessages(messagesFirestore);
if (latestMessage != null) {
if (
new Date(
latestMessage["createdAt"]["seconds"] * 1000 +
latestMessage["createdAt"]["nanoseconds"] / 1000000
) >
new Date(
x["createdAt"]["seconds"] * 1000 +
x["createdAt"]["nanoseconds"] / 1000000
)
) {
latestMessage = x;
}
} else {
latestMessage = x;
}
});
return () => unsubscribe();
}, []);
I got the data from my database and i saved the oldest data in to latestMessage (for pagination) but the problem is that:
I declared my latestMessage out of my function like that:
let latestMessage = null;
export default function ChatTutor({ route }) {
...
}
And I passed my props to ChatTutor component (chatRoomId, username...) and according to that id, the room and its data are rendered. But the latestMessage always set some value and when i go to parent component and clicked another chatRoom, ChatTutor has a value of latestMessage's other value(oldest value). How can i set latestMessage null when i go to the parent ?
You can use useRef to store local mutable data (it would not participate in re-renders):
export default function ChatTutor({ route }) {
const latestMessage = useRef(null); // null is initial value
// ...
latestMessage.current = 'some new message' // set data
console.log(latestMessage.current) // read data
return <>ChatTutor Component</>
}

Export default not waiting my function return query. React Native

I want login system. My export default not working Read function. Read function query user_id in AsyncStorage. Please help me :)
app.js
var sendTabs = <TabsScreens />
var sendLogin = <LoginScreens />
var Read = readStore = async () => {
try {
const value = await AsyncStorage.getItem('user_id');
if (value !== null) {
return 1;
} else {
return 0;
}
} catch (e) {
alert(e);
}
}
var Welcome;
Read().then((response) => {
if (response == 1) {
Welcome = sendTabs
} else {
Welcome = sendLogin;
}
});
export default () => (Welcome)
You could define new component to handle this logic:
export function WelcomeScreen() {
const [isLoggedIn, setIsLoggedIn] = React.useState(null); // null | true | false
React.useEffect(() => {
void async function getUserId() {
const id = await AsyncStorage.getItem('user_id');
setIsLoggedIn(Boolean(id)); // true -> user_id found, false -> no valid user_id found
}();
}, []);
return (userId === null)
? <Text>Loading...</Text> // this will show until storage is checked
: (isLoggedIn) ? <TabsScreens /> : <LoginScreens />; // depending on the value of id from storage you show tabs or login screen
}

Categories