how update state in rtk query by useEffect - redux-toolkit

i have two endpoints when i add new value to endpoint one i want endpoint two updated by useEffect Hook
const PostDetails = (props) => {
const id = props.match.params.id;
const [addNewComment, { isSuccess: success, isError }] = useAddNewCommentMutation();
const dispatch = useDispatch();
const selectPost = useMemo(() => postApi.endpoints.getPost.select(id), [id]);
const { data: post, isLoading } = useSelector(selectPost);
useEffect(() => {
const result = dispatch(postApi.endpoints.getPost.initiate(id));
return result.unsubscribe;
}, [id, dispatch, success]);
it said
name(pin):"ConditionError"
message(pin):"Aborted due to condition callback returning false."

That just means "there is already data and I have no reason to assume it is outdated, I'm not gonna fetch again".
You can do a
dispatch(postApi.endpoints.getPost.initiate(id, {forceRefetch: true}));
but that's not what you should do here.
What you really should use this is the invalidation feature.
So your endpoint getPost has a providesTags function that returns [{ type: 'Post', id: 5 }] and your addNewComment mutation has an invalidatesTags function that returns [{ type: 'Post', id: 5 }], too.
That way, whenever you call that addNewComment mutation, the getPost endpoint will refetch.
Please read the documentation chapter about Automated Refetching

Related

Why does MongoDB give a response stating the document has been updated, but no update it made?

I can't figure this out.
I have an object in an array within another object which I need to update with mongoDB updateOne.
I make the call, it says it found it OK and has updated it ({ n: 1, nModified: 1, ok: 1 }). But then on checking no update is made in the database...
What am I doing wrong here?
Model
const pathwayDetailsSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
associatedPathwayID: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
pages: [
{
_id: { type: String },
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
},
widgets: [
{
more nested objects..
}
]
}
]
}
Router call
router.post('/pageupdate/',auth, async(req,res)=>{
const pageID = req.body.pageID; //Page ID string
const pathwayID = req.body.pathwayID; // pathwayID string
const update = req.body.update; //{x: new X value, y: new Y value}
try{
console.log("receieved: ",pageID, pathwayID, update);
let updatedDoc = await PathwayDetails.updateOne(
{ associatedPathwayID: pathwayID, "pages._id": pageID },
{ $set: update}
);
console.log("successful? ",updatedDoc)
res.status(201).send(updatedDoc)
}
catch(e){
etc...
}
});
Changing x and y passes through fine and it says it updates. But on checking the database no change is made...
I think you have missed async keyword before await.
A function should be an async inorder to use the await keyword.
So, you wrap the code inside a async function.
Since you are not using the async function, await has lost it's functionality, so it's basically updating the old value again and again. It's not awaiting fo the new value. So you are not seeing any change in the value in the database even though the code is executed successfully.
Try the below code:
const update_document = async (req, res) => {
let updatedDoc = await PathwayDetails.updateOne(
{ associatedPathwayID: pathwayID, "pages._id": pageID },
{ $set: update}
);
res.status(201).send(updatedDoc)
};
After this call the update_document function with the router.
I think this will work.
Figured it out.
The update I was passing wasn't pointing to a nested object correctly.
Rather than update being {x: new X value, y: new Y value}
needed to pass a nested update it must be {"pages.$.x": new X value, "pages.$.y": new Y value}
It is annoying that mongo returns a response saying it has updated the database when it can't find the field to update!

How can I fetch XML data with RTK Query?

I'm already using RTK Query with different endpoints that return JSON which are working as expected. But there is one endpoint that returns XML. I can see in the network tab that HTTP response is correct but RTK Query's auto generated hook returns data as undefined.
Here's is my query definition:
import { myApi } from './base';
export const excel = myApi.injectEndpoints({
endpoints: (builder) => ({
fetchExcelUserList: builder.query({
query: () => ({
url: 'excel/user_list',
method: 'GET',
headers: {
'Content-Type': 'application/xml; charset:utf-8',
},
}),
}),
}),
});
export const { useFetchExcelUserListQuery } = excel;
That's how I use it:
const { data } = useFetchExcelQuery('');
console.log(data);
If this explanation isn't enough, check this link that describes the same problem.
Thanks a lot.
Your data is undefined, because your query goes into an error state - by default, everything will be parsed as json. You can provide a custom parsing function using the responseHandler functionality of fetchaseQuery though.

Why does express/mongodb updateOne post allow $set of some values, but will not update others?

I am baffled by this post method, it will update fields 'x and y', but any attempt to set an array of widgets fails.
It is finding the correct item to update, passing all the required information through, but it will not allow insertion of, or update to 'widgets' fields.
Even if I remove the data intended for widgets and arbitrarily send through 'foo' it will not update with a field 'widgets'.
What am I doing wrong here???
API Call to Update Widgets. The Arbitrary X and Y values will update on the database, but any attempt to update widget makes no change
const saveUpdatedWidgets = async (update, _id) => {
console.log("called to update widgets ",update.widgets," in pagecard saveUpdatedWidgets")
let widgetObject = []
for(let u=0;u<update.widgets.length;u++){
widgetObject.push({
id: update.widgets[u].id,
text: update.widgets[u].text
})
}
Api.withToken().post('/pagewidget/'+_id,
{widgets: widgetObject, x:250, y:250}
).then(function (response) {
console.log("?worked ",response.data)
}).catch(function (error) {
console.log("page save failed for some reason on pagecard: ",error.response);
});
};
This will return the following in the console:
Code for post method is:
//THIS ROUTER WILL NOT UPDATE ANY WIDGETS FOR SOME REASON
router.post('/pagewidget/:_id',auth, async(req,res)=>{
console.log("request to update ",req.body," for id ",req.params," in pagewidgetsave post")
const query = { "_id": req.params };
const addedWidgets = req.body;
const newValues = { $set: addedWidgets }
try {
const thePage = await Pages.updateOne( query, newValues);
res.status(201).send(thePage)
console.log("updated Page: ",thePage);
}
catch(e){
console.log(e);
res.status(400).send(e)
}
})
Results from the console running node shows that values are going through, but only x and y actually update in database..
Here is the axios api.js file if there are any issues here:
import axios from 'axios';
const baseURL = process.env.REACT_APP_BASE_URL || "http://localhost:3001"
export default {
noToken() {
return axios.create({
baseURL: baseURL
});
},
withToken() {
const tokenStr = window.sessionStorage.getItem("token")
return axios.create({
baseURL: baseURL,
headers: {"Authorization" : `Bearer ${tokenStr}`}
});
}
}
What is going on!!?? It finds the page OK, and updates x and y values, but can't update widgets, even if the values for widget are just a string or number...
I found the issue. the MongoDB documentation doesn't mention this too well, and in its examples for updateOne() it passes an object for the update argument.
BUT, if you are setting a new field, this argument must be wrapped inside an array to use $set, this is because it can accept both methods to $set and to $unset. (see mongoDB docs)
(i.e. updateOne({query} , [{$set: {field:"value"}, {$unset: {otherfield:"othervalue"}])
In the end the post method just had to change to const thePage = await Pages.updateOne( query, [newValues]); (with newValues stored as an object inside an array, to allow addition of $unset if it was needed.
This is why it would update existing values OK, but it would not set new values into the database.
What a journey....
Full code for post method here
router.post('/pagewidget/:_id',auth, async(req,res)=>{
const query = {"_id": req.params._id};
const addedWidgets = req.body;
const newValues = { $set: addedWidgets }
try {
const thePage = await Pages.updateOne( query, [newValues]);
res.status(201).send(thePage)
console.log("updated Page: ",thePage);
}
catch(e){
console.log(e);
res.status(400).send(e)
}
})

Axios/mongodb request, PromiseState stuck on pending, then() part is not called

I'm trying to update my mongodb database in javascript by accessing some documents from the database, changing a specific document and then performing a patch request via axios.
When I get to the patch request I'm able to update the database however the promise is stuck on pending and thus, the then() part of the code is not run.
This is the main structure of the code:
In the first part the documents are requested from the database via axios.get:
function updateDocument(someinputdata){
g = axios.all([axios.get('/getData1),axios.get('/getData2)])
.then(response => {
Data1 = response[0].data;
Data2 = response[1].data;
adjustData(Data1,Data2);
});
}
In the second part a specific document is changed and a patch request is called:
function adjustData(Data1,Data2){
...getting specific document and change value from specific field...
var newRec = {
title: "dummyTitle",
rate: newRateValue
};
promise = axios({
url: '/patch/The Real Title',
method: 'PATCH',
data: newRec,
headers: { "Content-Type": "application/json" }
})
.then(() => {
console.log('I want this text to display but it doesn't')
});
}
If I console.log(promise):
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
On the server side I have this:
router.patch('/patch/:title', (req,res) => {
const updatedPost = Model.updateOne(
{ "title": req.params.title},
{ $set: { "rate" : req.body.rate}},
(err, result) => {
if(err) {
console.log(err);
throw err;
}
})
.then(
console.log('This text is displayed');
)
})
I want to use the first then() part to update some HTML
Why is the patch request stuck on pending (so not fulfilled or rejected)?
I've figured out what my problem was.
I needed to add
res.json({msg: "Your data has been saved"});
to the code on the server side.

Uspert multiple documents with MongoDB/Mongoose

Say I have a list of models:
const documents = [{}, {}, {}];
And I want to insert these into the DB, or update them all, but only if a condition is met:
Model.update({isSubscribed: {$ne: false}}, documents, {upsert:true},(err, result) => {
});
The above signature is surely wrong - what I want to do is insert/update the documents, where the condition is met.
There is this Bulk API:
https://docs.mongodb.com/manual/reference/method/Bulk.find.upsert/
but I can't tell if it will work when inserting multiple documents.
Imagine this scenario: We have a list of employees and a form of some sorts to give them all a penalty, at once, not one by one :)
On the backend side, you would have your eg addBulk function. Something like this:
Penalty controller
module.exports = {
addBulk: (req, res) => {
const body = req.body;
for (const item of body) {
Penalty.create(item).exec((err, response) => {
if (err) {
res.serverError(err);
return;
}
});
res.ok('Penalties added successfully');
}
}
Then you'll probably have an API on your frontend that directs to that route and specific function (endpoint):
penaltyApi
import axios from 'axios';
import {baseApiUrl} from '../config';
const penaltyApi = baseApiUrl + 'penalty'
class PenaltyApi {
static addBulk(penalties) {
return axios({
method: 'post',
url: penaltyApi + '/addBulk',
data: penalties
})
}
}
export default PenaltyApi;
...and now let's make a form and some helper functions. I'll be using React for demonstration, but it's all JS by the end of the day, right :)
// Lets first add penalties to our local state:
addPenalty = (event) => {
event.preventDefault();
let penalty = {
amount: this.state.penaltyForm.amount,
unit: this.state.penaltyForm.unit,
date: new Date(),
description: this.state.penaltyForm.description,
employee: this.state.penaltyForm.employee.value
};
this.setState(prevState => ({
penalties: [...prevState.penalties, penalty]
}));
}
Here we are mapping over our formData and returning the value and passing it to our saveBulkEmployees() function
save = () => {
let penaltiesData = Object.assign([], this.state.penalties);
penaltiesData.map(penal => {
penal.employeeId = penal.employee.id;
delete penal.employee;
return penaltiesData;
});
this.saveBulkEmployees(penaltiesData);
}
...and finally, let's save all of them at once to our database using the Bulk API
saveBulkEmployees = (data) => {
PenaltyApi.addBulk(data).then(response => {
this.success();
console.log(response.config.data)
this.resetFormAndPenaltiesList()
}).catch(error => {
console.log('error while adding multiple penalties', error);
throw(error);
})
}
So, the short answer is YES, you can absolutely do that. The longer answer is above :) I hope this was helpful to you. If any questions, please let me know, I'll try to answer them as soon as I can.