如何使用 React 上下文 API?

Posted

技术标签:

【中文标题】如何使用 React 上下文 API?【英文标题】:How to use the React Context API? 【发布时间】:2019-12-15 12:51:11 【问题描述】:

我有一个带有全局元素(自定义光标,即鼠标后的圆形 div)的 React 应用程序(使用 CRA 设置),当悬停不同嵌套的其他各种组件时,我想更新/更改其样式,或者不那么深入(在下面提供的结构中,我仅列出一个示例组件)。据我了解,这是 Context API 的一个很好的用例。

我的 App 的结构如下(简化):

<Cursor />
<Layout>
  <Content>
    <Item />
  </Content>
</Layout>

所以当悬停&lt;Item /&gt;(在其他组件中)时,我想更新&lt;Cursor /&gt; 组件的样式。

因此,我尝试访问我在 &lt;Cursor /&gt; 组件中设置的函数,位于 &lt;Item /&gt; 组件中。不幸的是,悬停时它不会更新我的状态,因此我的&lt;Cursor /&gt; 的样式不会改变。

我的光标组件看起来像这样(简化):

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 - scrollOffsetYpx`, left: `$positionXpx`
                  : backgroundColor: "yellow", display: `$display`, top: `$positionY - scrollOffsetYpx`, left: `$positionXpx`
        />
      </div>
    </CursorContext.Provider>
    )
  



export default Cursor

我的可以悬停的Item Component看起来像这样(简化):

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 作为默认值,我的应用程序就会呈现但不会更新我的&lt;Cursor /&gt;状态。

我是否正确使用了 Context API?

【问题讨论】:

“当不传递默认值时”不确定这是指哪个部分 它是指拥有 React.createContext(false) 与拥有 React.createContext() 谢谢。赞成详细和集中的问题。 我在功能组件malikasinger.medium.com/…找到了这篇关于上下文API的文章 【参考方案1】:

React.createContext 默认值?

正如您所说的那样,在这种情况下,传递给 React.createContext() 的值无关紧要。

当不传递默认值时(我认为它们无论如何都是可选的)我得到一个 TypeError: Cannot read property 'handleMouseOverProjectTeaser' of undefined, 只要我传递了一个很长的 false 作为默认值,我的应用程序就会呈现但没有更新我的状态。

这表明您的默认值始终被使用:尝试运行 undefined.blahblahfalse.blahblah:前者抛出 TypeError 而第二个静默返回 undefined

所以我们知道您在&lt;Provider value=...&gt; 中设置的值永远不会到达消费者,但是为什么?

上下文仅对其后代可用

&lt;C.Consumer&gt; 未呈现为&lt;C.Provider&gt; 的后代,因此无法访问它。换句话说,提供者应该“包围”消费者。来自文档:

上下文旨在共享可被视为 React 组件树“全局”的数据 [...]

那棵树的根是你的&lt;C.Provider&gt;,在你的情况下,消费者不是那棵树的一部分。

类似的东西可以工作:

<CursorContext>
  <StyledCursor />
  <Layout>
    <Content>
      <Item />
    </Content>
  </Layout>
</CursorContext>

杂项

我什至需要使用静态 contextType = CursorContext 吗?

如果您使用的是&lt;CursorContext.Consumer&gt;,则不是。来自文档:

Context.Consumer:订阅上下文更改的 React 组件。

但在您的情况下,由于您不需要监听上下文更改(无论如何都来自您的示例代码),只需保留 static contextType:

  static contextType = CursorContext

  render() 
    return(
      <StyledItem
        onMouseOver=this.context.handleMouseOverProjectTeaser
        onMouseLeave=this.context.handleMouseLeaveProjectTeaser
      >
      </StyledItem>
    )
  

关键是你应该使用其中一个,你不需要两个


最后一件事,您在提供程序中传递this.state,并在子组件中使用this.context.handleMouseOverProjectTeaser...但是&lt;Cursor&gt; 的状态中没有这样的功能。也许您打算传递 &lt;Cursor&gt; 本身,或者更好的是,只传递处理程序?

【讨论】:

感谢您非常详细且解释清楚的回答,非常感谢。老实说,我找不到时间尝试它,但会告诉你。再次感谢您! 这篇文章可能会有所帮助malikasinger.medium.com/…

以上是关于如何使用 React 上下文 API?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React v16.6 的新 CONTEXT API 中获取多个静态上下文

如何使用 React Context API 跨多个 Routes 拥有状态?

在组件之间路由时如何保持 React 新的 Context API 状态?

如何将 React Context API 与 useReducer 一起使用以遵循与 Redux 类似的风格 [关闭]

如何在 React Context API 挂钩中访问多个上下文的调度方法?

如何将上下文 api 与反应路由器 v4 一起使用?