Update array value inside observable typescript - arrays

How do you write a function that takes an Observable<T[]> and updates the value inside the observable.
I tried with the below approach but i felt i am not doing the update on proper way.
I want to update the values of the observable with the row object.
import { of } from 'rxjs';
import { map } from 'rxjs/operators';
let gridData = [
{
id: '1',
firstName: 'Sophie',
lastName: 'Beckham',
gender: 'F',
jobTitle: 'Senior Associate-L1',
jobArea: 'London',
monthlyWage: '$100000',
},
{
id: '2',
firstName: 'Sophie',
lastName: 'Beckham',
gender: 'F',
jobTitle: 'Senior Associate-L2',
jobArea: 'London',
monthlyWage: '$100000',
},
{
id: '3',
firstName: 'Chloe',
lastName: 'Bryson',
gender: 'F',
jobTitle: 'Senior Associate-L3',
jobArea: 'London',
monthlyWage: '$100000',
},
];
let gridData$ = of(gridData);
let row = {
id: '2',
firstName: 'TAN',
lastName: 'Ara',
gender: 'M',
jobTitle: 'Senior Associate',
jobArea: 'ATL',
monthlyWage: '$130000',
}
gridData$.subscribe((val: any) => {
val.forEach((list: any) => {
if(list['id'] === row['id']){
for (const property in row) {
list[property] = row[property];
}
}
})
})
gridData$.subscribe((v) => console.log(v));
In my approach i have used for loop and based on the object id updating the value. If there is a better approach, kindly share your approach.
StackBlitz : https://stackblitz.com/edit/rxjs-gdgkyk?devtoolsheight=60

Observables are stateless and its recommended that emitted values are immutable.
But you can return a new observable that returns what you want by using the map operator (on all values).
Note that I've used from instead of of operator.
import { from } from 'rxjs';
import { map } from 'rxjs/operators';
...
let gridData$ = from(gridData);
...
gridData$ = gridData$.pipe(
map((val: any) => {
if(val['id'] === row['id']){
for (const property in row) {
val[property] = row[property];
}
}
return val;
})
);
gridData$.subscribe((v) => console.log(v));
Now each time you call gridData$ it will execute the new observable.
https://stackblitz.com/edit/rxjs-6mh6iu

Related

need to implement Undo function using react hooks

need to implement undo function in a state array data. i remove the data by id using filter method but i need to restore the data after click on undo button. how to implement in React Hooks?
const [dataSource, setDatasource] = useState([
{
id: 1,
name: 'john',
gender: 'm'
},
{
id: 2,
name: 'mary',
gender: 'f'
},
{
id: 3,
name: 'loki',
gender: 'M'
},
{
id: 4,
name: 'Thor',
gender: 'M'
},
]);
You will have to keep track of all the actions that the user performs, and calculate the end result from that. When a user clicks "undo", you can just remove the latest action.
const actions = [
{
operation: "create",
name: "Some name",
index: 1
},
{
operation: "delete",
index: 1
}
];
const state = (() => {
let appliedState = [];
for (const action of actions) {
if (action.operation === "create") {
appliedState .push(action.name);
} else if (action.operation === "delete") {
appliedState = appliedState .filter(currentState => currentState.index !== action.index);
}
}
return appliedState ;
})();
You can see a working react example here.

TypeError: Cannot assign to read only property 'quantity' of object '#<Object>'

I'm trying to modify the field quantity of obj meal in the array orderedList.
This is the error:
TypeError: Cannot assign to read only property 'quantity' of object '#'
How I can change this state.
Please help.Please help.Please help.Please help.Please help.Please help.Please help.
Please help.Please help.Please help.Please help.Please help.Please help.Please help.
import {createSlice} from "#reduxjs/toolkit";
const menuList = [
{
id: 'm1',
name: 'Sushi',
description: 'Finest fish and veggies',
price: 22.99,
quantity: 0
},
{
id: 'm2',
name: 'Schnitzel',
description: 'A german specialty!',
price: 16.5,
quantity: 0
},
{
id: 'm3',
name: 'Barbecue Burger',
description: 'American, raw, meaty',
price: 12.99,
quantity: 0
},
{
id: 'm4',
name: 'Green Bowl',
description: 'Healthy...and green...',
price: 18.99,
quantity: 0
},
];
const initialStateMeals = {
menuList,
orderedList: [],
numberOfOrderedMeals: 0,
showCart: false,
totalAmount: 0
}
const counterSlice = createSlice({
name: 'meals',
initialState: initialStateMeals,
reducers: {
add(state, action) {
state.numberOfOrderedMeals += +action.payload.input;
let totalAmountTemp = state.totalAmount + action.payload.meal.price * action.payload.input;
state.totalAmount = +totalAmountTemp.toFixed(2);
let mealIsInList = state.orderedList.filter(meal => meal.id === action.payload.meal.id).length > 0;
if (!mealIsInList) {
state.orderedList.push(action.payload.meal);
state.orderedList.filter(meal => meal.id === action.payload.meal.id)[0].quantity = 1;
//on this line I have this error
//TypeError: Cannot assign to read only property 'quantity' of object '#<Object>'
} else {
}
},
remove(state, action) {
state.numberOfOrderedMeals -= +action.payload.input;
}
,
closeCart(state) {
state.showCart = false;
}
,
showCart(state) {
state.showCart = true;
}
}
});
export default counterSlice.reducer;
export const mealsAction = counterSlice.actions;
My guess here is that redux-toolkit is protecting you from mutating the action's payload object as this mutation could leak out into any other reducer listening for the same action.
Instead of pushing into the array, and then iterating it again just to find the element you pushed, which will be the last element in the array, BTW, you could just create a new object with the properties you want and push this into the array instead.
if (!mealIsInList) {
state.orderedList.push({
...action.payload.meal,
quantity: 1,
});
}

Redux/Reducer correct way to spread previous unchanged nested state

In redux or reducer pattern, for nested objects do you need to deeply spread every level of the previous state or is that only necessary on the object that you are updating?
For example if you have some state that looks like this:
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
And then a reducer that updates the address:
case UPDATE_ADDRESS:
return {
...state,
address: {
...state.address,
city: 'Baltimore'
}
};
I am just confused wether this is right or not in terms of immutability. Since the ...state will only return the reference to the old name object and not an entire new object. Would I also need to spread ...state.name even though it is not being changed? I know there are a lot of questions about nested state and reducers and they all say that you need to return a new copy but they all show just ...state for the previous state and I haven't been able to find one that describes this and why you don't need to spread all the old nested state if that's the case. Thanks in advance.
Your code is correct. In short, in redux the return value from reducer will become the next value for the store. Only requirement is that we cannot change the state directly (unless you use something like Immutable.js)
What ...state does is it spread the object out. So your return value will become something like this
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
},
address: {
streetName: '123 fake st',
city: "New York",
city: 'Baltimore'
}
}
For object that has duplicate key, Javascript will use the one that is defined near the end of the object. For your instance it will reduce to
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: 'Baltimore'
}
}
You can read more about this here in the Spread in object literals section
Your understanding of immutability is correct here. With the object above, you will need to spread values as you have done.
As you develop more in redux, and scale up the size of your applications, the design of your redux store will become more important. Creating several reducers and then stitching these together with combineReducers will help reduce complexity.
Here is an example of how you would do it for your case above. Note this is overkill for your use case. I would only split the reducers up if the state gets much more complex:
const nameReducer = (state = initialState, action) => {
if (action.type === 'UPDATE_FIRST_NAME') {
return {
...state,
first: action.value
}
} else if (action.type === 'UPDATE_LAST_NAME'){
return {
...state,
last: action.value
}
}
return state
}
const addressReducer = (state = initialState, action) => {
if (action.type === 'UPDATE_CITY') {
return {
...state,
city: action.value
}
}
return state
}
export default combineReducers({
name: nameReducer,
counter: addressReducer
})
Once you get to grips with redux checkout Redux Tookit: https://redux-toolkit.js.org/tutorials/basic-tutorial. It helps get rid of some of the boilerplate.
EDIT:
For you example above you will need to spread name also to be fully immutable. Consider these examples:
Not immutable
const person = {
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
const copy = {
...person,
address: {
...person.address,
streetName: 'myHouse'
}
}
copy.name.first = 'Tom';
console.log({ copy, person });
/* result:
{ copy:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: 'myHouse', city: 'New York' } },
person:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: '123 fake st', city: 'New York' } } }
*/
Immutable - changing the copy does not affect the person object.
const person = {
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
const copy = {
name: {
...person.name
},
address: {
...person.address,
streetName: 'myHouse'
}
}
copy.name.first = 'Tom';
console.log({ copy, person });
/* result
{ copy:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: 'myHouse', city: 'New York' } },
person:
{ name: { first: 'Bob', last: 'Smith' },
address: { streetName: '123 fake st', city: 'New York' } } }
*/

How do you bind a dynamic API response to a React UI?

I am consuming an API which has a few 'required' fields and fields which are not required. As such, depending on the result of the response, the shape can change dramatically with fields being excluded in the response.
An example Schema:
{
name: string; // required
surname: string; // required
addresses: []
telephoneNumbers: []
gender: string; // required
age: number;
}
So a request could return multiple formats:
Example: 'api.com/users/123'
{
name: 'John',
surname: 'Doe',
addresses: [ ....]
telephoneNumbers: [....]
gender: 'male',
age: 20
}
Example: 'api.com/users/456
{
name: 'Betty',
surname: 'Boo',
gender: 'female',
}
When I bind the response to my UI (react) is there a way to check each field exists?
Do I have to check if each field exists and has a value in the response before rendering it? As in:
const DisplayResults = () => {
const { data } = await api.getUser('123');
return (
<div>
<div>{data.name}</div>
<div>{data.surname}</div>
{data.age && <div>{data.age}</div>}
{data.gender && <div>{data.age}</div>}
{data.addresses && <div>{data.addresses.map((address) => ...)}</div>}
</div>
)
}
Bear in mind any nested objects may have 'required' and non required fields which means I either need to sanitize the response to match the response to how I want or do a check on each field to make sure it's not undefined.
If I bind to a grid component, it'll throw errors for undefined fields which may or may not be there.
Any ideas?
Seems like depending on the type of data you need a different way to display the data.
You could create a piece of state which is an array of Objects that hold info on how to handle each key.
import React from 'react'
import ReactDOM from 'react-dom'
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user_rows: [
{
key_name: "name",
data_function: (data = "") => data,
display:(data) => data.length > 0 ? true : false
},
{
key_name: "addresses",
data_function:(data = []) => data.map((a,idx) => {
return( <div>{a}</div>)
}),
display:(data) => data.length > 0 ? true : false
},
{
key_name: "gender",
data_function:(data = "") => data,
display:(data) => data.length > 0 ? true : false
}
]
}
}
render() {
let data = {
name: 'John',
surname: 'Doe',
addresses: ["adress 1"],
telephoneNumbers: ["000 000 000"],
gender: '',
age: 20
}
return (
<div>
{this.state.user_rows.map((ur,idx) => (
ur.display && <div key={idx}>{ur.data_function(data[ur.key_name])}</div>
))}
</div>
);
}
}
ReactDOM.render(
<User/>,
document.getElementById('container')
);

How to filter array of object by value React Redux lodash

I have some stupid problem - I can't understand where is my mistake
I have reducer which looks:
import startsWith from "lodash";
const arr = [{id: "55ae8f78-4563-4445-b75f-267fe02c00a8", name: "Licensed Car", price: "775.00", discount: "68%"},
{id: "7d30c1c1-dd02-49cd-8b02-d8c3ec301d70", name: "Handmade Mouse", price: "349.00", discount: "71%"},
{id: "5f383f37-d9b1-4494-ac2f-d1ecb72a78f3", name: "Licensed Shirt", price: "425.00", discount: "69%"},
{id: "5ae0de55-f7e6-4d59-bebd-9b215d327efb", name: "Intelligent Chips", price: "767.00", discount: "62%"},
{id: "231a1dc1-1cf8-4b5e-bc3a-9b23932492e8", name: "Refined Cheese", price: "367.00", discount: "80%"}]
const INITIAL_STATE = {
time: undefined,
products: arr
};
export default (state = INITIAL_STATE, { type, payload }) => {
switch (type) {
case "FILTER_BY_VALUE": {
return {
...state,
products: [
...state.products.filter(product =>
startsWith(product.name.toLowerCase(), payload.toLowerCase())
)
]
};
}
default:
return state;
}
};
I also use lodash method "startsWith"
_.startsWith([string=''], [target], [position=0])
_.startsWith('abc', 'a');
// => true
_.startsWith('abc', 'b');
// => false
_.startsWith('abc', 'b', 1);
// => true
With payload is all okay, I want to filter an array and leave only those elements whose value name is the same or partly the same with the payload.
If someone knows another way to fix my problem without lodash - please write your ideas.
Thanks for your answers!
Well, vanilla JS has the same startsWith method as a lodash lib.
case "FILTER_BY_VALUE": {
return {
...state,
products: [
...state.products.filter(product => product.name.toLowerCase().startsWith(payload.toLowerCase()))
]
};
}

Resources