How to change the state of some component with setInterval()? - reactjs

How can I make so that every 2 seconds my state randomly changes? For now it changes in console log but it doesn't render it. The active state change is supposed to change the Switch component visually. For example, if I exclude boxList[0].machines[0].switches[0].active = true it changes correctly but when it's within setInterval() it doesn't change anything.
Example:
App.js
var boxList= [
{id: 1, machines:[
{id: 1, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 2, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 3, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 4, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
]},
{id: 2, machines:[
{id: 1, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 2, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 3, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
{id: 4, switches:[{id: 1, active: false}, {id: 2, active: false}, {id: 3, active: false}, {id: 4, active: false}]},
]},
]}];
class App extends React.Component {
render() {
setInterval(() => {
var x = [0,1].sample()
var x = [0,1].sample()
var z = [0,1,2,3].sample()
var info = [true, false].sample()
boxList[x].machines[y].switches[z].active = info;
}, 2000);
var boxes = boxList.map((box) =>
<Box key={box.id} box_id={box.id} machines={box.machines} />);
return (
<div>
{boxes}
</div>
);
}
}

Your component doesn't have state, it accesses some globally available data. However, the actual problem is that you are never telling the component to rerender. The component doesn't know that the data is changed, hence it doesn't update.
If you don't want to move the data into the component's state, you can use forceUpdate to force a rerender.
But: The render method should never directly or indirectly goes another render. Having the setInterval call in render doesn't make much sense either, since this would create a new interval every time the component renders.
Instead, make use of the lifecycle methods and create the interval after the component mounted. Don't forget to clear the interval when the component is destroyed, too!
class App extends React.Component {
componentDidMount() {
this._interval = setInterval(() => {
// ...
this.forceUpdate();
}, 2000);
}
componentWillUnmount() {
clearInterval(this._interval);
}
render() {
// ...
}
}

Related

How to Add Object to Array State in ReactJs?

I have this array of objects state, and it is working fine.
I need add another object to it dynamically.
const [productData, SetProductData] = useState({
sizes: [
{id: 2, value: 'Small', isActive: false},
{id: 2, value: 'Medium', isActive: false},
{id: 2, value: 'Large', isActive: true},
{id: 2, value: 'X Large', isActive: false},
{id: 2, value: 'XX Large', isActive: false}
]
})
I tried to do it like this, but it is not working
const addObjectToArray = obj => {
SetProductData(current => [...current, obj]);
};
addObjectToArray( {id: 3, value: 'XXX Large', isActive: true} )
I also need to update it dynamically
You need to keep the same structure in your state. You had an object, and you're changing it to an array. So
const addObjectToArray = obj => {
SetProductData(current => ({
...current,
sizes: [...current.sizes, obj]
}));
};
addObjectToArray( {id: 3, value: 'XXX Large', isActive: true} )

Sum of array items

I am working on React project and I am using Redux and Hooks. I have problem and wasted 2 days.
I have this array which comes from state:
Array = [
{value: 0, id: 161435}
{value: 1, id: 161435}
{value: 2, id: 161435}
{value: 3, id: 161435}
{value: 4, id: 161435}
,
{value: 1, id: 161434}
{value: 2, id: 161434}
{value: 3, id: 161434}
{value: 4, id: 161434}
{value: 5, id: 161434}
]
I want the result like
[{value:10,id:161435},{value:14,id:161434}]
I have tried array.reduce and it's not work.
Can anyone help me in this ?
Don't use reduce, do this instead:
const arr = [
{value: 0, id: 161435},
{value: 1, id: 161435},
{value: 2, id: 161435},
{value: 3, id: 161435},
{value: 4, id: 161435},
{value: 1, id: 161434},
{value: 2, id: 161434},
{value: 3, id: 161434},
{value: 4, id: 161434},
{value: 5, id: 161434},
];
const valueSumById = {};
for (const { value, id } of arr) {
valueSumById[id] = (valueSumById[id] || 0) + value;
}
const result = Object
.entries(valueSumById)
.map(([id, value]) => ({ id, value }));
console.log(result);
const allObj = [
{value: 0, id: 161435},
{value: 1, id: 161435},
{value: 2, id: 161435},
{value: 3, id: 161435},
{value: 4, id: 161435},
{value: 1, id: 161434},
{value: 2, id: 161434},
{value: 3, id: 161434},
{value: 4, id: 161434},
{value: 5, id: 161434}
]
const total = []
allObj.map((obj) => {
const theIndex = total.findIndex(item => item.id === obj.id);
if( theIndex === -1){
total.push(obj)
} else {
total[theIndex].value += obj.value;
}
});
console.log(total) // [ { value: 10, id: 161435 }, { value: 15, id: 161434 }]

Map Angular Array to Object

I have been looking and have found a few good references for transforming arrays to objects, but I can't seem to find my use case. I have an array with the following format
[
{id: 1, name: 'hello', display: false},
{id: 5, name: 'hello2', display: true},
{id: 7, name: 'hello8', display: true},
]
and I would like to map it into something like this
{
5: {id: 5, name: 'hello2'},
7: {id: 7, name: 'hello8'}
}
I have been trying to use the map function, but I can't figure it out since I want the keys of my map to be an id. This is what I have so far but it is obviously wrong.
const myArray = [
{id: 1, name: 'hello', display: false},
{id: 5, name: 'hello2', display: true},
{id: 7, name: 'hello8', display: true},
];
const myMap = myArray.filter(row => row.display)
.map(row => {
return {row.id: {id: row.id, name: row.name}
});
Filter the array, map it to pairs of [id, obj], and convert to an object using Object.fromEntries(). You can use destructuring and rest syntax (...) to remove display.
Notes: if Object.fromEntries() is not supported, change target in TS Config to ES2019.
const arr = [{id: 1, name: 'hello', display: false},{id: 5, name: 'hello2', display: true}, {id: 7, name: 'hello8', display: true}]
const result = Object.fromEntries(
arr.filter(o => o.display)
.map(({ display, ...o }) => [o.id, o])
)
console.log(result)
Another option is to use Array.reduce() to create the object. In that case, you can skip objects with false display.
const arr = [{id: 1, name: 'hello', display: false},{id: 5, name: 'hello2', display: true}, {id: 7, name: 'hello8', display: true}]
const result = arr.reduce((acc, { display, ...o }) => {
if(display) acc[o.id] = [o.id, o]
return acc
}, {})
console.log(result)

vis.js Network regeneratorRuntime is not defined [regeneratorRuntime is not defined] error in Salesforce

Trying to use vis.js Network library in Salesforce I have tried both in LWC and in an Aura component with api version 40.0 but am receiving
regeneratorRuntime is not defined [regeneratorRuntime is not defined]
trying to use https://unpkg.com/browse/vis-network#8.1.0/standalone/umd/vis-network.min.js
Here is what I believe would solve your issue.
First Approach (only if library is small enough to be uploaded as LWC component)
You need to create two LWC component:
myNetwork
myNetwork.js
myNetwork.html
myNetwork.js-meta.xml
visNetworkLib
visNetwork.js = content of esm/vis-network.min.js
visNetwork.js-meta.xml
import { LightningElement } from 'lwc'
import { DataSet, Network } from 'c/visNetworkLib'
export default class MyNetwork extends LightningElement {
nodes = null
edgeds = null
renderedCallback () {
this.nodes = new DataSet([
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'},
])
this.edges = new DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5},
{from: 3, to: 3},
])
const container = this.template.querySelector('div.myNetwork')
this.network = new Network(container, {
nodes: this.nodes,
edges: this.edges,
}, {})
}
}
<template>
<div
lwc:dom="manual"
class="myNetwork"
></div>
</template>
Second approach
Create a static resource name visNetwork which is the uploaded vis-network.min.js
Then load it like that in myNetwork.js
import { LightningElement } from 'lwc'
import { ShowToastEvent } from 'lightning/platformShowToastEvent'
import visNetworkUrl from '#salesforce/resourceUrl/visNetwork'
import { loadScript } from 'lightning/platformResourceLoader'
export default class MyNetwork extends LightningElement {
nodes = null
edgeds = null
visLoaded = false;
renderedCallback() {
if (!this.visLoaded) {
this.visLoaded = true;
loadScript(this, visNetworkUrl).then(() => {
this.initializeNetwork();
})
.catch(error => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error loading vis network',
message: error.message,
variant: 'error'
})
);
});
}
}
initializeNetwork () {
this.nodes = new DataSet([
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'},
])
this.edges = new DataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5},
{from: 3, to: 3},
])
const container = this.template.querySelector('div.myNetwork')
this.network = new Network(container, {
nodes: this.nodes,
edges: this.edges,
}, {})
}
}
I have figured it out, you have to load vis-data seperate from vis-network. you can find my working example here based on the NPSP Relationships and NPSP Affiliations.
https://github.com/mrainboldt/visNetworkMapLWC

Need help in creating network graph using visjs in angularjs

I need help in making this plunker work something similar to this vis example in angularjs.
I am using <vis-network data="data" options="options"></vis-network> tag and below data and options
data
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'}
];
var edges = [
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
];
$scope.data = VisDataSet({
nodes: nodes,
edges: edges
});
options
$scope.options = {
autoResize: true,
height: '100%',
width: '100%'
};
There is no console error, what am I missing. Please help.
Your data is plain object, however nodes & edges should be an object of VisDataSet
var nodes = VisDataSet([
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5'}
]);
var edges = VisDataSet([
{from: 1, to: 3},
{from: 1, to: 2},
{from: 2, to: 4},
{from: 2, to: 5}
]);
$scope.data = {
nodes: nodes,
edges: edges
};
I have updated your plunker here.

Resources