如何使用 React 上下文 API?
How to use the React Context API?
我有一个带有全局元素的 React 应用程序(使用 CRA 设置)(自定义光标,即鼠标后面的圆形 div),当鼠标悬停在其他各种元素上时,我希望其样式 update/change嵌套不同且深度不同的组件(在下面提供的结构中,我只列出了一个示例组件)。据我了解,这是 Context API.
的一个很好的用例
我的应用程序的结构如下所示(已简化):
<Cursor />
<Layout>
<Content>
<Item />
</Content>
</Layout>
因此,当悬停 <Item />
(在其他组件中)时,我想更新 <Cursor />
组件的样式。
因此,我尝试在 <Item />
组件中访问我在 <Cursor />
组件中设置的函数。不幸的是,悬停时它不会更新我的状态,因此我的 <Cursor />
的样式不会改变。
我的 Cursor 组件看起来像这样(简化):
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
我的可以悬停的项目组件看起来像这样(简化):
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
我还需要使用 static contextType = CursorContext
吗?
当不传递默认值时(我认为它们是可选的)我得到一个 TypeError: Cannot read property 'handleMouseOverProjectTeaser' of undefined
,只要我传递一个长 false
作为默认值,我的应用程序就会呈现但确实不更新我的 <Cursor />
状态。
我是否正确使用了 Context API?
React.createContext 默认值?
如您正确所述,传递给 React.createContext()
的值在这种情况下无关紧要。
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 state.
这表明您的默认值始终被使用:尝试 运行 undefined.blahblah
与 false.blahblah
:前者抛出 TypeError 而第二个静默 returns undefined
.
所以我们知道您在 <Provider value={...}>
中设置的值永远不会到达消费者,但是 为什么?
上下文仅对其后代可用
<C.Consumer>
未呈现为 <C.Provider>
的后代,因此无法访问它。换句话说,提供者应该 "enclose" 消费者。来自文档:
Context is designed to share data that can be considered “global” for a tree of React components [...]
那棵树的根是你的 <C.Provider>
,在你的例子中,消费者不是那棵树的一部分。
类似的东西可以工作:
<CursorContext>
<StyledCursor />
<Layout>
<Content>
<Item />
</Content>
</Layout>
</CursorContext>
其他
Do I even need to use static contextType = CursorContext?
不是真的,如果您使用 <CursorContext.Consumer>
。来自文档:
Context.Consumer: A React component that subscribes to context changes.
但在你的情况下,因为你不需要听上下文变化(无论如何从你的示例代码),只需保持 static contextType
:
static contextType = CursorContext
render() {
return(
<StyledItem
onMouseOver={this.context.handleMouseOverProjectTeaser}
onMouseLeave={this.context.handleMouseLeaveProjectTeaser}
>
</StyledItem>
)
}
关键是你应该使用其中之一,你不需要两者。
最后一件事,您在提供程序中传递 this.state
,并在子组件中使用 this.context.handleMouseOverProjectTeaser
...但是在 <Cursor>
的状态下没有这样的功能.也许您打算传递 <Cursor>
本身,或者更好的是,只传递处理程序?
我有一个带有全局元素的 React 应用程序(使用 CRA 设置)(自定义光标,即鼠标后面的圆形 div),当鼠标悬停在其他各种元素上时,我希望其样式 update/change嵌套不同且深度不同的组件(在下面提供的结构中,我只列出了一个示例组件)。据我了解,这是 Context API.
的一个很好的用例我的应用程序的结构如下所示(已简化):
<Cursor />
<Layout>
<Content>
<Item />
</Content>
</Layout>
因此,当悬停 <Item />
(在其他组件中)时,我想更新 <Cursor />
组件的样式。
因此,我尝试在 <Item />
组件中访问我在 <Cursor />
组件中设置的函数。不幸的是,悬停时它不会更新我的状态,因此我的 <Cursor />
的样式不会改变。
我的 Cursor 组件看起来像这样(简化):
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
我的可以悬停的项目组件看起来像这样(简化):
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
我还需要使用 static contextType = CursorContext
吗?
当不传递默认值时(我认为它们是可选的)我得到一个 TypeError: Cannot read property 'handleMouseOverProjectTeaser' of undefined
,只要我传递一个长 false
作为默认值,我的应用程序就会呈现但确实不更新我的 <Cursor />
状态。
我是否正确使用了 Context API?
React.createContext 默认值?
如您正确所述,传递给 React.createContext()
的值在这种情况下无关紧要。
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 state.
这表明您的默认值始终被使用:尝试 运行 undefined.blahblah
与 false.blahblah
:前者抛出 TypeError 而第二个静默 returns undefined
.
所以我们知道您在 <Provider value={...}>
中设置的值永远不会到达消费者,但是 为什么?
上下文仅对其后代可用
<C.Consumer>
未呈现为 <C.Provider>
的后代,因此无法访问它。换句话说,提供者应该 "enclose" 消费者。来自文档:
Context is designed to share data that can be considered “global” for a tree of React components [...]
那棵树的根是你的 <C.Provider>
,在你的例子中,消费者不是那棵树的一部分。
类似的东西可以工作:
<CursorContext>
<StyledCursor />
<Layout>
<Content>
<Item />
</Content>
</Layout>
</CursorContext>
其他
Do I even need to use static contextType = CursorContext?
不是真的,如果您使用 <CursorContext.Consumer>
。来自文档:
Context.Consumer: A React component that subscribes to context changes.
但在你的情况下,因为你不需要听上下文变化(无论如何从你的示例代码),只需保持 static contextType
:
static contextType = CursorContext
render() {
return(
<StyledItem
onMouseOver={this.context.handleMouseOverProjectTeaser}
onMouseLeave={this.context.handleMouseLeaveProjectTeaser}
>
</StyledItem>
)
}
关键是你应该使用其中之一,你不需要两者。
最后一件事,您在提供程序中传递 this.state
,并在子组件中使用 this.context.handleMouseOverProjectTeaser
...但是在 <Cursor>
的状态下没有这样的功能.也许您打算传递 <Cursor>
本身,或者更好的是,只传递处理程序?