How to use Apollo's multiple useQueries() hooks in React - reactjs

I need to use 2 queries in my file and I am writing them like so:
const {loading, data } = useQuery(getCharactersQuery);
const {loading, data} = useQuery(getSingleCharacterQuery);
The problem is, they both have the same "loading" and "data" variables and I don't see anywhere in the docs how can we have different ones. How can I differentiate them?

It's Object destructuring of JS Destructuring assignment. You can choose not to use it here to give different variable names.
const resCharacters = useQuery(getCharactersQuery);
const resSingleCharacter = useQuery(getSingleCharacterQuery);
if (resCharacters.loading || resSingleCharacter.loading) return 'Loading...';
...
Ref: Apollo document of useQuery

This way, by giving them an alias.
const {loading, data } = useQuery(getCharactersQuery);
const {loading: singleCharacterLoading, data: singleCharacterData} = useQuery(getSingleCharacterQuery);

const GET_DATA = gql`
query {
characters {
phone
rating
ratingType
review
city
id
}
singleCharacter {
title
text
}
}
`;
const {loading, data } = useQuery(GET_DATA);
console.log(data) //{characters: [...], singleCharacter: [...]}

Related

RTK Query createSelector that uses query cache

I'm trying to selected some data that is deeply nested within the result of the RTK Query api cache.
The issue I'm having is that I'm unsure how to define and use the custom createSelector properly so I can pass in the multiple parameters needed to get the data. I seem to run into the same error of expected 2 arguments but got 3.
From what I understand from the docs, and YouTube/articles, is that you pass in each param which will correspond with an input parameter in the selector. The final thing in the selector is the output where you grab the final data you want.
So I need to pass in 2 params. First one for the query cache signature so I can get the initial data. The second param is to filter the data in the output selector.
slice.ts
export const selectOnboardingCategoryFormInfoResult = (state: RootState, { category_id, project_id }: { category_id: number; project_id?: number }) => (!!project_id ? wordprestoApi.endpoints.getOnboardingFormInfo.select({ category_id, project_id })(state).data : undefined);
export const selectAnswer = (answer: OnboardingAnswersResponse) => answer;
export const selectApprovalByAnswer = createSelector([selectOnboardingCategoryFormInfoResult, (cats, answer: OnboardingAnswersResponse) => selectAnswer(answer)], (queryCategories, answer) => {
if (!queryCategories) return [];
let foundApproval: OnboardingApproval | undefined = undefined;
if (!queryCategories) return foundApproval;
for (const category of queryCategories) {
const questionResult = category.OnboardingQuestions.find(question => question.OnboardingAnswers.length > 0 && question.OnboardingAnswers[0].id === answer?.id);
if (questionResult) {
foundApproval = questionResult.OnboardingAnswers[0].OnboardingAnswerApprovals[0];
break;
}
}
return foundApproval;
});
usage
const storedApproval = useSelector(state => selectApprovalByAnswer(state, { project_id: answer?.project_id, category_id }, answer));
I'm sure I'm missing a key piece of understanding here. Any help would be fantastic :)

How to use useQuery hook without variables?

I'm using Typescript, and I know how to use useQuery hook with variables, but now I have a GraphQL query without variables like below:
const GetTopAlertsQuery = gql`
query getTopAlerts {
getTopAlerts {
ens
walletAddress
}
}
`;
Basically I just need it return all the data in the database without doing any filtering. I have already implemented the back-end and it works successfully, so the query should be good.
I also have set up these two interfaces to hold the data:
interface topAlertValue {
ens: string;
walletAddress: string;
}
interface jsonData {
topalerts: topAlertValue[];
}
And I have tried the below ways, but none of them work:
// attempt #1
const { data } = useQuery<jsonData>(
GetTopAlertsQuery
);
// attempt #2
const data = ({ topalerts }: jsonData ) => {
useQuery(GetTopAlertsQuery);
};
// attempt #3
const data = <Query<Data, Variables> query={GetTopAlertsQuery}>
{({ loading, error, data }) => { ... }}
</Query>
If you know how to use useQuery hook without variables, please help me out! Thanks!

How do you access query arguments in getSelectors() when using createEntityAdapter with RTK Query

I've been following along the REDUX essentials guide and I'm at part 8, combining RTK Query with the createEntityAdapter. I'm using the guide to implement it in a personal project where my getUni endpoint has an argument named country, as you can see from the code snippet below.
I'm wondering is there anyway to access the country argument value from the state in universityAdaptor.getSelector(state => ) at the bottom of the snippet, as the query key name keeps changing.
import {
createEntityAdapter,
createSelector,
nanoid
} from "#reduxjs/toolkit";
import {
apiSlice
} from "../api/apiSlice";
const universityAdapter = createEntityAdapter({})
const initialState = universityAdapter.getInitialState();
export const extendedApiSlice = apiSlice.injectEndpoints({
endpoints: builder => ({
getUni: builder.query({
query: country => ({
url: `http://universities.hipolabs.com/search?country=${country}`,
}),
transformResponse: responseData => {
let resConvert = responseData.slice()
.sort((a, b) => a.name.localeCompare(b.name))
.map(each => {
return { ...each,
id: nanoid()
}
});
return universityAdapter.setAll(initialState, resConvert)
}
})
})
});
export const {
useGetUniQuery
} = extendedApiSlice;
export const {
selectAll: getAllUniversity
} = universityAdapter.getSelectors(state => {
return Object.keys({ ...state.api.queries[<DYNAMIC_QUERY_NAME>]data }).length === 0
? initialState : { ...state.api.queries[<DYNAMIC_QUERY_NAME>]data }
})
UPDATE: I got it working with a turnery operator due to the multiple redux Actions created when RTK Query handles fetching. Wondering if this is best practice as I still haven't figured out how to access the country argument.
export const { selectAll: getAllUniversity } = universityAdapter
.getSelectors(state => {
return !Object.values(state.api.queries)[0]
? initialState : Object.values(state.api.queries)[0].status !== 'fulfilled'
? initialState : Object.values(state.api.queries)[0].data
})
I wrote that "Essentials" tutorial :)
I'm actually a bit confused what your question is - can you clarify what specifically you're trying to do?
That said, I'll try to offer some hopefully relevant info.
First, you don't need to manually call someEndpoint.select() most of the time - instead, call const { data } = useGetThingQuery("someArg"), and RTKQ will fetch and return it. You only need to call someEndpoint.select() if you're manually constructing a selector for use elsewhere.
Second, if you are manually trying to construct a selector, keep in mind that the point of someEndpoint.select() is to construct "a selector that gives you back the entire cache entry for that cache key". What you usually want from that cache entry is just the received value, which is stored as cacheEntry.data, and in this case that will contain the normalized { ids : [], entities: {} } lookup table you returned from transformResponse().
Notionally, you might be able to do something like this:
const selectNormalizedPokemonData = someApi.endpoints.getAllPokemon.select();
// These selectors expect the entity state as an arg,
// not the entire Redux root state:
// https://redux-toolkit.js.org/api/createEntityAdapter#selector-functions
const localizedPokemonSelectors = pokemonAdapter.getSelectors();
const selectPokemonEntryById = createSelector(
selectNormalizedPokemonData ,
(state, pokemonId) => pokemonId,
(pokemonData, pokemonId) => {
return localizedPokemonSelectors.selectById(pokemonData, pokemonId);
}
)
Some more info that may help see what's happening with the code in the Essentials tutorial, background - getLists endpoint takes 1 parameter, select in the service:
export const getListsResult = (state: RootState) => {
return state.tribeId ? extendedApi.endpoints.getLists.select(state.tribeId) : [];
};
And my selector in the slice:
export const selectAllLists = createSelector(getListsResult, (listsResult) => {
console.log('inside of selectAllLists selector = ', listsResult);
return listsResult.data;
// return useSelector(listsResult) ?? [];
});
Now this console logs listsResult as ƒ memoized() { function! Not something that can have .data property as tutorial suggests. Additionally return useSelector(listsResult) - makes it work, by executing the memoized function.
This is how far I got, but from what I understand, the code in the Essentials tutorial does not work as it is...
However going here https://codesandbox.io/s/distracted-chandrasekhar-r4mcn1?file=/src/features/users/usersSlice.js and adding same console log:
const selectUsersData = createSelector(selectUsersResult, (usersResult) => {
console.log("usersResult", usersResult);
return usersResult.data;
});
Shows it is not returning a memorised function, but an object with data on it instead.
Wonder if the difference happening because I have a parameter on my endpoint...
select returns a memoized curry function. Thus, call it with first with corresponding arg aka tribeId in your case and then with state. This will give you the result object back for corresponding chained selectors.
export const getListsResult = (state: RootState) => {
return state.tribeId ? extendedApi.endpoints.getLists.select(state.tribeId)(state) : [];
};
The intention of the getUni endpoint was to produce an array of university data. To implement the .getSelector function to retrieve that array, I looped over all query values, searching for a getUni query and ensuring they were fulfilled. The bottom turnery operator confirms the getUni endpoint was fired at least once otherwise, it returns the initialState value.
export const { selectAll: getAllUniversity } = universityAdapter
.getSelectors(state => {
let newObj = {};
for (const value of Object.values(state.api.queries)) {
if (value?.endpointName === 'getUni' && value?.status === 'fulfilled') {
newObj = value.data;
}
}
return !Object.values(newObj)[0] ? initialState : newObj;
})

Sorting data from an API (redux-toolkit)

I'm building a crypto app with react and redux-toolkit.
I'm trying to find a way to manage the data from the API. More specifically i want to be able to sort by value, volume, etc. and add an "isFavourite" property for each coin but i think (correct me if i'm wrong) that the only way to do this is by copying the data to another state. What i've tried so far was adding another state where i passed the data like this:
const [list, setList] = useState()
useEffect(() => {
setList(data)
}, [data])
//"const coinData = list?.data?.coins" instead of "const coinData = data?.data?.coins"
but then an error occured because the data on the "list" were "undefined".
The code bellow is the one that is running without any problems. How can i manage the API data? Am i on the right path or is there a more slick way to do what i want? Thank you!
function Main () {
const { data, error, isFetching } = useGetCryptosQuery()
if(isFetching) return 'Loading...'
const globalStats = data?.data?.stats
const coinData = data?.data?.coins
const coinList = coinData.map(coin => {
return (
<Coin
price = {coin.price}
key = {coin.uuid}
id = {coin.uuid}
name = {coin.name}
icon = {coin.iconUrl}
/>)
})
return (
<div>
<h2>Main</h2>
{coinList}
</div>
)
}
export default Main
You are on the right track - I set up something similar and added a check for null trying to map the data, and that avoids the error you probably got.
const coinList = coinData ? coinData.map((coin) => {
///...coin component
}) : <div></div>;
Then, instead of an error for undefined data, it will return an empty div - until the data is there, then it will render ok.

how can i multiple context state handle in screen?

how can i multiple context state handle in screen?                               
const { state } = useContext(profileContext);
const { state,AttendanceInGetByDate } = useContext(AttendanceContax);
You can rename properties while destructuring:
const { state: profileDAta } = useContext(profileContext);
const { state: attendanceData, AttendanceInGetByDate } = useContext(AttendanceContax);
There are a lot of articles on destructuring in Javascript, it's kind of hype recently(one of them), choose any.
Moreover you don't have to use destructuring with hooks if that creates any inconvenience to you:
const profile = useContext(profileContext);
const attendance = useContext(AttendanceContax);
...
// here you can refer to profile.state and attendance.state independently

Resources