I have a React App (setup with CRA) with a global element (custom cursor, that is a rounded div following the mouse) whose style I want to update/change when hovering various other components that are nested differently and more or less deeply (in the structure provided below I am only listing one example component). From what I understand this is a good use case for Context API.
The structure of my App looks like this (simplified):
<Cursor />
<Layout>
<Content>
<Item />
</Content>
</Layout>
So when hovering <Item /> (amongst other components) I want to update the style of the <Cursor /> component.
Therefore I tried to access a function I set up inside my <Cursor /> component in my <Item /> component. Unfortunately when hovering it does not update my state and as consequence the style of my <Cursor /> does not change.
My Cursor component looks like this (simplified):
import React, { Component } from "react"
export const CursorContext = React.createContext(false)
class Cursor extends Component {
constructor(props) {
super(props)
this.state = {
positionX: 0,
positionY: 0,
scrollOffsetY: 0,
display: "none",
isHoveringProjectTeaserImage: false,
}
this.handleMousePosition = this.handleMousePosition.bind(this)
this.handleMouseOverProjectTeaser = this.handleMouseOverProjectTeaser.bind(this)
this.handleMouseLeaveProjectTeaser = this.handleMouseLeaveProjectTeaser.bind(this)
}
handleMousePosition = (mouse) => {
this.setState({
positionX: mouse.pageX,
positionY: mouse.pageY,
display: "block",
scrollOffsetY: window.pageYOffset
})
}
handleMouseOverProjectTeaser = () => {
this.setState({
isHoveringProjectTeaserImage: true
})
}
handleMouseLeaveProjectTeaser = () => {
this.setState({
isHoveringProjectTeaserImage: false
})
}
componentDidMount() {
document.body.addEventListener("mousemove", this.handleMousePosition)
}
componentWillUnmount() {
document.body.removeEventListener("mousemove", this.handleMousePosition)
}
render() {
const {
positionX,
positionY,
display,
scrollOffsetY,
isHoveringProjectTeaserImage
} = this.state
return(
<CursorContext.Provider value={this.state}>
<div>
<StyledCursor
style={ isHoveringProjectTeaserImage
? {backgroundColor: "red", display: `${display}`, top: `${positionY - scrollOffsetY}px`, left: `${positionX}px`}
: {backgroundColor: "yellow", display: `${display}`, top: `${positionY - scrollOffsetY}px`, left: `${positionX}px`}}
/>
</div>
</CursorContext.Provider>
)
}
}
export default Cursor
And my Item Component that can be hovered looks like this (simplified):
import React, { Component } from "react"
import { CursorContext } from '../Cursor/Index';
class Item extends Component {
constructor(props) {
// not showing stuff in here that's not relevant
}
static contextType = CursorContext
render() {
return(
<CursorContext.Consumer>
{(value) =>
<StyledItem
onMouseOver={value.handleMouseOverProjectTeaser}
onMouseLeave={value.handleMouseLeaveProjectTeaser}
>
</StyledItem>
}
</CursorContext.Consumer>
)
}
}
export default Item
Do I even need to use static contextType = CursorContext?
When not passing a default value (I thought they are optional anyway) I am getting an TypeError: Cannot read property 'handleMouseOverProjectTeaser' of undefined, as soon as I pass a long a false as default value my App renders but does not update my <Cursor /> state.
Am I even using Context API correctly?