In the page where i wanna use useInfiniteQuery
const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery(
['posts', searchState],
({ pageParam = 1 }) => post.search({ ...searchState, pageParam }),
{
getNextPageParam: (lastPage, pages) => {
if (pages.length < 2) return pages.length + 1;
else return undefined;
},
},
);
This is my custom axios func:
export const post = {
search: async (params: { keyword: string; sort: string; region: string; pageParam: number }) => {
const { keyword, sort, region, pageParam } = params;
const sortValue = SortType[sort as keyof typeof SortType];
const url = `/post/search?keyword=${keyword}&page=${pageParam}&filter=${sortValue}®ion=${region}`;
console.log(url);
const res = await client.get(url);
return res;
},
}
res in post.search function returns:
{"status":200,"message":"search success","data":{"posts":[],"pageCount":0}}
And I'd like to use pageCount as a condition for the getNextPageParam func, hasNextPage value.
What i really want to ask is, how can i use pageCount value in the following code:
getNextPageParam: (lastPage, pages) => {
For anyone who is not used to using react-query.
You can just simply look up the res.data using lastPage
const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery(
['posts', searchState],
async ({ pageParam = 1 }) => (await post.search({ searchState, pageParam })).data,
{
getNextPageParam: (lastPage, allPages) => {
if (lastPage === undefined) return undefined;
else return allPages.length < lastPage.pageCount && allPages.length + 1;
},
},
);
Related
The problem I'm having is, that I have a useContext in which I provide all logged users. On the initial run of the app or when the users' log in the array gets populated with all the users that are currently on the server... Which works as expected. But I have also the functionality, that whenever the server "user-connected" event runs, the front-end should just push the user to the end of this array. And there lays the problem. From the backend, the right user is sent, but when I access the connectedUsers array, the array is empty... but it should be already populated.
UsersProvider.tsx
export const inState = {
connectedUsers: [],
addUser: (user: any) => {},
sortUsers: (user: any, socketID: string) => {},
setLoggedUsers: () => {},
};
export interface initState {
connectedUsers: any[];
addUser(user: any): void;
sortUsers(users: any, socketID: string): void;
setLoggedUsers: React.Dispatch < React.SetStateAction < any[] >> ;
}
const UsersContext = createContext < initState > (inState);
export const useUsers = () => {
return useContext(UsersContext);
};
const initUserProps = (user: any) => {
user.messages = [];
user.hasNewMessages = false;
};
export const UsersProvider = ({
children
}: Props) => {
const [connectedUsers, setLoggedUsers] = useState < any[] > ([]);
const addUser = (user: any) => {
console.log('add', connectedUsers);
// This is empty, but it should be already populated when next user connected.
};
const sortUsers = (users: any, socketUserID: string) => {
const usersCopy = users;
usersCopy.forEach((u: any) => {
for (let i = 0; i < usersCopy.length; i++) {
const existingUser = usersCopy[i];
if (existingUser.userID === u.userID) {
existingUser.connected = u.connected;
break;
}
}
u.self = u.userID === socketUserID;
initUserProps(u);
});
// put the current user first, and sort by username
let sorted = usersCopy.sort((a: any, b: any) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
return a.username > b.username ? 1 : 0;
});
setLoggedUsers([...sorted]);
};
return ( <
UsersContext.Provider value = {
{
connectedUsers,
setLoggedUsers,
addUser,
sortUsers
}
} >
{
children
} <
/UsersContext.Provider>
);
};
And the part of ChatBoard.tsx, you can find addUser function initiated whenever user-connected happens. I really don't know why the would array be empty, if it is populated on the first run with users event.
const ChatBoard = (props: Props) => {
const socket = useSocket();
const {
connectedUsers,
setLoggedUsers,
addUser,
sortUsers
} = useUsers();
useEffect(() => {
if (socket == null) return;
socket.on('users', (users) => {
console.log(users);
if (socket.userID) {
const socketID: string = socket ? .userID;
sortUsers(users, socketID);
}
});
socket.on('user-connected', (user: any) => {
console.log(user, 'this user connected!');
const connectingUser = user;
addUser(connectingUser);
});
socket.on('user-disconnected', (userID) => {
console.log('disconnected user');
const users = [...connectedUsers];
users.forEach((u) => {
if (u.userID === userID) {
u.connected = false;
setLoggedUsers([...users]);
}
});
});
return () => {
socket.off('users');
socket.off('user-connected');
};
}, [socket]);
CodeSandbox
So I have found the problem... so with React hooks sometimes a problem occurs called "Stale Closures", which means that React was picking up the old state (empty one, the one that was not yet populated and always returning that one.).
The solution to this problem, in my case is that when you use setState you use it with a callback. Like so, so you always get the latest state.
const addUser = (user: any) => {
setLoggedUsers((oldUsers) => {
const newUsers: any[] = [...oldUsers];
console.log(newUsers);
for (let i = 0; i < newUsers.length; i++) {
const existingUser = newUsers[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
return newUsers;
}
}
initReactiveProperties(user);
newUsers.push(user);
return newUsers;
});
};
My code looks something like this:
this.cashRegisterService
.query({
'storeName.contains': event.query,
'identifier.contains': event.query,
})
.subscribe(data => {
this.cashregisters = (data.body || []).map(cashRegister => ({
id: cashRegister.id,
name: `${cashRegister.identifier} ${cashRegister.storeName} (${cashRegister.mallName})`,
}));
});
I want to check if the identifier OR the storeName contain the event.query but what this does is to check if both of them contains the event.query. How can i do that? the method is generated by jhipster
query(req?: any): Observable<EntityArrayResponseType> {
const options = createRequestOption(req);
return this.http.get<ICashRegister[]>(this.resourceUrl, { params: options, observe: 'response' });
}
export const createRequestOption = (req?: any): HttpParams => {
let options: HttpParams = new HttpParams();
if (req) {
Object.keys(req).forEach(key => {
if (key !== 'sort') {
options = options.set(key, req[key]);
}
});
if (req.sort) {
req.sort.forEach((val: string) => {
options = options.append('sort', val);
});
}
}
return options;
};
I'm working on a project with graphs and I need to be able to cancel my requests if the user selects a different tab.
Here's my API call
export const getDifferentialData = (
sourceId: string,
sourceLine: string,
source: any
) => {
const graph1Request = getData(
sourceId,
sourceLine,
source
)
const graph2Request = getData(
sourceId,
sourceLine,
source
)
return Promise.all([graph1Request, graph2Request]).then(results => {
const [graphA, graphB] = results
return {
graphA: parsedData(graphA),
graphB: parsedData(graphB),
}
})
}
export const getData = (
sourceId: string,
sourceLine: string,
source?: any
) => {
if (sourceId && sourceLine) {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
} else {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
}
}
And the component where I'm doing the call. userDidChangeTab is called when pressing on a tab and it calls fetchGraph
const Graph: FC<Props> = () => {
const source = axios.CancelToken.source();
// we ensure that the query filters are up to date with the tab selected
const userDidChangeTab = (tabIndex: number) => {
const isDifferentialTabSelected = isDifferentialTab(tabIndex)
let newFilters = queryFilters
if (isDifferentialTabSelected) {
newFilters = {
// props go here
}
} else {
newFilters = {
// props go here
}
}
source.cancel()
fetchGraph(isDifferentialTabSelected)
setActiveTab(tabIndex)
}
// Function to fetch two differential graphs.
const fetchGraph = (isDifferential: boolean) => {
setFetching(true)
if (isDifferential) {
getDifferentialData(
sourceId,
sourceLine,
source
)
.then(({ graphA, graphB }: any) => {
setGraphData(graphA)
setMatchData(new diffMatch(graphA, graphB, 1.0))
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
} else {
getGraph(
sourceId,
sourceLine,
source
)
.then((graphData: any) => {
setGraphData(graphData)
setMatchData(null)
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
}
}
}
I was using graphql mutations like this and the .then & .catch work perfectly:
let submitForm = (
email: string,
firstName: string
) => {
setIsSubmitted(true);
if (email && (firstName)) {
const input: UpdateUserInput = {};
if (firstName) {
input.firstName = firstName;
}
updateUser({
variables: {
email: email,
input: input,
},
})
.then(({ data }: ExecutionResult<UpdateUserResponse>) => {
if (data !== null && data !== undefined) {
setIsUpdated(true);
}
})
.catch((error: { message: string }) => {
console.log('Error msg:' + error.message);
});
}
};
Now I am doing something similar here for a graphql query (fuller working version below):
let ShowUsers = () => {
const where: WhereInput = {};
if (criteria === '2') {
if (searchItem) {
where.firstName_contains = searchItem;
loadUsers({
variables: {
where: where
},
})
.then(({ data }: any) => {
if (data !== null && data !== undefined) {
}
})
}
}
}
but I keep getting an error on then that Property 'then' does not exist on type 'void'
Edit:
Without the .then, .catch, my code works correctly. Full form is something like this:
function UserSearchPage() {
const [criteria, setCriteria] = useState('');
const [searchItem, setSearchItem] = useState('');
const [loadUsers, { loading, data }] = useLazyQuery(LoadUsersQuery);
function PrintUsers({ data }: any) {
return (
<div>
{data &&
data.users.nodes &&
data.users.nodes.map((c: any, i: any) => (
<li key={i}>
Id: {c.id}, First Name: {c.firstName}, Last Name: {c.lastName},
Email: {c.email}, phoneNumber: {c.phoneNumber}
</li>
))}
</div>
);
}
let ShowUsers = () => {
const where: WhereInput = {};
if (criteria === '1') {
loadUsers({
variables: {
where: where
},
});
}
if (criteria === '2') {
if (searchItem) {
where.firstName_contains = searchItem;
loadUsers({
variables: {
where: where
},
});
}
}
};
return (
.....);
}
This is how the GraphQL query itself looks like:
interface UserFilter {
email_contains: String;
firstName_contains?: String;
lastName_contains?: String;
phoneNumber_contains?: String;
id?: Number;
}
export const LoadUsersQuery = gql`
query usersList($where: UserFilter) {
users(where: $where) {
nodes {
id
email
}
totalCount
}
}
`;
How else can I access the data properties/errors?
From the console.log, I know that this is returned:
Object
__typename: "User"
email: "first#first.com"
firstName: "First"
id: 148
lastName: "User"
phoneNumber: "+49123"
But if I try to access lets say data.users.id, why do I get undefined? How can I fix this?
As stated in other answers, it's known problem - "useLazyQuery execution function should return a promise #3499"
Instead of
loadUsers({
variables: {
where: where
},
})
.then(({ data }: any) => {
if (data !== null && data !== undefined) {
}
})
you can use onCompleted option
const [loadUsers, { loading, data }] = useLazyQuery(LoadUsersQuery, {
onCompleted: ( data : any ) => {
if (data !== null && data !== undefined) {
// some action
}
}
});
It depends on what exactly is happening in loadUsers, but it's likely that you've forgotten to a return statement there.
If you change loadUsers to return the promise for the user's it's loading, your code should start working nicely.
If you look at the docs for useLazyQuery, it does not return a Promise like useMutation so they behave differently.
Instead of relying on a Promise, you must utilize the second parameters (loading, data) returned when invoking useLazyQuery. This is why in your edit, your code works without the .then.
Problem with got data correctly execute function many one times, these function is execute in ngOnInit one time with abstraction but i dont know ocurrs these problem in a server, i thing in snapshotChanges but i don't know.
thx for help
https://i.stack.imgur.com/EinQg.png
return <Observable<Products[]>> t.db.collection(PATHS_FIRESTORE.products).snapshotChanges()
.pipe(
map(actions => {
let arr = actions.map((res) => {
let doc: any = <any>res.payload.doc.data()
let obj: any = {}
if (!isNullOrUndefined(cart)) {
for (const prod in cart) {
if (cart.hasOwnProperty(prod)) {
const element = cart[prod];
if (doc.uid === prod) {
obj[doc.uid] = {
name_product: doc.name_product,
path_img: doc.path_img,
price: doc.price,
quantity: doc.quantity + element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
} else {
t.db.collection(PATHS_FIRESTORE.products).doc(prod).ref.get().then( res => {
const data = res.data()
return obj[res.id] = {
name_product: data.name_product,
path_img: data.path_img,
price: data.price,
quantity: element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
})
}
}
console.log(obj)
}
return obj
}else {
obj = {
...doc
}
return obj
}
})
.filter((b: any) => {
return b.uid_local === uid_local
})
.filter((b: any) => {
return b.quantity > 0
})
.filter((b: any) => {
return !b.status
})
console.log(arr)
return arr
})
)