如何使用 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.blahblahfalse.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> 本身,或者更好的是,只传递处理程序?