How to recall getServerSideProps value without reload the page when the value changed - reactjs

I want to change languageId value without the page reloading. When the languageId value changes the title of products is changed.
export const getServerSideProps = async ({ req, res, query }) => {
try {
const languageId = getCookie('appLanguageId', { req, res })
const countryid = getCookie('countryId', { req, res })
const appLanguage = getCookie('appLanguage', { req, res })
const { id, page, brandId } = query
var fields = [{ condition: 'contains', value: id, dataField: 'categoryPath' }];
// FILTERS
let flag = true;
if (query.brandIds) {
flag = false;
fields.push({ condition: "equal", value: query.brandIds, dataField: 'brandId' });
}
if (query.priceStart) {
flag = false;
fields.push({ condition: ">=", value: query.priceStart, dataField: 'listPrice' });
}
if (query.priceEnd) {
flag = false;
fields.push({ condition: "<=", value: query.priceEnd, dataField: 'listPrice' });
}
const response = await axios.post(urlHelper + '/wapi/v1/product/listSearch',
{
filter: {
fields,
page: { number: page || 1, size: 20, },
}
},
{
headers: { languageId, countryid }
}
);
//
const products = response?.data;
const filterOptions = (await axios.get(`${urlHelper}/wapi/v1/category/filterOptions/${id}`)).data
return {
props: {
products: products?.data || [],
totalCount: products?.totalCount || 0,
filterOptions: filterOptions,
filterObj: {
brandIds: query.brandIds ? query.brandIds : null,
priceStart: query.priceStart ? query.priceStart : null,
priceEnd: query.priceEnd ? query.priceEnd : null,
isAllNull: flag
},
loader: false
},
};
};
I use getServerSideProps. How can I fix it? I have to access product titles correctly when the language change.

Related

How to I pass a filters variable to useInfiniteQuery with pageParam?

I'm trying to pass the queryKey variable alongside the pageParam in useInfiniteQuery? I've tried for a while but:
Cannot get the page data
In some cases, the data is rendered repeatedly.
How should I pass the variables?
export const fetchInfiniteVariants = async (
filters = {},
{ pageParam = 0 }
) => {
const records = await axios.get(baseURL, {
headers: authHeader,
params: {
pageSize: 24,
offset: pageParam,
fields: [
"name",
"packshot",
"packshot_size",
"brand_logo",
"price",
"slug",
],
// filterByFormula: `({validated} = 1)`,
filterByFormula: `(${filterByFields(filters)})`,
"sort[0][field]": "priority",
"sort[0][direction]": "asc",
},
})
return records
}
export const useInfiniteVariantsQuery = (
initialRecords,
offset,
filters = { brand: "HAY" }
) => {
const infiniteVariantsQuery = useInfiniteQuery(
["infiniteVariants", filters],
() => fetchInfiniteVariants(filters),
{
initialStale: true,
staleTime: 6000,
getNextPageParam: (lastPage, pages) => lastPage.data.offset,
}
)
return {
...infiniteVariantsQuery,
}
}
The queryFn you're passing to useInfiniteQuery has request context as a parameter to that callback, as documented on the useInfiniteQuery page:
Receives a QueryFunctionContext object with the following variables:
queryKey: EnsuredQueryKey: the queryKey, guaranteed to be an Array
pageParam: unknown | undefined
You can destructure and retrieve your queryKey from that, as below:
export const useInfiniteVariantsQuery = (
initialRecords,
offset,
filters = { brand: "HAY" }
) => {
const infiniteVariantsQuery = useInfiniteQuery(
["infiniteVariants", filters],
({ queryKey, pageParam }) => fetchInfiniteVariants(queryKey[1], pageParam),
{
initialStale: true,
staleTime: 6000,
getNextPageParam: (lastPage, pages) => lastPage.data.offset,
}
)
return {
...infiniteVariantsQuery,
}
}

react-awesome-query-builder empty select when click outside select

I use asyncFetch and I select value from SelectWiget , after any click outside make empty value this select.
I use next config for fields
products: {
type: '!struct',
label: 'Products',
subfields: {
title: {
type: 'select',
label: 'Name',
fieldSettings: {
asyncFetch: async (search, offset) => {
const toSearch = _.isEmpty(search) ? 'null' : search;
const prodApi = (await axios.get(`http://localhost:8002/api/products/1/${toSearch}?offset=${offset}`, { headers: authHeader() }));
const productsValues = prodApi.data.data.map(
product => ({
title: product.title,
value: product.id
})
)
return {
values: productsValues,
hasMore: true,
}
},
useAsyncSearch: true,
useLoadMore: true,
forceAsyncSearch: false,
allowCustomValues: false
}
},
Another error is when i select some value the asyncFetch is call again.
Is this a bug in that package or i have missing some configuration ?
the package what i use is react-awesome-query-builder
the value from result of function async need to be string, so function is like :
asyncFetch: async (search, offset) => {
const toSearch = _.isEmpty(search) ? 'null' : search;
const prodApi = (await axios.get(`http://localhost:8002/api/products/1/${toSearch}?offset=${offset}`, { headers: authHeader() }));
const productsValues = prodApi.data.data.map(
product => ({
title: product.title,
value: product.id.toString(),
})
)
return {
values: productsValues,
hasMore: true,
}
}

Apollo Client delete Item from cache

Hy I'm using the Apollo Client with React. I query the posts with many different variables. So I have one post in different "caches". Now I want to delete a post. So I need to delete this specific post from all "caches".
const client = new ApolloClient({
link: errorLink.concat(authLink.concat(httpLink)),
cache: new InMemoryCache()
});
Postquery:
export const POSTS = gql`
query posts(
$after: String
$orderBy: PostOrderByInput
$tags: JSONObject
$search: String
$orderByTime: Int
) {
posts(
after: $after
orderBy: $orderBy
tags: $tags
search: $search
orderByTime: $orderByTime
) {
id
title
...
}
}
`;
I tried it with the cache.modify(), which is undefined in my mutation([https://www.apollographql.com/docs/react/caching/cache-interaction/#cachemodify][1])
const [deletePost] = useMutation(DELETE_POST, {
onError: (er) => {
console.log(er);
},
update(cache, data) {
console.log(cache.modify())//UNDEFINED!!!
cache.modify({
id: cache.identify(thread), //identify is UNDEFINED + what is thread
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
}
}
})
}
});
I also used the useApolloClient() with the same result.
THX for any help.
Instead of using cache.modify you can use cache.evict, which makes the code much shorter:
deletePost({
variables: { id },
update(cache) {
const normalizedId = cache.identify({ id, __typename: 'Post' });
cache.evict({ id: normalizedId });
cache.gc();
}
});
this option worked for me
const GET_TASKS = gql`
query tasks($listID: String!) {
tasks(listID: $listID) {
_id
title
sort
}
}
`;
const REMOVE_TASK = gql`
mutation removeTask($_id: String) {
removeTask(_id: $_id) {
_id
}
}
`;
const Tasks = () => {
const { loading, error, data } = useQuery(GET_TASKS, {
variables: { listID: '1' },
});
сonst [removeTask] = useMutation(REMOVE_TASK);
const handleRemoveItem = _id => {
removeTask({
variables: { _id },
update(cache) {
cache.modify({
fields: {
tasks(existingTaskRefs, { readField }) {
return existingTaskRefs.filter(
taskRef => _id !== readField('_id', taskRef),
);
},
},
});
},
});
};
return (...);
};
You can pass your updater to the useMutation or to the deletePost. It should be easier with deletePost since it probably knows what it tries to delete:
deletePost({
variables: { idToRemove },
update(cache) {
cache.modify({
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
},
},
});
},
});
You should change variables to match your mutation. This should work since posts is at top level of your query. With deeper fields you'll need a way to get the id of the parent object. readQuery or a chain of readField from the top might help you with that.

graphql: how to handle prev page, next page, last page and first page properly?

For frontend I am using React + Relay. I have some connection at the backend that could be queried like:
query Query {
node(id: 123456) {
teams(first: 10) {
node {
id
name
}
page_info {
start_cursor
end_cursor
}
}
}
}
So for the traditional approach, I can use skip PAGE_SIZE * curr limit PAGE_SIZE to query for next page, prev page and first page and last page (In fact I can query for random page)
But how should I implement the frontend to make these requests elegantly?
#Junchao, what Vincent said is correct. Also, you must have a re-fetch query and send refetchVariables with your first value updated. I will try to provide you an example:
export default createRefetchContainer(
TeamsComponent,
{
query: graphql`
fragment TeamsComponent_query on Query
#argumentDefinitions(
first: { type: Int }
last: { type: Int }
before: { type: String }
after: { type: String }
) {
teams(
id: { type: "ID!" }
first: { type: Int }
last: { type: Int }
before: { type: String }
after: { type: String }
) #connection(key: "TeamsComponent_teams", filters: []) {
count
pageInfo {
endCursor
hasNextPage
}
edges {
node {
id
name
}
}
}
}
`,
},
graphql`
query TeamsComponent(
$after: String
$before: String
$first: Int
$last: Int
) {
...TeamsComponent_query
#arguments(
first: $first
last: $last
after: $after
before: $before
)
}
`,
);
I tried to build an example based on your code. This is basically the idea. The bottom query is the re-fetch one. Alongside with that, you must trigger this re-fetch somehow by calling this.props.relay.refetch passing your renderVaribles. Take a deep looker into the docs about this.
Hope is helps :)
UPDATE:
Just to add something, you could have a handleLoadMore function with something like this:
handleLoadMore = () => {
const { relay, connection } = this.props;
const { isFetching } = this.state;
if (!connection) return;
const { edges, pageInfo } = connection;
if (!pageInfo.hasNextPage) return;
const total = edges.length + TOTAL_REFETCH_ITEMS;
const fragmentRenderVariables = this.getRenderVariables() || {};
const renderVariables = { first: total, ...fragmentRenderVariables };
if (isFetching) {
// do not loadMore if it is still loading more or searching
return;
}
this.setState({
isFetching: true,
});
const refetchVariables = fragmentVariables => ({
first: TOTAL_REFETCH_ITEMS,
after: pageInfo.endCursor,
});
relay.refetch(
refetchVariables,
null,
() => {
this.setState({ isFetching: false });
},
{
force: false,
},
);
};
UPDATE 2:
For going backwards, you could have something like:
loadPageBackwardsVars = () => {
const { connection, quantityPerPage } = this.props;
const { quantity } = getFormatedQuery(location);
const { endCursorOffset, startCursorOffset } = connection;
const firstItem = connection.edges.slice(startCursorOffset, endCursorOffset)[0].cursor;
const refetchVariables = fragmentVariables => ({
...fragmentVariables,
...this.getFragmentVariables(),
last: parseInt(quantity || quantityPerPage, 10) || 10,
first: null,
before: firstItem,
});
return refetchVariables;
};

Mongoose/Mongo: Update Not Saving

I'm extremely perplexed by this issue that I'm having with mongo/mongoose. I'm essentially trying to get an array of products, delete a certain product from the array, and then update the shopping chart with the new array that omits the selected product. Here's the snippet of code I'm dealing with:
const remove = (req, res, next) => {
console.log('here is the product id ' + req.body.cart.product)
delete req.body._owner // disallow owner reassignment.
Cart.find({_id: req.user.cartId})
.then((products1) => {
console.log("array of products: " + products1[0].product)
const index = products1[0].product.indexOf(req.body.cart.product)
console.log("index valeu: " + index)
if (index > -1) {
products1[0].product.splice(index, 1)
return products1[0].product
}
return products1[0].product
})
.then((products2) => {
console.log('Second Promise Input: ' + products2)
Cart.update({_id: req.user.cartId}, {$set: {product: products2}})
})
.then(() => res.sendStatus(204))
.catch(next)
}
And here's the output from my server:
Server listening on port 4741
here is the product id 5952b57ea52d092b8d34c6b0
array of products: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
index valeu: 0
Second Promise Input: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
PATCH /carts-decrease/595b037e128cfd37e0c864d7 204 38.773 ms
According to my console.logs, I'm getting the array just the way I want it but it simply does not update the shopping cart with the new array. I've been staring at this code for far too long and I'd appreciate a second set of eyes on this. Thanks.
P.S. Ignore the fact that the product ids are all the same, its just a testing variable
Cart Schema:
'use strict'
const mongoose = require('mongoose')
const cartSchema = new mongoose.Schema({
product: {
type: Array,
required: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false
}
}, {
timestamps: true,
toJSON: {
virtuals: true,
transform: function (doc, ret, options) {
const userId = (options.user && options.user._id) || false
ret.editable = userId && userId.equals(doc._owner)
return ret
}
}
})
const Cart = mongoose.model('Cart', cartSchema)
module.exports = Cart
Product Schema:
'use strict'
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
}
}, {
toJSON: {
virtuals: true
}
})
const Product = mongoose.model('Product', productSchema)
module.exports = Product
Show request:
const show = (req, res) => {
const product = {}
product.array = []
// console.log(req.cart.product)
const promises = []
Promise.all(req.cart.product.map(function (id) {
return Product.find({_id: ObjectId(id)})
})).then(function (products) {
console.log(products)
req.cart.product = products
return res.json({
cart: req.cart.toJSON({virtuals: true, user: req.user})
})
}).catch(function (err) {
console.log(err)
return res.sendStatus(500)
})
}
I would recommend you to slightly modify your cartSchema and store products in the form of an array of embedded documents:
const cartSchema = new mongoose.Schema({
products: [{
name: { type: String },
price: { type: Number }
...
}]
...
});
If you do this you can simply use the $pull update operator to remove products from your cart:
{ $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
In your case the query should then look like this:
Cart.update(
{ _id: req.user.cartId },
{ $pull: { products: { '_id': req.body.cart.product } }}
);
As the embedded documents will have their own ObjectId there will only be one document matching the query.

Resources