I am using React.js
and I’ve been trying to use other functions within the useEffect()
hook and in onClickHandler()
functions, but I get Undefined
when I try to get the value of an attribute of an Object that is passed into the function as an argument.
I first encountered this problem when I developed this useEffect()
hook:
useEffect(() => {
const renderSegmentIfSetToTrue = (data) => {
if (data.segmentMetadata.selected === true) {
return arcGenerator({startAngle: data.segmentMetadata.startAngle, endAngle: data.segmentMetadata.endAngle});
}
return;
}
const circleSegmentsContainer = d3.select(circleSegmentsContainerRef.current);
circleSegmentsContainer
.selectAll('.circle-segment')
.data(musicKeysObject)
.join(
enter => enter.append('path')
.attr('d',data => renderSegmentIfSetToTrue(data))
.attr('class', 'circle-segment')
.attr('stroke', 'red'),
update => update.append('path')
.attr('d',data => renderSegmentIfSetToTrue(data))
.attr('class', 'circle-segment')
.attr('stroke', 'blue')
.attr('fill', 'green'),
exit => exit.append('path')
.attr('d',data => renderSegmentIfSetToTrue(data))
.attr('class', 'circle-segment')
.attr('stroke', 'blue')
.attr('fill', 'purple')
);
}, [musicKeysObject])
I am using D3.js as well to try and do some stuff here but that is not related to this problem or question. Please pay attention to the renderSegmentIfSetToTrue()
function (yes this is a terrible name, I’m going to change it later). This function is defined right at the top of the hook and you can see that if some attribute of the argument is true (if (data.segmentMetadata.selected === true)
), then I want to return something, again this part is unrelated.
I originally had this function defined OUTSIDE of the useEffect()
hook (it was defined literally right above) and I would get an error stating Cannot read properties of Undefined (reading segmentMetadata)
. I fixed this by moving the function to be defined within the hook instead and it worked fine.
I explain all of this because I am now encountering a similar issue in relation to onClickHandler()
functions. Here is a button with an onClickHandler
function (I have also tried defining the function separately and calling it like onClick={myHandlerFunction}
)
<button onClick={() => {
console.log('Button was clicked!')
setMusicKeysObject(musicKeysObject.map(({musicKey, index})=>
(index === 6) ? musicKey.segmentMetadata.selected = true : null
))}}>Click me!</button>
When I click this button, I get Cannot read properties of null (reading 'segmentMetadata')
but I have also seen the same error as mentioned above where it states properties of undefined
. This is making me think that I am not understanding something related to “scope” with React.js hook or just vanilla javascript functions.
For additional understanding, here is the object that I have defined, and it is defined in a different module and imported. I then use the useState()
hook to load the imported object into a new local object in the React.js function component:
import musicKeys from '../../MusicKeys'
export default function CircleOfFifths() {
const [musicKeysObject, setMusicKeysObject] = useState(musicKeys);
// Other code below (including the useEffect() hook explained above and this component contains the button with the `onClickHandler` problem I am now facing).
// 1 example of a musicKey
var F_Major = {
segmentMetadata: {
startAngle: null,
endAngle: null,
selected: false
}
}
// An array of musicKeys
var musicKeys = [
C_Major,
G_Major,
D_Major,
A_Major,
E_Major,
B_Major,
F_sharp_Major,
C_sharp_Major,
A_flat_Major,
E_flat_Major,
B_flat_Major,
F_Major
]
// Generate segment metadata
// Adds the startAngle and endAngle of each segment to an array
// Uses cumulativeAngle to keep track of each segment
const sectorAngle = (Math.PI * 2) / 12;
var cumulativeAngle = 0;
for (let i = 0; i < musicKeys.length; i++) {
let startAngle = cumulativeAngle;
cumulativeAngle += sectorAngle;
let endAngle = cumulativeAngle;
musicKeys[i].segmentMetadata.startAngle = startAngle;
musicKeys[i].segmentMetadata.endAngle = endAngle;
}
export default musicKeys;
I have a feeling this has something to do with “scope”, as putting the function inside the useEffect()
hook fixed that issue before, but I’d like a solid/answer explanation for what I’m doing wrong here and not understanding.
I have also thought that using typescript instead of javascript might help to fix this problem as a function would have to have a specific type passed into it, so then the function would now exactly what object it was dealing with and would know that it could get the value of a certain attribute.