【问题标题】:React/Gatsby component interaction (lifting state up) with components in MDX filesReact/Gatsby 组件与 MDX 文件中的组件交互(提升状态)
【发布时间】:2020-11-23 20:34:28
【问题描述】:

我使用带有 MDX 插件的 Gatsby。所以我可以在 Markdown 中使用 React 组件。没关系。

我有组件,可以互相交谈。为此,我使用Lifting State Up Pattern。没关系。

这是一个基本的 Counter 示例,用于展示我的概念验证代码。

import React from "react"

export class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
    this.handleCounterUpdate = this.handleCounterUpdate.bind(this)
  }

  handleCounterUpdate() {
    this.setState({ count: this.state.count + 1 })
  }

  render() {
    const children = React.Children.map(this.props.children, child => {
      const additionalProps = {}
      additionalProps.count = this.state.count
      additionalProps.handleCounterUpdate = this.handleCounterUpdate
      return React.cloneElement(child, additionalProps)
    })
    return <div>{children}</div>
  }
}

export function Display({ count }) {
  return <h2>Current counter is: {count}</h2>
}

export function UpdateButton({ handleCounterUpdate }) {
  return <button onClick={handleCounterUpdate}>Increment couter by one</button>
}

通过此设置,您可以使用这样的组件

<Counter>
  <Display />
  <UpdateButton />
</Counter>

甚至像这样

<Counter>
  <Display />
  <UpdateButton />
  <Display />
  <Display />
</Counter>

没关系。

在现实世界中,封闭的 Counter 组件(状态持有者)将类似于 Layout 组件。 &lt;Layout&gt; 在模板中使用并呈现 MDX 页面。看起来像这样:

<SiteLayout>
  <SEO title={title} description={description} />
  <TopNavigation />
  <Display />       // The state holder is <SiteLayout>, not <Counter> 
  <Breadcrumb location={location} />
  <MDXRenderer>{page.body}</MDXRenderer>  // The rendered MDX
</SiteLayout>

&lt;UpdateButton&gt;(在现实世界中类似于 &lt;AddToCartButton&gt;)位于 MDX 页面上,不再是 &lt;Layout&gt; 组件的直接子代。

这种模式不再起作用了。

我该如何解决这个问题?

谢谢大家

【问题讨论】:

  • 您应该能够将道具从您的&lt;MDXRenderer /&gt; 传递到&lt;AddToCartButton&gt;
  • 我认为您可以设置上下文提供程序并在间接子组件中使用其状态
  • 谢谢@Mark 你支持扩展和覆盖MDXRenderer吗?我还在学习 React。
  • Stefan,我实际上会推荐 Derek 的提议。它会给你一个很好的上下文 API 课程。
  • 是的@Mark。我也意识到了这一点。阅读并制作了 POC。一切都很好。将其发布为像我这样的其他初学者的解决方案。如果您喜欢,请提出改进​​意见和建议。谢谢大家的帮助。

标签: javascript reactjs react-state


【解决方案1】:
import React from "react"

// This is a proof of concept (POC) for intercomponent communication using
// React Context
//
// In the real world Gatsby/React app we use a kind of cart summary component
// at top right of each page. The site consists of thousands of pages with detailed
// product information and a blog. Users have the possibility to add products directly
// from product information pages and blog posts. Posts and pages are written in
// MDX (Mardown + React components). All <AddToCartButtons> reside in MDX files.
//
// This code uses a "increment counter button" (= add to cart button) and a
// display (= cart summary) as POC
//
// More information at
// https://reactjs.org/docs/context.html#updating-context-from-a-nested-component

export const CounterContext = React.createContext()

// The <Layout> component handles all business logic. Thats fine. We have
// very few app features.
export class Layout extends React.Component {
  constructor(props) {
    super(props)
    this.handleCounterUpdate = this.handleCounterUpdate.bind(this)
    this.state = { count: 0, increment: this.handleCounterUpdate }
  }

  handleCounterUpdate() {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    return (
      <div style={{ backgroundColor: "silver", padding: "20px" }}>
        <CounterContext.Provider value={this.state}>
          {this.props.children}
        </CounterContext.Provider>
      </div>
    )
  }
}

export class UpdateButton extends React.Component {
  static contextType = CounterContext
  render() {
    const count = this.context.count
    const increment = this.context.increment
    return (
      <button onClick={increment}>
        Increment counter (current value: {count})
      </button>
    )
  }
}

export class Display extends React.Component {
  static contextType = CounterContext

  render() {
    return (
      <div
        style={{
          backgroundColor: "white",
          padding: "10px",
          margin: "10px 0 0 0"
        }}
      >
        <div>I'm Display. I know the count: {this.context.count}</div>
        <div>{this.props.children}</div>
      </div>
    )
  }
}

// Function components use <CounterContext.Consumer>. Class components
// use static contextType = CounterContext
export function AChild() {
  return (
    <CounterContext.Consumer>
      {context => (
        <span>
          I'm a child of Display. I know the count too: {context.count}
        </span>
      )}
    </CounterContext.Consumer>
  )
}

这样使用

<Layout>
  <Display>
    <AChild />
  </Display>
  // UpdateButton inside MDX files
  <MDXRenderer>{page.body}</MDXRenderer>
 // Or elsewhere
  <UpdateButton /> 
</Layout>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2018-01-21
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多