Why the state hook in react changes all elements of the object? - javascript

I have a list of members and each member has the same set of applications. I created 2 classes to associate each member and application and ended up with a general list of members and applications, which I wrote in the initial state.
Then, using the function, I get the ID of the member that was clicked and I want to change the state of a particular application for it from false to true. But I get a change in the state of the application from false to work for ALL members. Why is this happening and where could there be an error ???
list members
let teamMembers = [
{ 'idMember': 0, 'name': 'John Littel', 'email': 'Delores_Barrows5#hotmail.com' },
{ 'idMember': 1, 'name': 'Tom Hamill', 'email': 'Luigi0#gmail.com' },
{ 'idMember': 2, 'name': 'Ann Quitzonl', 'email': 'AnnQ#hotmail.com' },
{ 'idMember': 3, 'name': 'Frances Schuster', 'email': 'Frances.Schuster#yahoo.com' },
{ 'idMember': 4, 'name': 'Morrison Mohr', 'email': 'Rafael.Hilll64#yahoo.com' }
];
list apps:
let appsFromBase = ['App 0', 'App 1', 'App 2', 'App 3', 'App 4'];
class Application {
constructor(idApp, isSelected, appName) {
this.idApp = idApp;
this.isSelected = isSelected;
this.appName = appName;
}
toggleSelected() { *this method changed apps from false => true*
this.isSelected = !this.isSelected
}
};
const apps = appsFromBase.map((app, i) => new Application(i, false, app));
const membersObject = teamMembers.map((member, i) => new Member(i, member.name, member.email, apps));
State:
const [members, setMembers] = useState(membersObject);
Handler method:
const handleChoosenApp = (app, member_i) => {
let newMembers = { ...members };
let _id = app.idApp;
if (_id !== 'btn-all-app') {
let myUpdatingMember = newMembers[member_i].apps[_id].toggleSelected()
setMembers({ ...newMembers });
}
}
member_i, _id - This is the member's ID and his application, which was clicked from the interface and he needs to change the value from true to false
in the console myUpdatingMember I get the member and application I need, but why does the state change for everyone ???

The selected state is a property of the Application, not the Member.
You create one Application array and pass it to every Member constructor.
Every Member object you create shares the same Application array.

Related

What should the output of a JavaScript Class look like?

I am attempting to use inquirer to collect user input, that is turned into an object, it is then passed into a class constructor. Everything seems to working the way I want it to accept the resulting array of objects after they are passed though the classes come out in a way that is confusing me,
when I console.log the array that contains the objects that are returned by my classes this is what comes out:
[
Manager {
name: 'bob',
id: '24',
email: 'bob.com',
officeNumber: '1'
},
Engineer {
name: 'jack',
id: '347',
email: 'jack.com',
github: 'jackolantern'
},
Intern {
name: 'sally',
id: '987',
email: 'sally.com',
school: 'UCF'
}
]
Here is an example of one of the classes:
class Manager extends Employee {
constructor (employeeObj) {
super (employeeObj);
this.name = employeeObj.name;
this.id = employeeObj.id;
this.email = employeeObj.email;
this.officeNumber = employeeObj.officeNumber;
}
getOfficeNumber () {
return this.officeNumber;
}
getRoll () {
return "Manager";
}
}
module.exports = Manager;
and this is how the objects are passed into the classes:
const prompts = async () => {
let employeeObj = {};
const employee = await inquirer.prompt(addEmployee);
switch(employee.addEmployee){
case 'Manager':
employeeObj = await managerQuestions();
const manager = new Manager(employeeObj);
output.push(manager);
if(employeeObj.addAnother){
return prompts();
} else {
complete();
}
break;
case 'Engineer':
employeeObj = await engineerQuestions();
const engineer = new Engineer(employeeObj)
output.push(engineer)
if(employeeObj.addAnother){
return prompts();
} else {
complete();
}
break;
case 'Intern':
employeeObj = await internQuestions();
const intern = new Intern(employeeObj)
output.push(intern)
if(employeeObj.addAnother){
return prompts();
} else {
complete();
}
break;
default:
console.log('you have reached the default switch statement. thant should not happen. Please try again!');
}
}
what I cant seem to figure out, is why the "roll" for each object (Manager, Engineer, Intern) is being placed outside the corresponding object and not inside it. I was able to add a this.roll = "manager" inside the constructor and as expected it added in a property called roll with a value of "manager" which will work just fine for what I need to do, but how do I get rid of that Manager, Engineer, and Intern that shows up before each object in the output array, or can it be moved inside the object?
thank you for taking the time to read through all this.

Put Nested ReactJS values into GraphQL create mutation

I presently have a page with a dynamically created form. I am having trouble understanding how to manipulate the state and GraphQL query to handle nested queries.
With my present implementation it does not seem to be able to create any new entries. I want to create 1 "target" with several sub "addr" tied to it in one mutation.
This is the state definitions:
state = {
name:'',
addr:[{
mobilepkg:'',
target_url:'',
target_ip: '',
idCars:[]
}],
category:'',
date: '',
location:''
}
Handler for Graph:
handleTarget = async e => {
e.preventDefault()
const { name,
target_url,
target_ip,category,
mobilepkg,date,location } = this.state
let idCars = this.state.idCars
let adras = this.state.addr
await this.props.createTargetMutation({
variables: {
data: {
name,
addr:{
create:
[{
target_url,
target_ip,
mobilepkg,
cars: {
connect: idCars
},
}]
},
date,
location,
category
}
}
})
this.props.history.replace('/targets')
}
}
My create mutation
const CREATE_DRAFT_MUTATION = gql`
mutation CreateTargetMutation($data: TargetCreateInput!) {
createTarget(data: $data) {
id
name
addr
category
}
}
`
GraphQL datamodel
type Target {
id: ID! #unique
name: String!
addr: [Addr!]!
category: String!
date:String!
location:String!
}
type Addr {
id: ID! #unique
target_url:String!
target_ip:String!
mobilepkg:String!
cars: [Car!]!
}
How do I put my ReactJS state which has a nested array into GraphQL?
PS:I am new to GraphQL and ReactJS.
EDIT: In playground im able to create my items but its not working in my actual application.
mutation CreateTargetMutation($data: TargetCreateInput!) {
createTarget(data: $data) {
id
name
addr{
target_ip
target_url
mobilepkg
cars{
id
}
}
category
date
location
}
}
{
"data": {
"name":"testerquery",
"addr": {
"create": {
"target_ip":"123",
"target_url":"123",
"mobilepkg":"asd",
"cars":{"connect":{"id":"cjs3yd83u004a0781jffzaqqr"}}
}
},
"category":"simple",
"date":"2019-03-12",
"location":"kl"
}
}
Bro, you are one the right path. You just need to iterate your values in order to solve this problem. Once you iterate through the values you simply need to make a call to the new array which contains everything and it will work. As your values are nested you will need to add "{}" to your car variable and within that contain your connect since you wish to create new "addr" whilst connecting to existing "car".
let create = []
for(let i=0; i < this.state.addr.length; i++){
create.push({
'mobilepkg':this.state.addr[i].mobilepkg,
'target_url':this.state.addr[i].target_url,
'target_ip':this.state.addr[i].target_ip,
'cars': {
'connect': this.state.addr[i].cars}
})
}
await this.props.createTargetMutation({
variables: {
data: {
name,
addr: {
create
},
category,
date,
location
}
}
})
this.props.history.replace('/targets')
}
}
Your values should now successfully pass into GraphQL and create targets with many "addr" whilst connecting to many "car"

Normalizr - is it a way to generate IDs for non-ids entity model?

I'm using normalizr util to process API response based on non-ids model. As I know, typically normalizr works with ids model, but maybe there is a some way to generate ids "on the go"?
My API response example:
```
// input data:
const inputData = {
doctors: [
{
name: Jon,
post: chief
},
{
name: Marta,
post: nurse
},
//....
}
// expected output data:
const outputData = {
entities: {
nameCards : {
uniqueID_0: { id: uniqueID_0, name: Jon, post: uniqueID_3 },
uniqueID_1: { id: uniqueID_1, name: Marta, post: uniqueID_4 }
},
positions: {
uniqueID_3: { id: uniqueID_3, post: chief },
uniqueID_4: { id: uniqueID_4, post: nurse }
}
},
result: uniqueID_0
}
```
P.S.
I heard from someone about generating IDs "by the hood" in normalizr for such cases as my, but I did found such solution.
As mentioned in this issue:
Normalizr is never going to be able to generate unique IDs for you. We
don't do any memoization or anything internally, as that would be
unnecessary for most people.
Your working solution is okay, but will fail if you receive one of
these entities again later from another API endpoint.
My recommendation would be to find something that's constant and
unique on your entities and use that as something to generate unique
IDs from.
And then, as mentioned in the docs, you need to set idAttribute to replace 'id' with another key:
const data = { id_str: '123', url: 'https://twitter.com', user: { id_str: '456', name: 'Jimmy' } };
const user = new schema.Entity('users', {}, { idAttribute: 'id_str' });
const tweet = new schema.Entity('tweets', { user: user }, {
idAttribute: 'id_str',
// Apply everything from entityB over entityA, except for "favorites"
mergeStrategy: (entityA, entityB) => ({
...entityA,
...entityB,
favorites: entityA.favorites
}),
// Remove the URL field from the entity
processStrategy: (entity) => omit(entity, 'url')
});
const normalizedData = normalize(data, tweet);
EDIT
You can always provide unique id's using external lib or by hand:
inputData.doctors = inputData.doctors.map((doc, idx) => ({
...doc,
id: `doctor_${idx}`
}))
Have a processStrategy which is basically a function and in that function assign your id's there, ie. value.id = uuid(). Visit the link below to see an example https://github.com/paularmstrong/normalizr/issues/256

Pushing array into object that is contained in array

UPDATED: Thanks to everyone for the help
I'm getting 2 collections from Firestore: Roles and Apps. Apps is a subcollection of Roles so it is contained inside of Roles collection, therefore, to get the apps I need to get the Roles document where they are contained first. I have no problem doing this but I need to save them as they are stored in firebase to group them later, I can't figure out how. I want some data like this:
[
{
roleDescription: 'System Administrator',
active: true,
roleId: '1'
apps: [
{
appId: '2',
appDescription: 'Manage Roles',
groupId: '1',
route: 'roles',
appName: 'Roles',
active: true
},
{
appId: '1',
appDescription: 'Users',
groupId: '1',
route: 'users',
appName: 'Users',
active: true
},
{
...
}
]
},
{
active: true,
roleId: '2',
roleDescription: 'Employee',
apps: [
{
appId: '5',
appDescription: 'Upload Data',
groupId: '1',
route: 'roles',
appName: 'Roles',
active: true
},
{
appId: '1',
appDescription: 'Users',
groupId: '1',
route: 'users',
appName: 'Users',
active: true
},
{
...
}
]
}
]
Currently I have this code where I can get all the roles in snapshot and map them to get every role individually and then with snapshot2 get the apps that are contained inside that role also individually to assign every app in snapshot2 to to the object or array of roles contained also in an array.
Here is my code:
ref.get()
.then(function(snapshot) {
return Promise.all(snapshot.docs.map(doc => {
var AppRole = {};
AppRole.role = doc.data();
roles.push(AppRole);
return doc.ref.collection('apps').get()
.then((snapshot2) => {
return snapshot2.docs.map(app => {
roles[count].apps = app.data(); // Here I need to push app.data() to roles[count].apps and problem solver but I don't know how to do it (roles[count].apps is an object and I know it doesnt have the push method but I tried with a counter also like roles[count].apps[i] = app.data(); i++; but no success )
})
})
console.log(count);
count++;
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})
You should be able to do this without any counters. I've commented the main changes in the code.
Basically, just keep a reference to the role after you create it. Then you can assign a list of apps to it all at once by using map().
const roles = []; // You can add and remove elements to a const array
const db = admin.firestore();
const ref = db.collection('roles');
ref.get()
.then(function(roleQuery) {
return Promise.all(roleQuery.docs.map(roleDoc => {
// ** Create a new role and keep a reference to it **
const role = roleDoc.data()
roles.push(role);
return roleDoc.ref.collection('apps').get()
.then((appQuery) => {
// ** Easily create a new array by calling
// ** map() on the app documents
role.apps = appQuery.docs.map(appDoc => {
return appDoc.data();
})
// ** alternate syntax:
// ** appQuery.docs.map(appDoc => appDoc.data())
})
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})
Don't really understand the full scope of your code, but why not just create an object that assigns apps to roles, and store the end result in the roles array?
You shouldn't need the j counter in this instance. I would imagine it looks something like this:
let roles = [];
const count = 0;
const db = admin.firestore();
const ref = db.collection('roles');
ref.get()
.then(function(querySnapshot) {
return Promise.all(querySnapshot.docs.map(doc => {
var AppRole = {}; //Declare AppRole object
AppRole.role = doc.data(); //Based on your question, this should return one role
roles.push(AppRole); //Adds object to array (You can use 'roles[count] = AppRole' if you feel more comfortable)
return doc.ref.collection('apps').get()
.then((snapshot2) => {
return snapshot2.docs.map(app => {
roles[count].apps = app.data(); //Based on your question, this should return an array of apps. That array should be stored within that particular object.
})
})
console.log(count);
count++;
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})

How to update object in React state

I have an indexed list of users in the JS object (not array). It's part of the React state.
{
1: { id: 1, name: "John" }
2: { id: 2, name: "Jim" }
3: { id: 3, name: "James" }
}
What's the best practice to:
add a new user { id: 4, name: "Jane" } with id (4) as key
remove a user with id 2
change the name of user #2 to "Peter"
Without any immutable helpers. I'm using Coffeescript and Underscore (so _.extend is ok...).
Thanks.
This is what i would do
add: var newUsers = _.extend({}, users, { 4: { id: 4, ... } })
remove: var newUsers = _.extend({}, users) then delete newUsers['2']
change: var newUsers = _.extend({}, users) then newUsers['2'].name = 'Peter'
If you're not using Flux, you use this.setState() to update the state object.
delUser(id) {
const users = this.state.users;
delete users[id];
this.setState(users);
}
addChangeUser(id, name) {
const users = this.state.users;
users[id] = {id: id, name: name};
this.setState(users);
}
Then you can execute your test cases with this:
addChangeUser(4, 'Jane);
addChangeUser(2, 'Peter');
delUser(2);
Apart from _.extend you can use Map for storing user
let user = new Map();
user.set(4, { id: 4, name: "Jane" }); //adds with id (4) as key
user.myMap.set(2, { id: 2, name: "Peter" }); // set user #2 to "Peter"
user.delete(3); //deletes user with id 3
setState also accepts a function, which you might find more intuitive
function add( user ) {
this.setState( users => {
users[ user.id ] = user
return users
}
}
function remove( id ) {
this.setState( users => {
delete users[ id ]
return users
}
These functions assume that your state object is your users object, if it is actually state.users then you'd have to pick users out, passing a function to setState will always be called passing the actual state object.
In this example add can also be used to amend, depending on your actual use-case you may want to create separate helpers.
Using spreads:
Adding
this.setState({
...this.state,
4: { id: 4, name: "Jane" },
}
Removing id 2
let prevState = this.state;
let {"2": id, ...nextState} = prevState;
this.setState({
...nextState,
}
Changing id 2
this.setState({
...this.state,
2: {
...this.state["2"],
name: "Peter",
}
}

Categories

Resources