简体   繁体   English

React/Gatsby 组件交互(将 state 向上提升)与 MDX 文件中的组件

[英]React/Gatsby component interaction (lifting state up) with components in MDX files

I use Gatsby with the MDX plugin.我将 Gatsby 与 MDX 插件一起使用。 So I can use React components in markdown.所以我可以在 markdown 中使用 React 组件。 That's fine.没关系。

I have components, talking to each other.我有组件,互相交谈。 To do this I use the Lifting State Up Pattern .为此,我使用Lifting State Up Pattern That's fine.没关系。

Here is a basic Counter example, to show my proof of concept code.这是一个基本的 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>
}

With this setup, one can use the components like this通过此设置,可以使用这样的组件

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

or even like this甚至像这样

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

That's fine.没关系。

In real-world, the enclosing Counter component (state holder), will be something like a Layout component.在现实世界中,封闭的 Counter 组件(状态持有者)将类似于 Layout 组件。 The <Layout> is used in a template and renders the MDX pages. <Layout>在模板中使用并呈现 MDX 页面。 This looks like that:这看起来像这样:

<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>

The <UpdateButton> (in real-world something like <AddToCartButton> ) is on the MDX page and not anymore a direct child from the <Layout> component. <UpdateButton> (在现实世界中类似于<AddToCartButton> )位于 MDX 页面上,不再是<Layout>组件的直接子级。

The pattern does not work anymore.该模式不再起作用。

How can I resolve this?我该如何解决这个问题?

Thanks all谢谢大家

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>
  )
}

Use it like this像这样使用它

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM