javascript - React/Gatsby 组件交互(提升状态)与 MDX 文件中的组件

标签 javascript reactjs react-state

我将 Gatsby 与 MDX 插件结合使用。所以我可以在 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 组件。 <Layout>在模板中使用并呈现 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>

<UpdateButton> (在现实世界中类似 <AddToCartButton> )在 MDX 页面上,不再是 <Layout> 的直接子代组件。

该模式不再有效。

我该如何解决?

谢谢大家

最佳答案

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>

关于javascript - React/Gatsby 组件交互(提升状态)与 MDX 文件中的组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63235149/

相关文章:

javascript - 如何在react/javascript中处理换行符<br>并将其转换为html

Reactjs map 函数 - 迭代 json 对象

javascript - 没有立即收到 Prop

javascript - 使用 JS ES6 播放(点击)带有自定义叠加层的 wistia 视频

javascript - 指令链接函数中的 "Scope"是根作用域,而不是指令作用域

javascript - 警报后窗口重定向到特定位置

javascript - 带有 Webpack 的 Typescript 中的环境变量

javascript - 使用 jscript 隐藏/显示动态表中的行

javascript - 从 firebase 一次设置多个变量的状态

reactjs - 由于在它们之前返回,因此无法看到条件渲染