Cannot read property of undefined, Typescript - javascript

I'm having issues with creating a class function with optional parameters. I'm plagued with the following error and since I'm a new comer to typescript I'm not really sure as to what I have to change in my code to fix it. This code works perfectly fine in an earlier project written in JS only.
It specifically highlights the getRequestOptions, when I go check it in base-service.ts in the browser error.
08:32:12.755 client.ts:22 [vite] connecting...
08:32:13.067 client.ts:52 [vite] connected.
08:32:17.043 locales-service.ts:7 getting locales
08:32:17.048 base-service.ts:19 get /Api/GetLocales/ undefined undefined undefined
08:32:17.288 base-service.ts:21 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'getRequestOptions')
at request (base-service.ts:21)
at getLocales (locales-service.ts:9)
at setup (locale-selector.vue:22)
at callWithErrorHandling (runtime-core.esm-bundler.js:6737)
at setupStatefulComponent (runtime-core.esm-bundler.js:6346)
at setupComponent (runtime-core.esm-bundler.js:6302)
at mountComponent (runtime-core.esm-bundler.js:4224)
at processComponent (runtime-core.esm-bundler.js:4199)
at patch (runtime-core.esm-bundler.js:3791)
at mountChildren (runtime-core.esm-bundler.js:3987)
And my code
base-service.ts
import axios from '#/plugins/axios'
export default class BaseService {
getRequestOptions(url:any, method:any, data?:any, params?:any , customHeaders?:any) {
const headers = {
...customHeaders
}
return {
url:url,
method:method,
data: data,
params: params,
headers: headers,
}
}
async request(method:any, url:any, data?:any, params?:any, headers?:any) {
console.log(method,url,data,params,headers)
const options = this.getRequestOptions(method, url, data, params, headers)
try {
console.log(options)
const response = await axios(options)
console.log("Getting response from api")
console.log(response)
if (response.status === 200) {
return response.data
}
} catch (error) {
console.log(error)
}
}
}
locales-service.ts
import BaseService from '../base-service'
//?Import models in the future
//import { } from '#/common/models'
class LocaleServ extends BaseService {
async getLocales() {
console.log("getting locales")
let result = await super.request(
'get',
'/Api/GetLocales/'
)
console.log(result)
return result
}
}
export const LocaleService = new LocaleServ()
Any help would be greatly appreciated
Edit:
<script lang="ts">
import { ref } from 'vue'
import { defineComponent } from 'vue'
import CountryFlag from 'vue-country-flag-next'
import { LocaleService } from '#/services'
export default defineComponent({
name: 'LocaleSelector',
components: {
CountryFlag
},
setup () {
const { getLocales } = LocaleService
const data = getLocales()
return {
data:data
}
}
})
</script>
And then in the component I call it with {{data}}

The issue lied in the way I called the destructured getLocales() method from LocaleService in locale-service.vue
I noticed that this was undefined after attaching a debugger. According to this article, destructuring it caused it to lose it's context. Therefore when I called getLocales() in base-service.ts, this was undefined.
Simple work around to fix the, caused due to inexperience, problem was to turn.
const { getLocales } = LocaleService
const data = getLocales()
into
const data = LocaleService.getLocales()

Related

Type error with react-query and UseQueryResult

I am using react-query in my TS project:
useOrderItemsForCardsInList.ts:
import { getToken } from '../../tokens/getToken';
import { basePath } from '../../config/basePath';
import { getTokenAuthHeaders } from '../../functions/sharedHeaders';
import { useQuery } from 'react-query';
async function getOrderItemsForCardsInList(listID: string) {
const token = await getToken();
const response = await fetch(`${basePath}/lists/${listID}/order_items/`, {
method: 'GET',
headers: getTokenAuthHeaders(token)
});
return response.json();
}
export default function useOrderItemsForCardsInList(listID: string) {
if (listID != null) {
return useQuery(['list', listID], () => {
return getOrderItemsForCardsInList(listID);
});
}
}
I use my query result over here:
import { useCardsForList } from '../../hooks/Cards/useCardsForList';
import useOrderItemsForCardsInList from '../../hooks/Lists/useOrderItemsForCardsInList';
import usePaginateCardsInList from '../../hooks/Cards/usePaginateCardsInList';
export default function CardsListFetch({ listID }: { listID: string }) {
const { isLoading, isError, error, data } = useCardsForList(listID);
const { orderItems } = useOrderItemsForCardsInList(listID);
const pagesArray = usePaginateCardsInList(orderItems, data);
return (
...
);
}
However, on my const { orderItems } = useOrderItemsForCardsInList(listID); line, I get the following error:
Property 'orderItems' does not exist on type 'UseQueryResult<any, unknown> | undefined'.
How can I resolve this? I don't really know how to consume the result of my query on Typescript, any help is be appreciated
The property on useQuery that you need to consume where you find your data is called data, so it should be:
const { data } = useOrderItemsForCardsInList(listID);
if that data has a property called orderItems, you can access it from there.
However, two things I'm seeing in your code:
a conditional hook call of useQuery (which is forbidden in React)
your queryFn returns any because fetch is untyped, so even though you are using TypeScript, you won't get any typesafety that way.
If you are using React with react-query, I suggest you install this devDependency called: "#types/react-query". When using VS Code or any other smart text editor that will help, because it will help you with type suggestions.
npm i --save-dev #types/react-query
Then go to your code and fix couple things:
remove condition from useOrderItemsForCardsInList(). Don’t call React Hooks inside loops, conditions, or nested functions. See React hooks rules.
import UseCategoryResult and define interface for your return object. You can call it OrderItemsResult or similar. Add type OrderType with the fields of the order object or just use orderItems: any for now.
Add return type UseQueryResult<OrderItemsResult> to useOrderItemsForCardsInList() function.
Fix return value of getOrderItemsForCardsInList(), it should not be response.json() because that would be Promise, not actual data. Instead use await response.json().
So your function useOrderItemsForCardsInList() will return UseQueryResult which has properties like isLoading, error and data. In your second code snipper, you already use data in one place, so instead rename data to orderData and make sure you define default orderItems to empty array to avoid issues: data: orderData = {orderItems: []}
useOrderItemsForCardsInList.ts:
import { getToken } from '../../tokens/getToken';
import { basePath } from '../../config/basePath';
import { getTokenAuthHeaders } from '../../functions/sharedHeaders';
import { useQuery, UseCategoryResult } from 'react-query';
type OrderType = {
id: string;
name: string;
// whatever fields you have.
}
interface OrderItemsResult {
orderItems: OrderType[],
}
async function getOrderItemsForCardsInList(listID: string) {
const token = await getToken();
const response = await fetch(`${basePath}/lists/${listID}/order_items/`, {
method: 'GET',
headers: getTokenAuthHeaders(token)
});
const data = await response.json();
return data;
}
export default function useOrderItemsForCardsInList(listID: string): UseQueryResult<OrderItemsResult> {
return useQuery(['list', listID], () => {
return getOrderItemsForCardsInList(listID);
});
}
Use your query result:
import { useCardsForList } from '../../hooks/Cards/useCardsForList';
import useOrderItemsForCardsInList from '../../hooks/Lists/useOrderItemsForCardsInList';
import usePaginateCardsInList from '../../hooks/Cards/usePaginateCardsInList';
export default function CardsListFetch({ listID }: { listID: string }) {
const { isLoading, isError, error, data } = useCardsForList(listID);
const { data: orderData = { orderItems: []}} = useOrderItemsForCardsInList(listID);
const pagesArray = usePaginateCardsInList(orderItems, data);
return (
...
);
}

Error: Error serializing `.data[4].description` returned from `getStaticProps` in "/" [duplicate]

I'm working with Next.js, I tried accessing data but got this error:
Error: Error serializing `.profileData` returned from `getStaticProps` in "/profile/[slug]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
My code:
import { getAllBusinessProfiles } from '../../lib/api';
const Profile = ({ allProfiles: { edges } }) => {
return (
<>
<Head>
<title>Profile</title>
</Head>
<Hero />
<section>
{edges.map(({ node }) => (
<div key={node.id}>
<Link href={`/profile/${node.slug}`}>
<a> {node.businessInfo.name} </a>
</Link>
</div>
))}
</section>
</>
);
}
export default Profile;
export async function getStaticProps() {
const allProfiles = await getAllBusinessProfiles();
return {
props: {
allProfiles
}
};
}
getAllBusinessProfiles from api.js:
const API_URL = process.env.WP_API_URL;
async function fetchAPI(query, { variables } = {}) {
const headers = { 'Content-Type': 'application/json' };
const res = await fetch(API_URL, {
method: 'POST',
headers,
body: JSON.stringify({ query, variables })
});
const json = await res.json();
if (json.errors) {
console.log(json.errors);
console.log('error details', query, variables);
throw new Error('Failed to fetch API');
}
return json.data;
}
export async function getAllBusinessProfiles() {
const data = await fetchAPI(
`
query AllProfiles {
businessProfiles(where: {orderby: {field: DATE, order: ASC}}) {
edges {
node {
date
title
slug
link
uri
businessInfo {
name
title
company
image {
mediaItemUrl
altText
}
highlight
phone
city
country
facebook
instagram
email
website
profiles {
profile
profileInfo
}
extendedProfile {
title
info
}
}
}
}
}
}
`
);
return data?.businessProfiles;
};
What could be the error here? I used the getStaticProps method on Next.js but got the error above instead. Please, check. Thanks.
The error:
Server Error
Error: Error serializing .profileData returned from getStaticProps in "/profile/[slug]".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I don't know what could cause this though.
Add JSON.stringify when calling an asynchronous function that returns an object.
Try modifying your getStaticProps function like this.
export async function getStaticProps() {
const profiles = await getAllBusinessProfiles();
const allProfiles = JSON.stringify(profiles)
return {
props: {
allProfiles
}
};
}
The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
Source: MDN
I had this issue using Mongoose and Next.js.
To solve it: I switched from convert require to import then wrapped my result in JSON.parse(JSON.stringify(result));.
Good: import mongoose from 'mongoose';
Bad: const mongoose = require('mongoose');
I had the same serialization error when accessing a Vercel system environment variable in getStaticProps.
Using JSON.stringify did not do the trick, but String() worked. My code:
export async function getStaticProps() {
const deploymentURL = String(process.env.NEXT_PUBLIC_VERCEL_URL);
return {
props: {
deploymentURL,
},
};
}
Thanks to this GitHub issue for the inspiration
I had the same issue when I was working with redux with next js and the reason was one of the fields in the default state I set it to undefined. Instead I used null:
const INITIAL_STATE = {
products: [],
loading: false,
error: undefined,
cart: [],
};
error:undefined was causing the error. Because "undefined" cannot be serialized:
export async function getStaticProps() {
const allProfiles = await getAllBusinessProfiles();
return {
props: {
allProfiles
}
};
}
you are returning "allProfiles" which is the result of async getAllBusinessProfiles() which is either returning undefined, error or one of the fields of the returned object is undefined. "error" object is not serializable in javascript
Instead of using undefined, you have to use null as the value for your variables.
Note that the error shows you exactly which variable is using undefined as its value. Just modify its value to be null.
The value 'undefined' denotes that a variable has been declared, but hasn't been assigned any value. So, the value of the variable is 'undefined'. On the other hand, 'null' refers to a non-existent object, which basically means 'empty' or 'nothing'.
Source: [1]
I was having the same issue while trying to find a match in the array of data using the id. The issue I had was the items in the array had ids which were numbers while the value I was getting from params was a string. So all i did was convert the number id to a string to match the comparison.
export async function getStaticProps({ params }) {
const coffeeStore = coffeeStoreData.find(
(store) => store.id.toString() === params.slug[0]
);
return {
props: {
coffeeStore,
},
};
}
install a package called babel-plugin-superjson-next and superjson and added a .babelrc file with these contents:
{
"presets": ["next/babel"],
"plugins": ["superjson-next"]
}
see this topic : https://github.com/vercel/next.js/discussions/11498.
I had a similar problem too where I was fetching data through apollo directly inside of getStaticProps. All I had to do to fix the error was add the spread syntax to the return.
return {
props: {
data: { ...data }
}
}
return { props: { allProfiles: allProfiles || null } }
In getStaticProps() function, after fetching your data it will be in json format initially, but you should change it as follow:
const res = await fetch(`${APP_URL}/api/projects`);
const data = JSON.parse(res);
now it will work.
When you call api you should use try catch. It will resolve error.
Example:
import axios from "axios";
export const getStaticProps = async () => {
try {
const response = await axios.get("http:...");
const data = response.data;
return {
props: {
posts: data
}
}
} catch (error) {
console.log(error);
}
}
Hope help for you !
put res from API in Curly Brackets
const { res } = await axios.post("http://localhost:3000/api", {data})
return { props: { res } }
try this, it worked for me:
export async function getStaticProps() {
const APP_URL = process.env.PUBLIC_NEXT_WEB_APP_URL;
const res = await fetch(`${APP_URL}/api/projects`);
const projects = await res.json();
return {
props: {
projects: projects?.data,
},
};
}

'Argument expression expected' error during ajax call in rxjs

I am getting this error after using api call. I am trying to fetch data from myjson api. Am i doing something wrong with the current implementation or Is there any better way to call the same api.
import { createAction, handleActions } from 'redux-actions'
import { PLUGIN_NAME, STORE_MOUNT_POINT } from '../constants'
import { Epic,ofType } from 'redux-observable'
import { Store } from '../Store'
import { defer, of } from 'rxjs'
import { mergeMap } from 'rxjs/operators'
import { ajax } from '../util'
const fetchReportTemplate$: Epic<Store.IFSA> = (action$, state$) =>
action$.ofType(startFetchReportsTemplate.toString(),
return ajax(
{
url: 'https://api.myjson.com/bins/15dcd9',
body: ''
}
)
).map(({ response: { data } }) => {
return of(clearAllHttpErrors(), receiveReportsTemplateRows(data))
})
Yes, it's syntax error in your code.
action$.ofType(startFetchReportsTemplate.toString(),
// here, floating return without function
return ajax(

Error using redux saga, take(patternOrChannel): argument 8 is not valid channel or a valid pattern

I'm trying to use redux saga library to capture some actions but I'm having this error when I run the app:
index.js:2178 uncaught at rootSaga at rootSaga at projectSaga at
watchFetchRequest at takeEvery Error: take(patternOrChannel):
argument 8 is not valid channel or a valid pattern
at take (http://localhost:3000/static/js/bundle.js:84689:9)
at takeEvery (http://localhost:3000/static/js/bundle.js:85993:94)
at createTaskIterator (http://localhost:3000/static/js/bundle.js:85179:17)
at runForkEffect (http://localhost:3000/static/js/bundle.js:85583:24)
at runEffect (http://localhost:3000/static/js/bundle.js:85468:872)
at next (http://localhost:3000/static/js/bundle.js:85348:9)
at proc (http://localhost:3000/static/js/bundle.js:85303:3)
at runForkEffect (http://localhost:3000/static/js/bundle.js:85587:19)
at runEffect (http://localhost:3000/static/js/bundle.js:85468:872)
at http://localhost:3000/static/js/bundle.js:85677:14
at Array.forEach ()
at runAllEffect (http://localhost:3000/static/js/bundle.js:85676:10)
at runEffect (http://localhost:3000/static/js/bundle.js:85468:413)
at next (http://localhost:3000/static/js/bundle.js:85348:9)
at proc (http://localhost:3000/static/js/bundle.js:85303:3)
at runForkEffect (http://localhost:3000/static/js/bundle.js:85587:19)
at runEffect (http://localhost:3000/static/js/bundle.js:85468:872)
at http://localhost:3000/static/js/bundle.js:85677:14
at Array.forEach ()
at runAllEffect (http://localhost:3000/static/js/bundle.js:85676:10)
at runEffect (http://localhost:3000/static/js/bundle.js:85468:413)
at next (http://localhost:3000/static/js/bundle.js:85348:9)
at proc (http://localhost:3000/static/js/bundle.js:85303:3)
at runSaga (http://localhost:3000/static/js/bundle.js:85858:76)
at Object../src/store/ReduxRoot.tsx (http://localhost:3000/static/js/bundle.js:95823:16)
at webpack_require (http://localhost:3000/static/js/bundle.js:679:30)
at fn (http://localhost:3000/static/js/bundle.js:89:20)
at Object../src/index.tsx (http://localhost:3000/static/js/bundle.js:95325:75)
at webpack_require (http://localhost:3000/static/js/bundle.js:679:30)
at fn (http://localhost:3000/static/js/bundle.js:89:20)
at Object.0 (http://localhost:3000/static/js/bundle.js:96424:18)
at webpack_require (http://localhost:3000/static/js/bundle.js:679:30)
at ./node_modules/#babel/runtime/helpers/arrayWithoutHoles.js.module.exports
(http://localhost:3000/static/js/bundle.js:725:37)
at http://localhost:3000/static/js/bundle.js:728:10
This is the saga code:
import { all, call, fork, put, takeEvery } from 'redux-saga/effects';
import { ActionType, Action, SearchCriteria } from '../model/types';
import { searchProjectsError, searchProjectsCompleted } from '../actions/projects';
import { API_URL } from '../../config';
// import axios from 'axios';
function callApi(method: string, url: string, path: string, data?: any) {
return fetch(url + path, {
method,
headers: {'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*'},
body: JSON.stringify(data)
}).then(res => res.json());
}
// Here we use `redux-saga` to trigger actions asynchronously. `redux-saga` uses something called a
// "generator function", which you can read about here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
function* handleFetch(action: Action<SearchCriteria>) {
try {
// To call async functions, use redux-saga's `call()`.
const res = yield call(callApi, 'get', API_URL , '/api/DocumentLoader/GetProjects/', action.payload);
if (res.error) {
yield put(searchProjectsError(res.error));
} else {
yield put(searchProjectsCompleted(res));
}
} catch (err) {
if (err instanceof Error) {
yield put(searchProjectsError(err.stack!));
} else {
yield put(searchProjectsError('An unknown error occured.'));
}
}
}
// This is our watcher function. We use `take*()` functions to watch Redux for a specific action
// type, and run our saga, for example the `handleFetch()` saga above.
function* watchFetchRequest() {
yield takeEvery(ActionType.SEARCH_PROJECTS, handleFetch); // I think the error is here
}
// We can also use `fork()` here to split our saga into multiple watchers.
function* projectSaga() {
yield all([fork(watchFetchRequest)]);
}
export default projectSaga;
I already tried to find an answer here in SO, but the only I could find was this post, but ActionType is been exported. I think the problem is with the parameter of handleFetch function
This is the action:
export function searchProjects(criterias: SearchCriteria): Action<SearchCriteria> {
return {
type: ActionType.SEARCH_PROJECTS,
payload: criterias
};
}
Another thing it could be is maybe I did something wrong at the time to compose the saga middleware:
const sagaMiddleware = createSagaMiddleware();
var middleware = applyMiddleware(logger, thunk, sagaMiddleware);
if (process.env.NODE_ENV === 'development') {
middleware = composeWithDevTools(middleware);
}
// Here we use `redux-saga` to trigger actions asynchronously. `redux-saga` uses something called a
// "generator function", which you can read about here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
export function* rootSaga() {
yield all([fork(projectSaga)]);
}
// running the root saga, and return the store object.
sagaMiddleware.run(rootSaga);
I also tried removing the action parameter from handleFetch and the error is still happening
I found what was the error, the problem was the definition of ActionType enum. It was without assigning an string value for each action.
export const enum ActionType {
// Projects
SEARCH_PROJECT,
SEARCH_PROJECTS_COMPLETED,
SEARCH_PROJECTS_ERROR,
}
This fixed the problem:
export const enum ActionType {
// Projects
SEARCH_PROJECTS= '##projects/SEARCH_PROJECTS',
SEARCH_PROJECTS_COMPLETED= '##projects/SEARCH_PROJECTS_COMPLETED',
SEARCH_PROJECTS_ERROR= '##projects/SEARCH_PROJECTS_ERROR',
}
The only possible error could be this:
Check whether you have imported the { ActionType } object here, from the models file where you have defined your constants.
export function searchProjects(criterias: SearchCriteria):
Action<SearchCriteria> {
return {
type: ActionType.SEARCH_PROJECTS,
payload: criterias
};
}

javascript - call exported function from another class

Here's the scenario: There is a file called api.js which has method api() to make api calls. There is another class called AutoLogout which has functionality to show autologout modal and logging out user after certain time in case of no activity. These works fine.
index.js in ../services
export { default as api } from './api';
// export { api, onResponse } from './api'; tried this as well
export { default as userService } from './userService';
api.js
import userService from './userService';
export function onResponse(response) {
// returns response to calling function
return response;
}
async function api(options) {
const settings = Object.assign(
{
headers: {
'content-type': 'application/json',
'x-correlation-id': Math.random()
.toString(36)
.substr(2),
},
mode: 'cors',
credentials: 'include',
body: options.json != null ? JSON.stringify(options.json) : undefined,
},
options,
);
const response = await window.fetch(`/api/v0${options.endpoint}`, settings);
// calling onResponse() to send the response
onResponse(response);
if (response.status === 403) return userService.logout();
if (response.status > 299) throw new Error();
if (response.status === 204) return true;
return response.json ? response.json() : false;
}
export default api;
Now, in response header I've "x-expires-at" and I want to use it in autologout. So, that if api call is made the user token resets.
auto-lougout.js
import { userService, api } from '../services';
// import { userService, api, onResponse } from '../services'; tried this as well
export default class AutoLogout {
constructor() {
super();
if (!userService.getUser()) userService.logout();
// here I am not able to call onResponse() from api.js
// getting response received from onResponse()
api.onResponse((resp) => { console.log(resp.headers.get('x-expires-at'))});
}
}
Trying to implement as an example given in this article:
https://zpao.com/posts/calling-an-array-of-functions-in-javascript/
Here I cannot use export { api, onResponse }; as api is already being used at multiple places in whole project.
How do I call onResponse function in one js file from another class in another js file ? Am I using callback correctly here ? If not, how to use callback correctly in such scenario ?
Here I cannot use export { api, onResponse }; as api is already being used at multiple places in whole project.
The correct import/export syntax for your project would be
// api.js
export function onResponse() { … }
export default function api() { … }
// index.js
export { default as userService } from './userService';
export { default as api, onResponse } from './api';
// elsewhere
import { userService, api, onResponse } from '../services';
// use these three
Am I using callback correctly here?
No, not at all. onResponse should not be a function declared in your api.js file, and it should not be exported from there - sparing you all the above hassle.
If not, how to use callback correctly in such scenario?
Make the callback a parameter of the function that uses it:
export default async function api(options, onResponse) {
// ^^^^^^^^^^
const settings = Object.assign(…);
const response = await window.fetch(`/api/v0${options.endpoint}`, settings);
onResponse(response);
…
}
Then at the call of the api function, pass your callback as an argument:
api(options, resp => {
console.log(resp.headers.get('x-expires-at'));
});

Categories

Resources