rect-query: Pass additional key for navigation - reactjs

I am trying to pass a key for navigation, which specifies to show the query/page after the current query call.
useform.ts
...
...
export const useupdateSurveyForm = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateSurveyForm,
onSuccess: (data) => {
queryClient.invalidateQueries(["searched-public-survey"]);
},
});
};
here added "invalidateQueries(["searched-public-survey"]" directly then the code is working properly, but I want to make this dynamic. like,
queryClient.invalidateQueries(navigationqueryKey);
For that I made some changes
plans.ts
...
...
const {
mutate: updateArchiveSurveyStatus,
navigationqueryKey: "searched-public-survey",
} = useupdateSurveyForm();
...
...
pass "navigationKey: "searched-public-survey", but it shows an error
Property 'searched-public-survey' does not exist on type 'UseMutationResult<any, unknown, SurveyUpdatePayload, unknown>'.
Give me some solution to fix this problem.

I dont know if it is correct to pass the value for the query that way.
The statement from the plain.ts is just the return value. I did not found anything in the docs which would lead to putting in the navigation query key.
If I get you correctly I think what you want to do would look like:
export const useupdateSurveyForm = (key: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateSurveyForm,
onSuccess: (data) => {
queryClient.invalidateQueries([key]);
},
});
};
And the plans.ts would then look like:
const {
mutate: updateArchiveSurveyStatus,
} = useupdateSurveyForm("searched-public-survey");
Maybe that helps. :-)

Related

Filter function for Array of Objects

I'm following this tutorial and made a few changes to typescript for learning purposes but got stuck when creating a filter function from react context script.
I have a working function called getCampaigns where it maps all the object from the blockchain like below:
const getCampaigns = useCallback(async () => {
const signer = accountProvider?.getSigner();
const contractWithSigner = contract?.connect(signer);
const campaigns = await contractWithSigner?.getCampaigns();
const parsedCampaigns = campaigns.map((campaign, i) => ({
owner: campaign.owner,
title: campaign.title,
description: campaign.description,
target: ethers.utils.formatEther(campaign.target.toString()),
deadline: campaign.deadline.toNumber(),
amountCollected: ethers.utils.formatEther(
campaign.amountCollected.toString()
),
image: campaign.image,
pId: i,
}));
return parsedCampaigns;
}, [contract, accountProvider]);
This is working as it should and manage to see the content like below:
[{…}]
0:
amountCollected:"0.0"
deadline:1673049600000
description: "I want to build a Robot"
image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAA
owner:"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
pId:0
target:"3.0"
title:"Build a Robot"
As my new function, I wanted to filter from the getCampaigns function only to display all of the owner's post and display it on a Profile page like below:
const getUserCampaigns = async () => {
const allCampaigns = await getCampaigns();
const filteredCampaigns = allCampaigns.filter(
campaign => campaign.owner === account
);
return filteredCampaigns;
};
So when I console.log filteredCampaigns, it doesnt show any result. Is there anything that I missed here? The typeof account is string and it is working if I put it like this
const filteredCampaigns = allCampaigns.filter(
campaign => campaign.owner === "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
);
Update:
So far I have been playing around with the syntax and console.log the following:
const filteredCampaigns = allCampaigns.filter(campaign => {
console.log(campaign.owner);
return campaign.owner === account;
});
it's managed to fetch the same data and the typeof campaign.owner is in fact a string (same as typeof account). But when I run it like this
const filteredCampaigns = allCampaigns.filter(campaign => {
console.log(campaign.owner === account.toString());
return campaign.owner === account;
});
It's still come out as false
It is working if I hard coded like this
console.log(campaign.owner === "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
filteredCampaign is empty, because the content of account doesn't match any content of campaign.owner.
Check the content of account.
allCampaign.filter(elementOfArray => condition)
return element only if condition is true.
The logic of your getUserCampaign, looks right for what you want to do.
Not sure if this is the case, but may have sense, to have a field/global var/state where you keep all your campaigns.
In this way when you want to filter, you can do something like
const filteredCampaign = (account: string) => {
return allCampaigns.filter(campaign => campaign.owner === account);
}
filteredCampaign is not anymore async call, because doesn't have to await and receive the
account

RTK Query UseLazyQuery and Typescript: how to get the correct type from hook argument

I need to create a react hook that accepts a RTK Query UseLazyQuery function (from any endpoint), apply a debounce to it and return the trigger function and results.
Everything is working as expected but I can't manage to get the correct type to the data returned by it.
As "UseLazyQuery" type requires a type for its generic type and I don't know what type it'll be, I set it to "any", but it makes the data to be typed as "any" too.
I need help to get/extract the correct type from the given "UseLazyQuery" function given to the hook and make the returned data have the correct type.
export default function useSearch<T extends UseLazyQuery<any>>(lazyQueryFn: T) {
const [ trigger, results ] = lazyQueryFn()
const getParamsAndTrigger = async (
params: ISearchParamsRequest,
filterParams?: (params: ISearchParamsRequest) => ISearchParamsRequest
) => {
await trigger(filterParams ? filterParams(params) : params, true)
}
const debouncedTrigger = useDebouncedFn(getParamsAndTrigger, 1500)
return [debouncedTrigger, { ...results }] as const
}
The "results" const returned by "lazyQueryFn" has the type "UseQueryStateDefaultResult". I need it to have the right data type.
I had a similar situation this days and I came with this approach which fixed it and should help you too:
interface ResponseData<V> {
items: V[];
cursor: string;
}
export const useGetCursorPaginatedData = <
V,
T extends UseLazyQuery<QueryDefinition<any, any, any, ResponseData<V>, any>>
>(
lazyQuery: T
) => {
const [trigger] = lazyQuery();
const { onTriggerAction, isLoading, error } = useAsyncAction();
const [items, setItems] = useState<V[]>([]);
const [newCursor, setNewCursor] = useState<string | undefined>();
const getData = async (isRefresh?: boolean) => {
const tempList = isRefresh ? [] : items;
const { data } = await trigger(isRefresh ? undefined : newCursor);
setItems([...tempList, ...data?.items]);
setNewCursor(data?.cursor);
};
const onLoadMore = useCallback(() => {
getData();
}, [newCursor]);
const onRefresh = () => {
onTriggerAction(async () => getData(true));
};
useEffect(() => {
onTriggerAction(getData);
}, []);
return {
items,
isLoading,
isError: error != null,
onLoadMore,
onRefresh
};
};
And this is how to use it:
const {
items: dailyTweets,
isLoading,
isError,
onLoadMore,
onRefresh
} = useGetCursorPaginatedData<DailyTweet, typeof useLazyGetDailyTweetsQuery>(
useLazyGetDailyTweetsQuery
);
Basically what you need extra is to define an interface for the QueryDefinition Response, in my case ResponseData and add one more generic type to function definition that is the type of the returned data in your response.
I hope this will help you.

Saving an ID value from an API to a User with GraphQL

I'm working on a video game website where a user can save a game to a list. How this is supposed to work is when the user clicks "Complete Game", the ID of the game is saved to a state that holds the value. The value is then passed into the mutation, then the mutation runs, saving the ID of the game to the users list of completed games. However, all I'm seeing in the console is this:
"GraphQLError: Variable \"$addGame\" got invalid value { gameId: 740, name: \"Halo: Combat Evolved\",
The above error continues, listing the entirety of the API response, instead of just the gameId.
I was able to successfully add the game to the list in the explorer with the following mutation:
mutation completeGame($addGame: AddNewGame!) {
completeGame(addGame: $addGame) {
_id
completedGameCount
completedGames {
gameId
}
}
}
with the following variable:
{
"addGame": {"gameId": 740}
}
How can I trim down what is being passed into the mutation to just be the gameId?
Below is the entirety of the page, except the return statement at the bottom.
const [selectedGame, setSelectedGame] = useState([]);
const [savedGameIds, setSavedGameIds] = useState(getSavedGameIds());
const [completeGame, { error }] = useMutation(COMPLETE_GAME);
const { id: gameId } = useParams();
useEffect(() => {
return () => saveGameIds(savedGameIds);
});
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
platformId: game.platforms,
genres: game.genres,
genreId: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
const handleCompleteGame = async (gameId) => {
const gameToComplete = selectedGame.find((game) => game.gameId === gameId);
const token = Auth.loggedIn() ? Auth.getToken() : null;
if (!token) {
return false;
}
try {
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
console.log(data);
setSavedGameIds([...savedGameIds, gameToComplete]);
} catch (err) {
console.error(err);
}
};
With the mutation working in the explorer when I'm able to explicitly define the variable, I am led to believe that the issue is not with the resolver or the typedef, so I'm going to omit those from this post because I don't want it to get too long.
However, I'd be happy to attach any extra code (resolver, typeDef, getSavedGameIds function, etc) if it would allow anyone to assist. The issue (I think) lies in getting my response to match the syntax I used in the explorer, which means trimming down everything except the gameId.
I specifically am extremely suspicious of this line
const gameToComplete = selectedGame.find((game) => game.gameId === gameId)
but I have fiddled around with that for awhile to no avail.
Thank you to anyone who is able to help!
It sounds like you're trying to pass more into your mutation then your schema is defined to allow. In this part:
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
You're spreading gameToComplete here which means everything in the gameToComplete object is going to be sent as a variable. If your schema is setup to just expect gameId to be passed in, but your error message is showing that name is also being passed in, you just need to adjust your variables to exclude everything you can't accept. Try:
const { data } = await completeGame({
variables: { addGame: { gameId } },
});

context is giving me undefined

I am uncertain why after the initial load the values in context becomes undefined.
The way I have my context written up is:
export const ProductListContext = createContext({});
export const useListProductContext = () => useContext(ProductListContext);
export const ListProductContextProvider = ({ children }) => {
const [listProduct, setListProduct] = useState({
images: [],
title: "Hello",
});
return (
<ProductListContext.Provider value={{ listProduct, setListProduct }}>
{children}
</ProductListContext.Provider>
);
};
On the initial load of my component. I so get the listProduct to be correct as a console.log will produce
the list is Object {
"images": Array [],
"title": "Hello",
}
The problem is when I try to read listProduct again after it says it is undefined unless I save it to a useState. Any help on this is appreciated. The problem is within the pickImage function
// Initial has all properties correctly
const { listProduct, setListProduct } = useListProductContext();
// Seems to work at all times when I save it here
const [product] = useState(listProduct);
console.log('the list product listed is ', listProduct);
useEffect(() => {
(async () => {
if (Platform.OS !== 'web') {
const {
status,
} = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
exif: true,
});
// PROBLEM - listProduct is undefined
console.log('before copy it is ', listProduct);
const listProduct = { ...product };
console.log('the list is', listProduct);
listProduct.images.push(result.uri);
// listProduct.images.push(result.uri);
// const images = listProduct.images;
// images.push(result.uri);
setListProduct({ ...listProduct });
return;
};
Your useListProductContext is violating the rules of hooks, as React sees the use qualifier to validate the rules of hooks.
Rules of Hooks
Using a Custom Hook
"Do I have to name my custom Hooks starting with “use”? Please do. This convention is very important. Without it, we wouldn’t be able to automatically check for violations of rules of Hooks because we couldn’t tell if a certain function contains calls to Hooks inside of it."

How call two function under onPress in react

Here the addToCart function. i want to call this funtion and navigate to another page.
addToCart = () => {
const {product, quantity, variation, meta_data} = this.state;
const {dispatch} = this.props;
let check = true;
// Check select variations
if (product.get('type') === productType.VARIABLE) {
const attributeProduct = product
.get('attributes')
.filter(attr => attr.get('variation'));
if (!meta_data.size || attributeProduct.size !== meta_data.size) {
check = false;
showMessage({
message: 'Please select variations',
type: 'danger',
});
}
}
if (check) {
dispatch(
addToCart(
{
product_id: product.get('id'),
quantity,
variation,
product,
meta_data,
},
() => this.setState({isAddToCart: true}),
),
);
}
};
And Render function
<FooterProduct
isAddToCart={isAddToCart}
onPressAddCart={this.addToCart}
onPressBuyNow={
this.addToCart // function one
() => navigation.navigate(homeTabs.cart) // function two
}
/>
how can I run both? actually I try to create a buy now button.
thank you.
Create a custom method that calls both the methods like below
const combinedMethod = () => {
this.addToCart();
navigation.navigate(homeTabs.cart);
}
And use the above method like
<FooterProduct
isAddToCart={isAddToCart}
onPressAddCart={this.addToCart}
onPressBuyNow={combinedMethod}
/>
Try this one:
onPressBuyNow = () => {
this.addToCart(); // function one
navigation.navigate(homeTabs.cart); // function two
}
The answer would be either this or create a wrapper function like other folks suggest.
Edit
Modified the answer after Michael Mishin's comment.

Resources