I'm deleting invitations by passing their IDs to the back end, which works. However, my reducer is not working properly to re-render the new, filtered array of invitations. When I refresh the page, the deleted invitation is gone. What am I doing wrong?
The action:
export function deleteInvitation(id) {
const user = JSON.parse(localStorage.getItem('user'));
console.log('now deleting id ', id);
return function(dispatch) {
axios
.delete(`${ROOT_URL}/invitation/`, {
headers: { authorization: user.token },
params: { id: id }
})
.then(response => {
console.log(id);
dispatch({
type: DELETE_INVITATION,
id
});
});
};
}
The reducer:
export default function(state = {}, action) {
switch (action.type) {
case INVITATION_SUCCESS:
return { ...state, invited: true, error: {} };
case INVITATION_FAILURE:
return { ...state, invited: false, error: { invited: action.payload } };
case FETCH_INVITATIONS:
return { ...state, invitations: action.payload };
case DELETE_INVITATION:
return {
...state,
invitations: state.invitations.filter(_id => _id !== action.id)
};
default:
return state;
}
}
I'm making a guess about the structure of the invitations array...
In the reducer, the filter function appears to be incorrect. The action is passing an id property, which I'm guessing is a property of an invitation object. But the filter function is filtering objects from state.invitations where the object is the id. That is, the reducer is doing something like this:
const action = {id: 0}
const invitation = [{
_id: 0,
name: 'Name 0',
location: 'Location 0'
},
{
_id: 1,
name: 'Name 1',
location: 'Location 1'
},
{
_id: 2,
name: 'Name 2',
location: 'Location 2'
}
];
console.log(invitation.filter(_id => _id !== action.id));
which will return the full original array because the filter function is checking for the inequality of action.id (a number) to an invitation (an object). Basically:
{
_id: 0,
name: 'Name 0', !=== number
location: 'Location 0'
}
will return true for any num and/or any invitation object, so the filter function will return every item in state.invitations.
To correct this, check the invitation._id against the action.id, like this:
const action = {id: 0}
const invitation = [{
_id: 0,
name: 'Name 0',
location: 'Location 0'
},
{
_id: 1,
name: 'Name 1',
location: 'Location 1'
},
{
_id: 2,
name: 'Name 2',
location: 'Location 2'
}
];
console.log(invitation.filter(invitation => invitation._id !== action.id));
Related
I need help with this code.
I working with "#angular/cli": "~12.0.5".
The createArray method receives an object and I want to transform the object to an array, but I have an error in 'userObj [key]'. I get the object (userObj) from Firebase through an http request and I can't change its structure.
This is the error message. -> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. No index signature with a parameter of type 'string' was found on type '{}'
Thanks!
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
export class UserModel{
id: string;
name: string;
}
private createArray(userObj){ /*(userObj: object)*/
const users: UserModel[] = [];
if (userObj == null) { return []; }
Object.keys(userObj).forEach(key => {
const user: UserModel = userObj[key];
user.id = key;
users.push(user);
});
return users;
}
Try this.
private createArray(userObj){ /*(userObj: object)*/
const users: UserModel[] = [];
if (userObj == null) { return []; }
for (const [key, object] of Object.entries(userObj)) {
const user: UserModel = object as UserModel;
user.id = key;
users.push(user);
}
return users;
}
yow broh.
use Object.values instead
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
export class UserModel{
id: string;
name: string;
}
private createArray(userObj): UserModel[] {
return Object.values(userObj)
}
const userObj = {
'SJKLDFAD903':{
id: '',
name: 'User 1'
},
'PLMKL-BAD89':{
id: '',
name: 'User 2'
},
'JHK34R-R903':{
id: '',
name: 'User 3'
}
}
function createArray(userObj) {
return Object.values(userObj)
}
console.log(createArray(userObj))
I am modifying an array of object using pure function but facing some challenge to modify userDetails object.
My requirement is instead 1, I need to show true inside permissions object. Please check my code and suggest. I know, I am doing some small mistake but still trying to identify.
I dont want main data source should get affected. Please suggest if you have any other good option
let data = [
{ id: 1,
title: 'admin1',
permissions: {
userDetails: { activeUser: 1, team: null},
salaryList: { abc: 1, def: 2, asdf: 0, poi: 1}
}
},
{ id: 1,
title: 'admin1',
permissions: {
userDetails: { activeUser: 1, team: null},
salaryList: { abc: 0, def: 1, asdf: null, poi: 0,wew: 1, aaa: 1}
}
},
]
let modifiedObjs = data.map( record => {
return {
id: record.id,
title: record.title + " Edited Object",
permissions: handlePermission(record.permissions)
}
})
function handlePermission(permissions){
Object.keys(permissions).forEach((key)=> {
Object.keys(permissions[key]).forEach((obj) => {
if(permissions[key][obj]===null){
delete permissions[key][obj]
}else{
const value = permissions[key][obj];
const finalV = value === 0? false : value ===1? true : value === 2 ? null : value;
permissions[key][obj] = finalV
}
})
})
return permissions
}
console.log(data, "main data")
console.log(modifiedObjs, "modified data")
The main issue with your code is that permissions is a reference inside of handlePermission, meaning that when you delete from it, it will also the key will also be removed from your original object (as they're sharing the same reference). One way to fix this is to instead build a new object. Whenever you find a key you want to keep, you can add it to the new object. Then, instead of deleting, you can simply not add the key/value to the object.
See example below:
const data = [{ id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 1, def: 2, asdf: 0, poi: 1 } } }, { id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 0, def: 1, asdf: null, poi: 0, wew: 1, aaa: 1 } } }, ];
let modifiedObjs = data.map( record => {
return {
id: record.id,
title: record.title + " Edited Object",
permissions: handlePermission(record.permissions)
}
});
function handlePermission(permissions){
const newPermissions = {};
Object.keys(permissions).forEach((key)=> {
newPermissions[key] = {}; // set object for key
Object.keys(permissions[key]).forEach((obj) => {
if(permissions[key][obj]!==null){
const value = permissions[key][obj];
const finalV = value === 0? false : value ===1? true : value === 2 ? null : value;
newPermissions[key][obj] = finalV;
}
});
});
return newPermissions;
}
console.log(data, "main data")
console.log(modifiedObjs, "modified data")
I would instead approach it like so, which uses Object.entries() to get a [key, value] pair array of entries from your object, along with Object.fromEntries() to reconstruct the object after its values have been mapped/filtered:
const data = [{ id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 1, def: 2, asdf: 0, poi: 1 } } }, { id: 1, title: 'admin1', permissions: { userDetails: { activeUser: 1, team: null }, salaryList: { abc: 0, def: 1, asdf: null, poi: 0, wew: 1, aaa: 1 } } }, ];
const modifiedObjs = data.map(({id, title, permissions}) => ({
id,
title: title + " Edited Object",
permissions: Object.fromEntries(Object.entries(permissions).map(([key, obj]) => [
key,
Object.fromEntries(Object.entries(obj).filter(([,v]) => v !== null)
.map(([k, v]) => [k, v > 1 ? null : !!v]))
]))
}));
console.log(data, "main data");
console.log(modifiedObjs, "modified data");
I have a requirement that from Axios call I'll be getting a list of objects which need to be updated in the redux store.
For example, consider I have an initial state like this.
const initialState = {
someData: [
{
id: 1,
data: 'some data',
...
chosen: true,
user:'Joe'
},
{
id: 2,
data: 'some data',
...
chosen: false,
user:null
},
{
id: 3,
data: 'some data',
...
chosen: false,
user:null
},
{
id: 4,
data: 'some data',
...
chosen: true,
user:'Jacob'
},
{
id: 5,
data: 'some data',
...
chosen: false,
user:null
}
]
}
and from Axios call, I'll get a list of objects which need to be updated in the store.
sample data of a received list of objects :
[
{
id: 2,
data: 'some data',
...
chosen: true,
user:'Jake'
},
{
id: 3,
data: 'some data',
...
chosen: true,
user:'Logan'
},
{
id: 5,
data: 'some data',
...
chosen: true,
user:'Felix'
}
]
I want to update the chosen and user field in the initial state with the array of object's chosen and user, which I received in the Axios call. I was thinking of a logic where I can compare the received object's id with the state's object id. If they're equal I'll update the chosen and user (using the spread operator) if not I'll just return the state's object. I'm not sure how to map through the state or the received list of objects and compare the ids. Please, someone help me with this logic.
return {
...state,
someData: state.someData.map(item => {
const replaceWith = updatedData.find(
updatedItem => item.id === updatedItem.id
);
return replaceWith
? { ...item, chosen: replaceWith.chosen, user: replaceWith.user }
: item;
})
};
needing some help... how do I update the state to reflect a new schedule being added to a specific child (by id)?
I currently have a form that provides a new set of data that looks like this (with values from the form in the empty strings):
{
date: '',
parent: '',
activity: ''
}
I've created this function below, and I'm passing it the id of the child, and the new schedule which looks like the one above... I'm stuck on this one:
addSched = (id, schedule) => {
const newSched = this.state.children.map(child => {
if (child.id !== id) return child;
return {
...child,
schedules: schedule
};
});
this.setState({ children: newSched });
};
My current state looks like this:
state = {
children: [
{
id: 1,
firstName: 'Bella',
lastName: 'Laupama',
schedules: [
{
id: 1,
date: '25 December 2018',
parent: 'Chris',
activity: 'Christmas'
},
{
id: 2,
date: '31 December 2018',
parent: 'Laura',
activity: 'New Years Eve'
}
]
},
{
id: 2,
firstName: 'Cara',
lastName: 'Malane',
schedules: [
{
id: 1,
date: '25 December 2018',
parent: 'Chris',
activity: 'Christmas'
} ...etc
And the component that has the form has the following:
export default class AddSched extends React.Component {
state = {
date: '',
parent: '',
activity: ''
}
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
})
}
submitHandler = e => {
e.preventDefault()
this.props.addSched(this.props.id, this.state)
console.log('SUBMITTED:', this.state)
this.setState({
date: '',
parent: '',
activity: ''
})
}
You can use the array spread operator to concatenate the existing array plus the new schedule:
schedules: [...child.schedules, schedule]
Here's the complete function with the change:
ddSched = (id, schedule) => {
const newSched = this.state.children.map(child => {
if (child.id !== id) return child;
return {
...child,
schedules: [...child.schedules, schedule]
};
});
this.setState({ children: newSched });
};
I'm a learner developer, and I'm build a app with a tree menu(react + redux + sagas), but I'm getting some errors of Mutation State, I saw what best practices is stay de state flat as possible, but I didn't finded one menu tree what work with a flat state, so my data is look this:
menuTree: [{
id: 'id-root',
name: 'root',
toggled: true,
children: [
{
id: 'id-parent1',
name: 'parent1',
toggled: true,
children: [
{
id: '123',
name: 'parent1_child1'
},
{
id: '234',
name: 'parent1_child2'
}
]
},
{
id: 'id-loading-parent',
name: 'loading parent',
loading: true,
children: []
},
{
id: 'id-parent2',
name: 'parent2',
toggled: true,
children: [
{
id: 'parent2_children1',
name: 'nested parent2',
children: [
{
id: '345',
name: 'parent2 child 1 nested child 1'
},
{
id: '456',
name: 'parent2 child 1 nested child 2'
}
]
}
]
}
]
}],
And my redux action:
case types.SOLUTION__MENUCURSOR__SET:
// console.log('action payload', action.payload);
// console.log('state', state);
const cursor = action.payload.cursor;
// console.log('set menu cursor action', cursor);
return {
...state,
menuTree: state.menuTree.map(
function buscaIdMenuTree(currentValue, index, arr){
if(currentValue.id){
if(currentValue.id.includes(cursor.id)){
currentValue.toggled = action.payload.toggled;
return arr;
}else{
if(currentValue.children)
{
currentValue.children.forEach(function(currentValue, index, arr){
return buscaIdMenuTree(currentValue, index, arr);
});
}
}
return arr;
}
}
)[0]
};
The code works but I get Mutation State Error, so someone can help me to fix it ?
You can rebuild your menu as a plain list:
let menuTree = [{
id: 'id-root',
name: 'root',
toggled: true,
parent: null
},{
id: 'id-parent1',
name: 'parent1',
toggled: true,
parent: 'id-root'
},{
id: '123',
name: 'parent1_child1',
parent: 'id-parent1'
},{
id: '234',
name: 'parent1_child1',
parent: 'id-parent1'
},
{
id: 'id-loading-parent',
name: 'loading parent',
loading: true,
parent: 'id-root'
},{
id: 'id-parent2',
name: 'parent2',
toggled: true,
parent: 'id-root'
},{
id: 'parent2_children1',
name: 'nested parent2',
parent: 'id-parent2'
},{
id: '345',
name: 'parent2 child 1 nested child 1',
parent: 'parent2_children1'
},
{
id: '456',
name: 'parent2 child 1 nested child 2',
parent: 'parent2_children1'
}]
then if your menu renderer require a tree you can convert the list to a tree so inside the component renderer this.menuTree will be a tree:
const buildTree = (tree, cParent = null) => {
return tree.filter(cNode => cNode.parent == cParent).reduce((curr, next) => {
let cNode = {...next, children: buildTree(tree, next.id)}
delete cNode.parent
return [...curr, cNode]
}, [])
}
function mapStateToProps(state) {
return {
mapTree: builTree(state.mapTree)
};
}
export default connect(mapStateToProps)(YourComponent);
Inside the mutation now you just need to create a list of node that needs to be toggled and then map the state accordingly
case types.SOLUTION__MENUCURSOR__SET:
// console.log('action payload', action.payload);
// console.log('state', state);
const cursor = action.payload.cursor;
// console.log('set menu cursor action', cursor);
const getToggleList = (tree, cursor) => {
let target = tree.find(cNode => cNode.id == cursor.id)
if(target.parent != null){
let parent = tree.find(cNode => cNode.id == target.parent)
return [target.parent, ...getToggleList(tree, parent)]
}else{
return []
}
}
let toggleList = [cursor.id, ...getToggleList(state.menuTree, cursor.id)]
return {
...state,
menuTree: state.menuTree.map(node => ({...node, toggle: toggleList.includes(node.id)}))
};