how to update redux state using immutability helper? - javascript

I am trying to update my redux crud form.I am using with the help of immutatbility helper.
My reducer is:
case UPDATE_TODO:
console.log("reducer todo",action.toDo,state)
return update(state, { $set: [action.toDo] })
But instead of replacing specific object it replace whole array into one.Where I am doing wrong??
My State is this:
[
{_id: "5b3d2696e099830f249dddfd", title: "hello", description: "hello", reminder: "2018-07-05T01:27", date: "1530734230965", …}
{_id: "5b3d2696e099830f249dddxe", title: "hello", description: "hello", reminder: "2018-07-05T01:27", date: "1530734230965", …}
]
and after updating it should be like this:
[
{_id: "5b3d2696e099830f249dddfd", title: "hello", description: "hello", reminder: "2018-07-05T01:27", date: "1530734230965", …}
{_id: "5b3d2696e099830f249dddxe", title: "hello1", description: "hello", reminder: "2018-07-05T01:27", date: "1530734230965", …}
]
but instead its giving result this:
[
{_id: "5b3d2696e099830f249dddxe", title: "hello1", description: "hello", reminder: "2018-07-05T01:27", date: "1530734230965", …}
]

Your code is not targeting the specific todo you want to update in state, hence it replaces the whole state. There are two ways you can achieve this:
1) Find the index of the todo item you want to update using Array.findIndex method before using $set method.
const todoIndex = state.findIndex(todo => todo.id === action.toDo.id)
const newState = update(state, {[todoIndex]: {$set: action.todDo }})
2) Find the index of the todo item you want as (1) above and then use $splice method.
const todoIndex = state.findIndex(todo => todo.id === action.toDo.id)
const newState = update(state, {$splice: [[todoIndex,1,action.todDo]]})

Related

How to make aggregations in TypeOrm

I need to get lists of interests with data aboit it and count customers that subscibed to interest
I have many-to-one relation between interests and customers.
This is what only might to do
const list = await this.interestRepository
.createQueryBuilder('interest')
.leftJoinAndSelect("interest.translations", "translations")
.leftJoinAndSelect("interest.customers", "customers")
.getMany()
And this is the result:
list [
Interest {
id: 'YJnu_8bpzMSrFiztDl1zH',
creationDate: 2022-12-19T16:49:55.090Z,
countryCode: 'LTU',
rank: 2,
status: 'active',
translations: [ [InterestTranslation], [InterestTranslation] ],
customers: [ [Customer] ]
},
Interest {
id: '-h1UPpOSkw4TqcTc7F5Ej',
creationDate: 2022-12-19T16:57:47.718Z,
countryCode: 'USA',
rank: 1,
status: 'active',
translations: [ [InterestTranslation], [InterestTranslation] ],
customers: []
}
]
But i want to count the customers, not get all of them, because it could be thouthands of them and it will affect on request duration.
if I understood correctly you need put this
.loadRelationCountAndMap('interest.customers', 'interest.customers')
instead of
.leftJoinAndSelect("interest.customers", "customers")

Best way to return a nested object the matches a property requested

I'm trying to create a new object that only contains the a product array with the seller I req. I have an order object that has a product array. I'd like to return a specific seller. I tried:
const newOrders = orders.map((element) => {
return {
...element,
product: element.product.filter(
(seller) => seller === req.currentUser!.id
),
};
});
does mongoose have a preferred method for doing what I bring to achieve? I've read through the find queries but none of the methods seem useful to this use case.
orders: [
{
userId: "638795ad742ef7a17e258693",
status: "pending",
shippingInfo: {
line1: "599 East Liberty Street",
line2: null,
city: "Toronto",
country: "CA",
postal_code: "M7K 8P3",
state: "MT"
},
product: [
{
title: "new image",
description: "a log description",
seller: "6369589f375b5196f62e3675",
__v: 1,
id: "63737e4b0adf387c5e863d33"
},
{
title: "Mekks",
description: "Ple",
seller: "6369589f375b5196f62e3675",
__v: 1,
id: "6376706808cf1adafd5af32f"
},
{
title: "Meeks Prodyuct",
description: "long description",
seller: "63868795a6196afbc3677cfe",
__v: 1,
id: "63868812a6196afbc3677d06"
}
],
version: 1,
id: "6388138170892249e01bdcba"
}
],
Im sure this can be improved, doesn't feel that its the best way possible but it gets the result. Like the previous answer you have to find first the order the seller is in then find the products than filter the seller by the id. I'm using typescript and there's a bug https://github.com/microsoft/TypeScript/issues/50769 so you have to use the bracket notation.
const orders = await Order.find({
"product.seller": req.currentUser!.id,
});
const allOrders = orders[0].product;
const sellerOrders = allOrders.filter((obj) => {
return obj["seller"] === req.currentUser!.id;
});

How to achive below result using provided array?

I'm builing notifications component where I want to sort notification by dates and display them.
Can someone give me a hint to achieve that?
How can I do it over a following array ?
Array :
notificationRows: Array(25)
0:
notificationRow:
fee: "20 TEST"
id: "1.11.191431"
info: "[userlink=demo3], send 0.00021 TEST to ,[userlink=demo1]"
key: "1.11.191431"
time: "2022-05-17 16:54:21"
type: "transfer"
[[Prototype]]: Object
unread: false
[[Prototype]]: Object
1:
notificationRow:
fee: "20 TEST"
id: "1.11.191430"
info: "[userlink=demo3], send 0.012 TEST to ,[userlink=demo1]"
key: "1.11.191430"
time: "2022-05-17 14:52:39"
type: "transfer"
[[Prototype]]: Object
unread: false
[[Prototype]]: Object
2:
notificationRow:
fee: "20 TEST"
id: "1.11.191427"
info: "[userlink=demo3], send 0.0021 TEST to ,[userlink=demo1]"
key: "1.11.191427"
time: "2022-05-17 14:34:15"
type: "transfer"
[[Prototype]]: Object
unread: false
My expected result is :
Sort the array with descending dates, use sort() and getTime() for dates.
And in the return of your component, use map() to return an HTML element for each array item.
example using a function component
function App(props) {
const {arr} = props;
arr.sort((a, b) => {
const date1 = new Date(a.notificationRow.time);
const date2 = new Date(b.notificationRow.time);
return date2.getTime() - date1.getTime();
})
return (
{
arr.map((elem, i) => (<div key={i}>whatever you want here</div>))
}
)
}
you can use array.sort() function in JavaScript to sort by date.
const notificationRows = [
{
notificationRow: {
fee: "20 TEST",
id: "1.11.191431",
info: "[userlink=demo3], send 0.00021 TEST to ,[userlink=demo1]",
key: "1.11.191431",
time: "2022-05-17 16:54:21",
type: "transfer",
unread: false
}
},
{
notificationRow: {
fee: "20 TEST",
id: "1.11.191430",
info: "[userlink=demo3], send 0.012 TEST to ,[userlink=demo1]",
key: "1.11.191430",
time: "2022-05-17 14:52:39",
type: "transfer",
unread: false
}
},
{
notificationRow: {
fee: "20 TEST",
id: "1.11.191427",
info: "[userlink=demo3], send 0.0021 TEST to ,[userlink=demo1]",
key: "1.11.191427",
time: "2022-05-17 14:34:15",
type: "transfer",
unread: false
}
}
];
const sorted = constarr.sort(
(a, b) =>
// return sorted date by newest. if want sort by old use Date.parse(a.notificationRow.time) - Date.parse(b.notificationRow.time)
Date.parse(b.notificationRow.time) - Date.parse(a.notificationRow.time)
);
console.log("sorted", sorted);
Since you use react and you may use this sorted by time notifications to display, set this sorted arrays to the react state and render accordingly.

Add object to nested array

I have initial state as
sites = [
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
];
I'm trying to add a vehicle to a given site when selected from a list which is in a component SiteVehcleSelection and the method that handles the selection is:
handleVehicleSelection = (event) => {
const vehicle = this.props.vehicles.find((v) => v.id === parseInt(event.target.dataset.id, 10));
this.props.handleVehicleSelection(event, this.state.site.id, {...vehicle});
};
which passes it up to parent SiteList method:
handleVehicleSelection = (event, siteId, vehicle) => {
this.props.dispatch(siteActions.handleVehicleSelect(siteId, vehicle), event.target.checked);
}
called from the SiteList class:
export function handleVehicleSelect(siteId, vehicle, cmd){
return (dispatch) => {
debugger;
return fetch(`${BASE_URL}/accounts/site-vehicle-action/${siteId}/${vehicle.id}/${cmd}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: ''
}).then((res) => {
return res.json();
}).then((json) => {
if (json.msg === true) {
dispatch(vehicleSelect(siteId, vehicle));
}
});
}
}
which dispatches to this:
export function vehicleSelect(siteId, vehicle){
return {type: actionTypes.ADD_VEHICLE_TO_SITE, siteId, vehicle};
}
and my reducer is:
case actionTypes.ADD_VEHICLE_TO_SITE:
debugger;
const siteIndex = state.findIndex((site) => site.id === action.siteId);
console.log(state);
const newState = [...state.slice(0, siteIndex), {...state[siteIndex], vehicles: [...state[siteIndex].vehicles, action.vehicle],}, ...state.slice(siteIndex +1)];
console.log(newState);
return newState;
when I log before and after the changes have taken place, the vehicle has been added to the correct site but it does not show/refresh in the view here is the logging of the state before and after.
Before change :
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(0)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
After change:
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(1)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
can see the first one had the vehicle added correctly and this is the new state but nothing happens on return as if sitesList does not refresh.
Hope this edit helps explain more.
I think below code will shed some light. I assume you have the corresponding indexes.
let state = [
{ id: "id1", name: "name1", items: [{},{}] },
{ id: "id2", name: "name2", items: [{},{}] },
]
function reducer() {
switch("Action") {
case ADD_SITE: { // add new element to state
return [
...state,
payload.site,
]
}
case ADD_SITE_AT_INDEX: { // add new element to state at index: idx
return [
...state.slice(0, idx),
payload.newSite,
...state.slice(idx)
]
}
case ADD_ITEM: { // add new item to site with index: idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items,
payload.newItem
],
},
...state.slice(idx+1)
]
}
case ADD_ITEM_AT_INDEX: { // add new item to site with index: idx, at item index: item_idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items.slice(0, item_idx),
payload.newItem,
...state[idx].items.slice(item_idx),
],
},
...state.slice(idx+1)
]
}
}
}
let say this is your structure of state with keys (i am giving random name to the keys so i can explain)
{
data:[{id:"some_id",name:"some_name",items1:[{},{}]}]
}
//suppose this is your reducer code.
case ADD_ITEM:
return{
...state,
data: state.data.map(val=>{
if(val.Id===action.payload.Id){
return{
...val,
items1:[...val.items1,action.payload.data]
}
}
return{...val}
})
}
here you'll be sending Id and data from the action like:
{type:"ADD_ITEM",payload:{Id,data}}
where id will be the id of first level object which's array needs to be updated,
and data will be the data you want to add into the array..
If you just want to add an object to an array with a given structure, you can do this:
Structure
[
{ id, name, items1[{object},{object}]
]
copy an existing state and then add a new object to the end of the array.
return {
...state,
items1: state.items1.concat(action.newObject),
};
or with ES6 spread
return {
...state,
items1: [...state.items1, action.newObject],
};
// state: { id, name, items1: [{object},{object},{newObject}] }

Setstate of specific object in array

I have a cats array that I pull from an api
i map over these and render them on a page
each one gets rendered with a like button, when I hit like I want it to like it and when i hit like again, it should unlike it
my initialState is :
state = {
cats: []
};
then once i call the api state looks like this:
cats: [
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
]
I have a like cat method whereby I find the cat that I liked like this:
var cat = this.state.cats.find(c => c.id[0] === cat.id[0])
considering I have all this information, how do I call setState for that specific cat to change the liked from false to true?
I was thinking something like this:
this.setState(prevState => ({ cats: {
cat: {
...prevState.cat,
liked: !prevState.cat.liked
}
}}))
but it does not know what liked is of undefined
any ideas?
One problem with your approach is that there's no prevState.cat.
Assuming the (un)liked cat is stored in cat:
this.setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
Demo:
var state;
function setState(a) {
state = Object.assign(state, a(state));
}
state = {
cats: [
{url: [0], id: [1], source_url: [0], liked: false},
{url: [0], id: [2], source_url: [0], liked: false}
]
};
var cat = state.cats[1];
setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
console.log(state.cats[1].liked);

Categories