Cant access object from scene - javascript

I am having trouble accessing a object in my scene. I've figured out the reason I wasn't able to access it was because the Raytracer was loading before the world had finished loading. It works fine for objects added by code, but it doesn't work when looking for objects imported by GLTFLoader.
I have set up something to trigger an event when all the objects have been loaded. But I am unable to access the variable inside of the check in my update function.
setRayCaster()
{
this.rayCaster = new THREE.Raycaster()
this.foundButton = this.main.scene.children.find(child => child.name === 'Test')
this.objectsToTest = [this.main.scene.children.find(child => child.name === 'Test')]
this.resources.on('ready', () =>
{
this.UNABLETOACCESSTHIS = this.scene.getObjectByName('Trackpad')
})
}
update()
{
this.rayCaster.setFromCamera(this.cursor, this.camera.view)
this.intersects = this.rayCaster.intersectObject(this.foundButton)
UNABLETOACCESSTHIS.here
for(const object of this.objectsToTest)
{
object.material.color.set('#ff0000')
}
for(const intersect of this.intersects)
{
intersect.object.material.color.set('#0000ff')
}
// console.log(this.intersects)
}
}

Related

React "magically" updates two states instead of one

I have two states defined like so:
const [productProperties, setProductProperties] = useState<
PropertyGroup[] | null
>(null);
const [originalProductProperties, setOriginalProductProperties] = useState<
PropertyGroup[] | null
>(null);
The first one is supposed to be updated through user input and the second one is used later for a comparison so that only the PropertyGroup's that have changed values will be submitted via API to be updated.
I have done this a thousand times before, but for some reason when I change the name value for a PropertyGroup and update the state for 'productProperties' like so:
(e, itemId) => {
const update = [...productProperties];
const i = update.findIndex((group) => group.id === itemId);
if (i !== -1) {
update[i].name = {
...update[i].name,
[selectedLocale]: e.currentTarget.value,
};
setProductProperties([...update]);
}
}
The state of originalProductProperties also updates. Why? setOriginalProductProperties is never called here, I am also not mutating any state directly and I use the spread operator to be sure to create new references. I am lost.
Preface: It sounds like the two arrays are sharing the same objects. That's fine provided you handle updates correctly.
Although you're copying the array, you're modifying the object in the array directly. That's breaking the main rule of state: Do Not Modify State Directly
Instead, make a copy of the object as well:
(e, itemId) => {
const update = [...productProperties];
const i = update.findIndex((group) => group.id === itemId);
if (i !== -1) {
update[i] = { // *** Note making a new object
...update[i],
[selectedLocale]: e.currentTarget.value,
};;
setProductProperties(update); // (No need to *re*copy the array here, you've already done it at the top of the function)
}
}
Or, since you have that i !== -1 check there, we could copy the array later so we don't copy it if we don't find the group matching itemId:
(e, itemId) => {
const i = productProperties.findIndex((group) => group.id === itemId);
if (i !== -1) {
const update = [...productProperties];
update[i] = { // *** Note making a new object
...update[i],
[selectedLocale]: e.currentTarget.value,
};;
setProductProperties(update);
}
}
FWIW, in cases where you know there will be a match, map is good for this (but probably not in this case, since you seem to indicate the group may not be there):
(e, itemId) => {
const update = productProperties.map((group) => {
if (group.id === itemId) {
// It's the one we want, create the replacement
group = {
...group,
[selectedLocale]: e.currentTarget.value,
};
}
return group;
});
setProductProperties(update);
}
Or sometimes you see it written with a conditional operator:
(e, itemId) => {
const update = productProperties.map((group) =>
group.id === itemId
? { // It's the one we want, create a replacement
...group,
[selectedLocale]: e.currentTarget.value,
}
: group
);
setProductProperties(update);
}

Object values not changing

I'm working on a VueJS component that, among other things, can export data to .xlsx. For this, I'm using the json2xls library - so I need to pass to the json2xls() function an array of objects with identical keys (said keys will be column names)
This data I have to export is in an array of pretty deeply nested objects, though, so I need a function that will process that data to a form that will work with json2xls.
This is the method I'm using for that:
exportReport () {
const dataMap = []
this.reportPreview.forEach(elm => {
const auxObj = {}
auxObj.name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
dataMap.push(auxObj)
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}
If I do this, however, it appears that in cycles of elm.legal_files.forEach() the properties auxObj.legalfile and auxObj.actions are not overwritten, pushing several objects with identical values to dataMap
Why is this happening? What am I doing wrong? I'm hacking my way around this copying auxObj after "overwriting" the legalfile and actions properties and pushing the copy. This hack works, but I wonder what causes the first behavior and if there's a cleaner way around it.
exportReport () {
const dataMap = []
this.reportPreview.forEach(elm => {
const auxObj = {}
auxObj.name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
/*
If I just push auxObj to dataMap, the object is pushed with the same properties every time.
Copying auxObj and pushing the copy is a hack around this problem, but there may be a cleaner solution.
*/
const objCopy = { ...auxObj }
dataMap.push(objCopy)
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}
You pushed the same object every time.
exportReport() {
const dataMap = []
this.reportPreview.forEach(elm => {
const name = `${elm.client.first_name} ${elm.client.surname_1} ${elm.client.surname_2}`
elm.legal_files.forEach((e) => {
const auxObj = {} // Create a new object here
auxObj.name = name
auxObj.legalfile = e.code
auxObj.actions = e.actions.count
dataMap.push(auxObj) // Push it into the array
})
})
exportToXls(dataMap, `action-report-by-client-${this.options.start_date}-${this.options.end_date}.xlsx`)
}

Overriding an array function for a single instance

I'm trying to write a custom push function and only override it for a single instance. I'm trying to wire it up so every time I push to this array I can trigger my extra code. I don't want to override it for all Arrays or create a custom class if I don't have to.
First I had this:
let user_progress = {completed: [], inprogress: [] }
user_progress.completed.push = (item) => {
Array.prototype.push.call(user_progress.completed, item)
//do extra stuff
common.store_data(user_progress, RESOURCE_COLLECTION)
return undefined;
}
Then I tried this:
let Progress = function(){
this.completed = new Array()
this.inprogress = new Array()
}
let user_progress = new Progress()
user_progress.completed.push = (item) => {
Array.prototype.push.call(user_progress.completed, item)
common.store_data(user_progress, RESOURCE_COLLECTION)
return undefined;
}
user_progress.completed.push(item) works the first time you call it then it gets overwritten and becomes the standard Array.push. I assume it has something today with the Array.Prototype.push.call() and passing in user_progress.completed. What am i doing wrong? Both don't work and have the same issue. What is going on in the background here and is there an easy fix?

getElementById returns element but then returns null

So I'm trying to set up some references of some DOM elements in my JS. However, for some reason, they return the correct elements, but then return null shortly after. So i tried wrapping them in an if check to make sure the elements aren't null to prevent this. however, it still seems to be happening.
resizer.js
// Define DOM elements
const resizer = document.getElementById('resizer');
let btnMob;
let btnTablet;
let btnLaptop;
let btnDesktop;
// Check buttons exist and asign to variables if they do
if (document.getElementById('resizer-mob') !== null) {
btnMob = document.getElementById('resizer-mob');
}
if (document.getElementById('resizer-tablet') !== null) {
btnTablet = document.getElementById('resizer-tablet');
}
if (document.getElementById('resizer-laptop') !== null) {
btnLaptop = document.getElementById('resizer-laptop');
}
if (document.getElementById('resizer-desktop') !== null) {
btnDesktop = document.getElementById('resizer-desktop');
}
// Define string constants
const DESKTOP_CLASS = 'resizer--desktop';
const LAPTOP_CLASS = 'resizer--laptop';
const TABLET_CLASS = 'resizer--tablet';
const MOB_CLASS = 'resizer--mob';
// Check elements have been asign correctly
console.log('mob is ', btnMob);
console.log('tablet is ', btnTablet);
console.log('laptop is ', btnLaptop);
console.log('desktop is ', btnDesktop);
// Update resizer to display desktop
btnDesktop.addEventListener('click', () => {
console.log("DESKTOP");
resizer.classList.remove(LAPTOP_CLASS);
resizer.classList.remove(TABLET_CLASS);
resizer.classList.remove(MOB_CLASS);
resizer.classList.add(DESKTOP_CLASS);
});
// Update resizer to display laptop
btnLaptop.addEventListener('click', () => {
console.log("LAPTOP");
resizer.classList.remove(DESKTOP_CLASS);
resizer.classList.remove(TABLET_CLASS);
resizer.classList.remove(MOB_CLASS);
resizer.classList.add(LAPTOP_CLASS);
});
// Update resizer to display tablet
btnTablet.addEventListener('click', () => {
console.log("TABLET");
resizer.classList.remove(DESKTOP_CLASS);
resizer.classList.remove(LAPTOP_CLASS);
resizer.classList.remove(MOB_CLASS);
resizer.classList.add(TABLET_CLASS);
});
// Update resizer to display mobile
btnMob.addEventListener('click', () => {
console.log("MOB");
resizer.classList.remove(DESKTOP_CLASS);
resizer.classList.remove(LAPTOP_CLASS);
resizer.classList.remove(TABLET_CLASS);
resizer.classList.add(MOB_CLASS);
});
Have you tried != instead of !==?
And it returns null or undefined?
If it's undefined you need to use:
typeof(element) != 'undefined'
The code is behaving as expected. The first time you run the code everything works as you haven't deleted any elements. The second time you have, and so some elements return null because they don't exist anymore.

Updating State React

The following image represents an object with two ui controls that are stored as this.state.controls
Initially the statesValue values are set via data that is received prior to componentDidMount and all is good. However updates to the each of the controls statesValues are sent via an event , which is handled with the following function
const handleValueStateChange = event => {
let controls = Object.entries(this.state.controls);
for (let cont of controls) {
let states = cont[1].states;
if (states) {
let state = Object.entries(states);
for (let [stateId, contUuid] of state) {
if (contUuid === event.uuid) {
cont[1].statesValue[stateId] = event.value;
}
}
}
}
};
which updates the values happily, however bearing in mind the updated values that change are a subset of this.state.controls, I have no idea how to use this.setState to update that that has been changed.
thanks in advance for any pointers
Instead of using Object.entries try destructuring to keep the reference to the objects.
And have a look at lodash. There are some nice helper functions to iterate over objects like mapValues and mapKeys. So you can keep the object structure and just replace the certain part. Afterwards update the whole state-object with the new one.
const handleValueStateChange = event => {
let {controls} = this.state;
controls = _.mapValues(controls, (cont) => {
const states = cont[1].states;
if (states) {
_.mapValues(states, (contUuid,stateId) => {
if (contUuid === event.uuid) {
cont[1].statesValue[stateId] = event.value;
}
});
}
return cont;
});
this.setState({controls});
};
Code is not tested but it should work like this.
Problem is you're updating an object which you've changed from it's original structure (by using Object.entries). You can still iterate in the same way however you'll need to update an object that maintains the original structure. Try this:
Make a copy of the controls object. Update that object. Replace it in state.
const handleValueStateChange = event => {
// copy controls object
const { controls } = this.state;
let _controls = Object.entries(controls);
for (let cont of _controls) {
let states = cont[1].states;
if (states) {
let state = Object.entries(states);
for (let [stateId, contUuid] of state) {
if (contUuid === event.uuid) {
// update controls object
controls[cont[0]].statesValue[stateId] = event.value;
}
}
}
}
}
// replace object in state
this.setState({controls});
};

Categories