Deeper levels in reducer getting overwritten - reactjs

I have an action that fetches some data from a database using multiple calls. I am trying to create the correct structure for my data in the reducer, but the deeper levels of the object just get overwritten.
React call:
_.forEach(['cat1', 'cat2', 'cat3', 'cat4'], (category) => {
_.forEach(['day1', 'day2', 'day3'], (day) => {
this.props.fetchSingleResult('2018', 'USA', day, category)
})
})
My action:
export function fetchSingleResult(year, venue, day, category) {
// Get race result from a single category and day
const request = axios.get('/api/races/singleresult',
{params: {
year: year,
venue: venue,
day: day,
category: category }
});
return {
type: FETCH_SINGLE_RESULT,
payload: request,
meta: {year: year, venue: venue, day: day, category: category}
};
}
My reducer:
case FETCH_SINGLE_RESULT:
return {...state,
[action.meta.year]: {...state[action.meta.year],
[action.meta.venue]: {...state[action.meta.venue],
[action.meta.category]: {...state[action.meta.category],
[action.meta.day]: action.payload.data}}}};
The output of this is an object that comes from this only has the last day and category as keys i.e.
{2018: {'USA': {'cat4': {'day3' : data}}}}
instead of having all categories and days like:
{2018:
{'USA':
{'cat1': {'day1' : data, 'day2' : data, 'day3' : data}},
{'cat2': {'day1' : data, 'day2' : data, 'day3' : data}},
{'cat3': {'day1' : data, 'day2' : data, 'day3' : data}},
{'cat4': {'day1' : data, 'day2' : data, 'day3' : data}}
}
}
Any ideas?
There are a lot of years and a lot of venues.

The way you're using the spread operator is causing that result, try:
case FETCH_SINGLE_RESULT:
const {year, venue, category, day} = action.meta;
return _.merge(state, {[year]: {[venue]: {[category]: {[day]: action.payload.data}}}});

Look first of all this level of nesting is very difficult to maintain and respect immutability in react. You should change your data endpoint's response structure.
const getnewState = (state, action) => {
const initialStateValue = {...state }
const { year, venue, day, category } = action.meta
const data = action.payload
if(initialStateValue[year]) {
const currentYearValue = initialStateValue[year]
if (currentYearValue[venue]) {
const currentVenueValue = currentYearValue[venue]
if(currentVenueValue[category]) {
let currentCategoryValue = currentVenueValue[category]
currentCategoryValue = {
...currentCategoryValue,
[day]: data,
}
}
}
}
}
I just handled one flow in this code above , you need to take care of other scenartios also like , if the key is iteself not there for all levels..
I went on to code the solution for this, but found it overly complex to code.

Related

Receive object datatype from back4app in react

I have an issue with receiving the datatype object from the database in back4app in the column "expiration" 1. I have no issue with receiving the other datatypes (String, Array..), but I can't deconstruct the object into day, month and year.
I have the following code where I receive the other datatypes and save it in an useState:
const [ideaTable, setIdeaTable] = useState();
async function fetchIdeas() {
const query = new Parse.Query("Idea");
try {
const ideas = await query.find();
console.log("Parse Objects: ", ideas);
const destructuredIdeas = destructureIdeas(ideas);
setIdeaTable(destructuredIdeas);
console.log("from readIdeas: ", ideaTable);
return true;
} catch (error) {
alert(`Error ${error.message}`);
return false;
}
}
function destructure(idea) {
return {
id: idea.id,
title: idea.get("title"),
section: idea.get("tags"),
author: idea.get("author"),
/* expiration: JSON.stringify(idea.get("expiration")) */
expiration: idea.get("expiration")
};
}
function destructureIdeas(ideas) {
return ideas.map(destructure);
}
Here I just call: "expiration: idea.get("expiration")" as in above code. So, I think I need to restructure the expiration object to get the day, month and year... but how?
If I do this: "expiration: JSON.stringify(idea.get("expiration"))", I get the following result 3.
So, what to do if I want to display for example: "14.12.2021"?
I found the answer myself:
expiration: idea.get("expiration").day
expiration: idea.get("expiration").month
expiration: idea.get("expiration").month

Firebase realtime database pagination not working as expected [duplicate]

I am trying to sort orders in descending and start after on particular key but its not working
nextAfter : -Mk4-n5BnVpwhum62n2g or any Key / _id
db record:
{
'-Mk4-n5BnVpwhum62n2g': {
_id: '-Mk4-n5BnVpwhum62n2g',
createdAt: -1632171667626,
name: 'abc'
},
'-Mk40Ko9DbSeMdjIpY4': {
_id: '-Mk40Ko9DbSeMdjIpY4',
createdAt: -1632171809831,
name: 'new '
}
}
trying query :
query = dbRef.orderByChild('createdAt').startAfter(nextAfter).limitToFirst(limit);
The startAfter() method accepts two parameters - the first is the relevant orderBy value and the second is the optional key of the last entry (for when multiple entries have the same value for the orderBy criteria). So to correctly paginate the reference, you need to pass the previous entry's createdAt value and its key.
const baseQuery = dbRef
.orderByChild('createdAt')
.limitToFirst(limit);
let pageCount = 0, lastChildOnPage = undefined;
const children = [];
while (true) {
const pageQuery = pageCount === 0
? baseQuery
: baseQuery
.startAfter(lastChildOnPage.createdAt, lastChildOnPage.key);
const pageSnapshot = await pageQuery.once('value');
pageSnapshot.forEach((childSnapshot) => {
children.push({ key: childSnapshot.key, ...childSnapshot.val() });
})
const newLastChildOnPage = children[children.length-1];
if (lastChildOnPage !== newLastChildOnPage) {
lastChildOnPage = newLastChildOnPage;
pageCount++;
} else {
break; // no more data
}
}
console.log(`Grabbed ${pageCount} page(s) of data, retrieving ${children.length} children`);

Return specific array from object collection

So I get some data into my socket
The code in Client is :
useEffect(() => {
const socket = io("http://localhost:5000/api/socket");
socket.on("newThought", (thought) => {
console.log(thought);
});
}, []);
And then the code in my server is
connection.once("open", () => {
console.log("MongoDB database connected");
console.log("Setting change streams");
const thoughtChangeStream = connection.collection("phonenumbers").watch();
thoughtChangeStream.on("change", (change) => {
io.of("/api/socket").emit("newThought", change);
});
});
When something in my "phonenumbers" collection gets changed I get in return the whole collection . How would I be able to only get the array that got changed from the object in collection?
So for example if in the collection the only service that changed is the one with id "607deefd13c4ebcbcfa0900a" that should be the only one returned and not the whole collection object.
The fullDocument parameter to the options (second) argument to the watch method can be used to get a delta describing the changes to the document for update operations:
const thoughtChangeStream = connection.collection("phonenumbers").watch([], {
fullDocument: 'updateLookup'
});
thoughtChangeStream.on("change", (change) => {
io.of("/api/socket").emit("newThought", change);
});
This will then return a response document like this where updateDescription contains the fields that were modified by the update:
{
_id: {
_data: '8260931772000000012B022C0100296E5A1004ABFC09CB5798444C8126B1DBABB9859946645F696400646082EA7F05B619F0D586DA440004'
},
operationType: 'update',
clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1620252530 },
ns: { db: 'yourDatabase', coll: 'yourCollection' },
documentKey: { _id: 6082ea7f05b619f0d586da44 },
updateDescription: {
updatedFields: { updatedField: 'newValue' },
removedFields: []
}
}
Note: This will only work for update operations and will not work for replace, delete, insert, etc.
See also:
http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html.
https://docs.mongodb.com/manual/reference/change-events/

Update an object in a array - reactjs

I'm calling an api request to update (edit) values in my array of objects. But the issue is i can only see the updated value after page refresh. To see the updated (edited) values instantly i'm trying to insert the updated values to my object locally (Only if the request is executed successfully). My approach is as follows
Action
.then(res => {
if (res.data.success === true) {
const updatedBillingPlan = {
"billingId":data.billingId,
"name": data.name,
"rateKWh": data.rateKWh,
"rateMin": data.rateMin,
"rateTransaction": data.rateTransaction
}
dispatch(updateBillingPlansSuccess(updatedBillingPlan));
} else {
alert("error");
}
})
Reducer
case UPDATE_BILLING_PLANS_SUCCESS:
let updatedBillingArray = (Here i need to include action.data to billingData )
return update(state, {
billingData: updatedBillingArray,
})
I'm trying to use immutability-helper library.
How would i go about this?
For example when i delete a record, to display the newest array with the deleted record , i'm doing something like this.
case DELETE_MODAL_SUCCESS:
let updatedBillingArray = state.billingData.filter(property => property.billingId !== action.data);
return {
...state,
billingData: updatedBillingArray,
billingDeleteToast: true
}
I want to do the same with update as well.
assuming billingID is unique among the billing plans, you could use that to update the proper element in the array:
case UPDATE_BILLING_PLANS_SUCCESS:
let updatedBillingArray = state.billingData.map(bp => if (bp.billingId === action.data.billingId) return action.data else return bp);
return update(state, {
...state,
billingData: updatedBillingArray,
})

Moment.js is converting Date to Today date using React Big Calendar

I've got a problem where the date is being converted to today's date even though the date is being parse by moment. It looks like Moment is converting the date to today's date. I believe I am just using the script incorrectly. I'm not familiar with Moment. Any help would be appreciated.
export function getEvents (callback) {
request
.get(url)
.end((err, resp) => {
if (!err) {
const events = [];
JSON.parse(resp.text).items.map((event) => {
events.push({
start: moment(event.start.date).toDate()|| moment(event.start.dateTime),
end: moment(event.end.date).toDate() || moment(event.end.dateTime),
title: event.summary,
})
});
callback(events)
}
})
This is an example of the trace where the "start" date from Google Calendar is in a Timeformat.
This is the conversion and a trace of the script
Here is the date in real time on the call:
The issue that fixed this is in the Moment api. Use this :
JSON.parse(resp.text).items.map((event) => {
var start = moment(event.start.dateTime,'YYYY-MM-DD HH:mm').toDate();
var end = moment(event.start.dateTime,'YYYY-MM-DD HH:mm').toDate();
events.push({
start: start,
end: end,
title: event.summary,
})
});
After looking into your resp.text that provided in comments I created the following parse method to parse your response as the way you wanted.
Here response passed to this method is your resp.text which you supplied in comments.
import moment from 'moment'
const parseResponse = (response) => {
const events = []
response.forEach(obj => {
obj.items.forEach(
item => {
events.push({
start: moment(item.start.dateTime),
end: moment(item.end.dateTime),
title: item.summary
})
}
)
})
return events
}
Note: Check the codesandbox.io/s/ywpznzrmv9 pen if you want to look into moment workaround. You can get rid of first forEach block if the resp.text is having only one array of object.|
like:
const parseResponse = (response) => {
const events = []
response[0].items.forEach(
item => {
events.push({
start: moment(item.start.dateTime),
end: moment(item.end.dateTime),
title: item.summary
})
}
)
return events
}
Note: If you stick to using JSON.parse() then change map to forEach. map creates an object, which is a garbage that you won't need in your case.

Resources