I am trying to reload my table of data once my useMutation has completed.
On page load i am querying:
const { loading: appLoading, data: applicationsData } = useQuery(
applications.operations.GET_APPLICATIONS_BY_COMPANY,
{
client: applications.client,
variables: { companyId: userDetails.companyId },
}
)
when a user selects a button to clone a record:
const [
CloneApplication,
{ loading: cloneLoading, data: cloneData, error: cloneError },
] = useMutation(applications.operations.CLONE_APPLICATION_BY_COMPANY, {
client: applications.client,
onCompleted: (data) => {
setFinalData((prev) => [...prev, data]), console.log('data', data)
},
})
im adding a record to the list but when i refresh its not there. My assumption is instead of adding it to state, I need to refetch the applications and then save that to state which in turn will automatically refresh the table.
My question is how can i do that?
Edit:
const { applications } = apis
const { queryString, parameters } = getTemplatesListApiDetails()
const [finalData, setFinalData] = useState<any>([])
const [templatesList, setTemplatesList] = useState([])
const { loading, data } = useQuery(queryString, parameters)
const { loading: appLoading, data: applicationsData } = useQuery(
applications.operations.GET_APPLICATIONS_BY_COMPANY,
{
client: applications.client,
variables: { companyId: userDetails.companyId },
}
)
const [
CloneApplication,
{ loading: cloneLoading, data: cloneData, error: cloneError },
] = useMutation(applications.operations.CLONE_APPLICATION_BY_COMPANY, {
client: applications.client,
refetchQueries: [
{ query: applications.operations.GET_APPLICATIONS_BY_COMPANY },
],
})
useEffect(() => {
if (data && data.getCompanyTemplates)
setTemplatesList(
userDetails.globalTemplates === false
? data.getCompanyTemplates
: data.getAllTemplates
)
if (applicationsData && templatesList) {
const newFinalData = getFinalData({
applicationsList: applicationsData.getApplicationsByCompany,
templatesList: templatesList,
})
setFinalData(newFinalData)
}
}, [applicationsData, cloneData, data, templatesList])
getFinalData Function
export function getFinalData(request: {
templatesList: GetAllTemplate[]
applicationsList: GetApplicationsByCompany[]
}): FinalDataResponse[] {
const templates = request.templatesList.map((template) => {
const applicationsForTemplate = request.applicationsList.filter(
(app) => app.templateId === template.templateId
)
return { ...template, applications: applicationsForTemplate }
})
const groupedData = _.chain(templates)
.groupBy('templateId')
.map((value, key) => {
const templateName = _.chain(value)
.groupBy('templateName')
.map((value, key) => key)
.value()
const createdDate = _.chain(value)
.groupBy('dateCreated')
.map((value, key) => dayjs(key).format('ll'))
.value()
const lastModified = _.chain(value)
.groupBy('lastModified')
.map((value, key) => dayjs(key).format('ll'))
.value()
return {
templateId: key,
templateName: templateName[0],
createdDate: createdDate[0],
lastModified: lastModified[0],
applications: value[0].applications,
}
})
.value()
const finalData = groupedData.map((object, index) => {
return {
...object,
totalApplications: object.applications.length,
}
})
console.log('returning final data: ', finalData)
return finalData
}
To refetch the data automatically, you need to invalidate the previously cached results. In apollo, this is done using refetchQueries:
useMutation(applications.operations.CLONE_APPLICATION_BY_COMPANY, {
refetchQueries: [{ query: applications.operations.CLONE_APPLICATION_BY_COMPANY}]
})
More ways of solving this here: https://www.apollographql.com/blog/apollo-client/caching/when-to-use-refetch-queries/
Related
I change the redux in my project to ReactQuery,and i got some problem with put req in my code.
this is my code
const { dispatch } = store;
const editClientDataAsync = async ( id,data ) => {
await axiosObj().put(`clients/${id}`,data);
}
const { mutateAsync: editClientData, isLoading } = useMutation(['editClientData'], editClientDataAsync, {
onSuccess: () => dispatch({ type: SUCCESS_DATA, payload: { message: "Success" } }),
onError: () => dispatch({ type: ERROR_DATA, payload: { message: "Error" } })
});
return { editClientData, isLoading }
}
same problem with when i try to get some req with id
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async ({ queryKey }) => {
const [_, { id }] = queryKey;
console.log(queryKey)
if (!id)
return;
const { data } = await axiosObj().get(`clients/${id}`)
console.log(data)
return data;
}
const { data: clientDetails, isLoading } = useQuery(['ClientId', { id }], getClientDetails)
return { clientDetails, isLoading }
Mutation functions only take 1 argument
Check where you use the editClientData mutation and pass the arguments in one object.
const editClientDataAsync = async ({ id, data }) => {
await axiosObj().put(`clients/${id}`,data);
}
return useMutation(['editClientData'], editClientDataAsync, ...);
Are you sure you get an id passed to the function?
You can disable the query until you get that id with the enabled option, so you don't make an unnecessary http call.
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async (id) => {
const { data } = await axiosObj().get(`clients/${id}`)
return data;
}
return useQuery(['client', id], () => getClientDetails(id), { enabled: !!id })
Disable/pausing queries
I am receiving data from an api call, taking that data and restructuring it to properly display in a table. When a user clicks a button I am trying to create a copy of that record. I've got it all working, its just not updating the table with the appended, or removed (for delete) data. until after i refresh the page through the browser.
Is it possible to call a function after refetchQueries?
const {
loading: appLoading,
data: applicationsData,
refetch: refetchApplicationsData,
} = useQuery(applications.operations.GET_APPLICATIONS_BY_COMPANY, {
client: applications.client,
variables: { companyId: userDetails.companyId },
})
const [
CloneApplication,
{ loading: cloneLoading, data: cloneData, error: cloneError },
] = useMutation(applications.operations.CLONE_APPLICATION_BY_COMPANY, {
client: applications.client,
onCompleted: () => {
refetchApplicationsData
},
})
useEffect(() => {
if (applicationsData && templatesList) {
const newFinalData = getFinalData({
applicationsList: applicationsData.getApplicationsByCompany,
templatesList: templatesList,
})
console.log('oldFinalData: ', finalData)
console.log('newFinalData: ', newFinalData)
setFinalData(newFinalData)
console.log('updatedFinalData: ', finalData)
}
}, [applicationsData, templatesList])
const cloneAndRefresh = (applicationId, companyId, ucId) => {
CloneApplication({
variables: {
applicationId: applicationId,
companyId: companyId,
ucId: ucId,
},
}).then(({ data: responseData }) => {
if (responseData) {
console.log('response data: ', responseData)
console.log('applications: ', applicationsData)
}
})
}
the function to restructure data:
export function getFinalData(request: {
templatesList: GetAllTemplate[]
applicationsList: GetApplicationsByCompany[]
}): FinalDataResponse[] {
const templates = request.templatesList.map((template) => {
const applicationsForTemplate = request.applicationsList.filter(
(app) => app.templateId === template.templateId
)
return { ...template, applications: applicationsForTemplate }
})
const groupedData = _.chain(templates)
.groupBy('templateId')
.map((value, key) => {
const templateName = _.chain(value)
.groupBy('templateName')
.map((value, key) => key)
.value()
const createdDate = _.chain(value)
.groupBy('dateCreated')
.map((value, key) => dayjs(key).format('ll'))
.value()
const lastModified = _.chain(value)
.groupBy('lastModified')
.map((value, key) => dayjs(key).format('ll'))
.value()
return {
templateId: key,
templateName: templateName[0],
createdDate: createdDate[0],
lastModified: lastModified[0],
applications: value[0].applications,
}
})
.value()
const finalData = groupedData.map((object, index) => {
return {
...object,
totalApplications: object.applications.length,
}
})
console.log('returning final data: ', finalData)
return finalData
}
I guess im trying to rerun getFinalData after the refetchquery then save it to state and it should re-render the table?
EDIT: I've updated my queries with new code, though it didnt quite work. If its possible to get the data from the refetched query I think i could make it work. I assume that refetching the query would update applicationsData as a result but i dont think it did?
By default, the useQuery hook checks the Apollo Client cache to see if all the data you requested is already available locally. If all data is available locally, useQuery returns that data and doesn't query your GraphQL server. This cache-first policy is Apollo Client's default fetch policy. If you say that you will call handleRefresh() after mutation the below code will work fine.
here read fetch policy
const {
loading: appLoading,
data: applicationsData,
refetch: refetchApplicationsData,
} = useQuery(applications.operations.GET_APPLICATIONS_BY_COMPANY, {
client: applications.client,
variables: { companyId: userDetails.companyId },
fetchPolicy: "network-only",
})
I'm updating an array and I wanted to update the productCode based on the given newCode response. This is by clicking the 'CREATE ALL PRODUCTS' button.
I'm thinking that the problem is on the reducer. It's currently not updating the productCode and newProductCode
Tip: productIndex is the key to finding it
Click Here: CODESANDBOX
Action
export const createAllProducts = (products) => async (dispatch) => {
try {
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_REQUEST
});
const responses = [
{
config: null,
data: {
newCode: "NEW_AA"
},
headers: null
},
{
config: null,
data: {
newCode: "NEW_FF"
},
headers: null
},
{
config: null,
data: {
newCode: "NEW_GG"
},
headers: null
}
];
const finalResponses = responses.map((product, index) => ({
newProductCode: product.data.newCode,
productCode: product.data.newCode,
productIndex: products[index].productIndex
}));
console.log(finalResponses);
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_SUCCESS,
payload: finalResponses
});
} catch (error) {
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_FAILURE
});
}
};
Reducer
case appConstants.CREATE_ALL_PRODUCTS_SUCCESS:
const updatedProducts = state.products.map((product, index) => {
const found = action.payload.find((el) => el.productIndex === index);
return found
? {
...updatedProducts,
productCode: found.productCode,
newProductCode: found.newProductCode
}
: product;
});
return {
...state,
isCreatingAllProducts: false,
products: updatedProducts
};
The issue is with the reducer
case appConstants.CREATE_ALL_PRODUCTS_SUCCESS:
return {
...state,
products: state.products.map((product, index) => {
const found = action.payload.find((el) => el.productIndex === index);
console.log(found);
return found
? {
...product,
productCode: found.productCode,
newProductCode: found.newProductCode
}
: product;
})
};
You used reduce methods with the initial value state, which is the actually old state.
Consider this example:
const state = { history: null }
const payload = [ 'hello', 'equal' ]
//your current reducer
const newState = payload.reduce((acc, cur) => { acc[cur] = cur; return acc } , state)
//the state reference point to the same obj, then redux will not trigger re-render
console.log(newState === state) // true
I tried so hard to update Apollo cache after running Mutation, but i couldn't be able to remove 1 second delay after the mutation.
I followed 'ac3-state-management-examples' for solve this problem, but still couldn't find any problem.
This is my client-side code.
export const DELETE_ITEM_IN_CART = gql`
mutation DeleteItemInCart($cartItemId: String!) {
DeleteItemInCart(cartItemId: $cartItemId)
}
`;
export function useDeleteItemInCart() {
console.log(`DELETION START! ${Date()}`);
const [mutate, { data, error }] = useMutation<
DeleteItemInCartType.DeleteItemInCart,
DeleteItemInCartType.DeleteItemInCartVariables
>(DELETE_ITEM_IN_CART, {
update(cache, { data }) {
const deletedCartItemId = data?.DeleteItemInCart;
const existingCartItems = cache.readQuery<myCart>({
query: MY_CART,
});
if (existingCartItems && deletedCartItem && existingCartItems.myCart) {
cache.writeQuery({
query: MY_CART,
data: {
myCart: {
cartItem: existingCartItems.myCart.cartItem.filter(
t => t.id !== deletedCartItemId,
),
},
},
});
console.log(`DELETION OVER! ${Date()}`);
}
},
});
return { mutate, data, error };
}
And here's my server-side mutation
export const DeleteItemInCart = mutationField('DeleteItemInCart', {
args: {cartItemId: nonNull('String')},
type: nonNull('String'),
description: 'Delete an item in my cart',
resolve: (_, {cartItemId}, ctx) => {
const {prisma} = ctx;
try {
prisma.cartItem.delete({
where: {
id: cartItemId,
},
});
return cartItemId;
} catch (error) {
return cartItemId;
}
},
});
This is an example of Apollo-remote-state-mananagement
export const DELETE_TODO = gql`
mutation DeleteTodo ($id: Int!) {
deleteTodo (id: $id) {
success
todo {
id
text
completed
}
error {
... on TodoNotFoundError {
message
}
}
}
}
`
export function useDeleteTodo () {
const [mutate, { data, error }] = useMutation<
DeleteTodoTypes.DeleteTodo,
DeleteTodoTypes.DeleteTodoVariables
>(
DELETE_TODO,
{
update (cache, { data }) {
const deletedTodoId = data?.deleteTodo.todo?.id;
const allTodos = cache.readQuery<GetAllTodos>({
query: GET_ALL_TODOS
});
cache.writeQuery({
query: GET_ALL_TODOS,
data: {
todos: {
edges: allTodos?.todos.edges.filter((t) => t?.node.id !== deletedTodoId)
},
},
});
}
}
)
return { mutate, data, error };
}
Any advice?
1 second delay is inevitable using apollo cache?
I took a short video of my issue. i dont think it's inevitable...
I'm new to Firebase Realtime Database, and i'm trying to implement a search field that allow users to search for other users and view their profiles.
The Problem Is:
I want to make the search realTime(on each input change).but whenever a new request's sent, the old request is still working in the backend which's causing unexpected behavior,i've wrapped this functionality in a useEffect Hook,old sideEffects has to be cleaned up to make the query results predictable,how can i abort the previous request.
useSearchOwner Custom Hook:
const useSearchOwner = () => {
const [{ SearchValue, SearchResult, Search }, dispatch] = useReducer(
reducer,
{
SearchValue: "",
SearchResult: "",
Search: false,
}
);
const isFirstRender = useRef(true);
const onChangeHandler = (e) =>
dispatch({
type: ACTIONS.UPDATE_SEARCH_VALUE,
payload: { searchValue: e.target.value },
});
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: ACTIONS.START_SEARCHING });
const DispatchQueryByResult = async () => {
const ArrayOfOwners = await FirebaseUtilityInstance.SearchOwnerResult(
SearchValue
);
dispatch({
type: ACTIONS.UPDATE_SEARCH_RESULT,
payload: { searchResult: ArrayOfOwners },
});
dispatch({ type: ACTIONS.STOP_SEARCHING });
return () => {
FirebaseUtilityInstance.SearchOwnerCleanup();
};
};
DispatchQueryByResult();
}, [SearchValue]);
useEffect(() => {
console.log(SearchResult);
}, [SearchResult]);
return {
onChangeHandler: onChangeHandler,
Query: SearchValue,
QueryResult: SearchResult,
isSearching: Search,
};
};
Firebase Method To Do Query:
SearchOwnerResult = async (Query) => {
const { firstName, lastName } = getFirstNameAndLastName(Query);
let ArrayOfOwners = [];
await this.Database()
.ref("users")
.orderByChild("UserType")
.equalTo("owner")
.once("value", (snapshot) => {
const OwnersContainer = snapshot.val();
const keys = Object.keys(OwnersContainer);
for (let i = 0; i < keys.length; i++) {
const CurrentOwner = OwnersContainer[keys[i]];
if (
CurrentOwner.FirstName === firstName ||
CurrentOwner.LastName === lastName
) {
ArrayOfOwners.push(OwnersContainer[keys[i]]);
}
}
});
return ArrayOfOwners;
};