【问题标题】:React - Proper way to render dynamic content?React - 呈现动态内容的正确方法?
【发布时间】:2019-09-16 16:50:17
【问题描述】:

我想通过向其注入组件来使模态视图具有动态内容。

class RootView extends Component {
    state = {
        modalBody: null
    }

    setModalBody = (body) => {
        this.setState({modalBody: body})
    }

    render() {
        return(<ContextProvider value={this.setModalBody}><Modal>{this.state.modalBody}</Modal></ContextProvider>)
    }
}

然后在任何子视图中我使用setState 更改父modalBody

每个路由都可以设置modalBody,也就是说modalBody可以是input列表、selection列表或者纯文本。所以modalBody 必须有自己的状态来控制这些输入。

这样渲染正常,但是状态改变后动态内容无法更新。父动态内容无法接收到 ChildView 新状态,重新渲染后我必须一次又一次地setModalBody

例如,如果 modalBody 中的输入发生变化,则无法更新父级。

class ChildView extends Component {
    state = {
        inputValue: null
    }

    handleChange = (e) => {
        this.setState({inputValue: e.target.value})
    }


    setModalBody(body) {
        this.props.context.setModalBody(<input value={this.state.inputValue} onChange={this.handleChange} />)
    }

    render() {
        return(<Modal>{this.state.modalBody}</Modal>)
    }
}

完整代码:https://codesandbox.io/s/lp5p20mx1m 将动态内容呈现给父级的任何正确方法?

【问题讨论】:

标签: reactjs state dynamic-content


【解决方案1】:

当您可以将Modal 制作成一个简单的可重用child 组件时,我不确定您为什么需要创建父Modal 组件。

请参阅here,了解有关如何实现控制子模态的有状态父级的详细说明。


但是,如果您必须有一个父 Modal 组件,那么您可以创建一个 render prop 来传递 props 以供其 children 使用。

工作示例

components/Modal.jsparent 组件——它有很多较小的组件,为了可重用性和易于理解而分开——它们基本上很简单divs附上一些styles -- 请参阅下面的注释)

import React, { Fragment, Component } from "react";
import PropTypes from "prop-types";
import BackgroundOverlay from "../BackgroundOverlay"; // grey background
import ClickHandler from "../ClickHandler"; // handles clicks outside of the modal
import Container from "../Container"; // contains the modal and background
import Content from "../Content"; // renders the "children" placed inside of <Modal>...</Modal>
import ModalContainer from "../ModalContainer"; // places the modal in the center of the page


class Modal extends Component {
  state = { isOpen: false };

  handleOpenModal = () => {
    this.setState({ isOpen: true });
  };

  handleCloseModal = () => {
    this.setState({ isOpen: false });
  };


  // this is a ternary operator (shorthand for "if/else" -- if cond ? then : else)
  // below can be read like: if isOpen is true, then render the modal,
  // else render whatever the child component is returning (in this case,
  // initially returning an "Open Modal" button)
  render = () =>
    this.state.isOpen ? (
      <Container>
        <BackgroundOverlay />
        <ModalContainer>
          <ClickHandler
            isOpen={this.state.isOpen}
            closeModal={this.handleCloseModal}
          >
            <Content>
              {this.props.children({
                isOpen: this.state.isOpen,
                onCloseModal: this.handleCloseModal,
                onOpenModal: this.handleOpenModal
              })}
            </Content>
          </ClickHandler>
        </ModalContainer>
      </Container>
    ) : (
      <Fragment>
        {this.props.children({
          isOpen: this.state.isOpen,
          onCloseModal: this.handleCloseModal,
          onOpenModal: this.handleOpenModal
        })}
      </Fragment>
    );
}

// these proptype declarations are to ensure that passed down props are 
// consistent and are defined as expected
Modal.propTypes = {
  children: PropTypes.func.isRequired // children must be a function
};

export default Modal;

components/Example.jschild 组件接受来自parentisOpenonCloseModalonOpenModal——使用这种方法,您会注意到,有重复isOpen 逻辑。虽然这种方法可以让您完全控制父级,但它是重复的。但是,您可以通过将“打开模式”按钮逻辑移动到父级来简化组件,并将&lt;Modal btnTitle="Open Modal"&gt; 之类的道具传递给让它有点灵活,但是isOpenfalse 时,你仍然会失去对最初呈现的内容的一些控制。)

import React, { Fragment } from "react";
import Modal from "../Modal";
import "./styles.css";

const Example = () => (
  <div className="example">
    <h2>Parent Modal Example</h2>
    <Modal>
      {({ isOpen, onCloseModal, onOpenModal }) =>
        isOpen ? (
          <Fragment>
            <h1 className="title">Hello!</h1>
            <p className="subtitle">There are two ways to close this modal</p>
            <ul>
              <li>Click outside of this modal in the grey overlay area.</li>
              <li>Click the close button below.</li>
            </ul>
            <button
              className="uk-button uk-button-danger uk-button-small"
              onClick={onCloseModal}
            >
              Close
            </button>
          </Fragment>
        ) : (
          <button
            className="uk-button uk-button-primary uk-button-small"
            onClick={onOpenModal}
          >
            Open Modal
          </button>
        )
      }
    </Modal>
  </div>
);

export default Example;

【讨论】:

  • 我要的是动态的内容,不是静态的,也就是说父组件中的Modal只是一个包装器,每个路由都可以设置ModalBody,也就是说modal body可以是input列表, selection 仅列出或文本。所以模态体必须有自己的状态来控制这些输入类型,这可以通过将其传递给父级来完成。子状态已更改,但父状态无法更新。关闭和隐藏是标准方法,我已经做到了。
  • 请再读一遍。内容不是静态的。模态渲染您传递给它的任何内容。如果需要,您可以使用 state 渲染子 class 组件。您还可以通过&lt;Modal prop={prop}&gt;props 传递给父级。同样,它是一个可重用的组件。如果这不是您想要的,请提供一个 mvce:stackoverflow.com/help/mcve(codesandbox 是理想的)。
  • 我读过你的代码,它是静态的,因为你导入了所有模式类型以在Example 中使用,你只有四种类型的内容:Modal, Hello, Form, List。那么如果我想让 Example 用 selection box, input 组合来渲染其他复杂的组件,我必须在使用它之前导入它?!。动态内容意味着它将在不知道它是动态的情况下传递,该组件是从任何子组件生成的,可以使用任何类型而无需从父组件导入。
  • 您不必import 子组件来显示它。您可以定义children 内联。您唯一需要导入的是可重用的ModalSimpleModal 父组件并在component 中使用它。例如,我更新了代码框以包含一个 SimpleForm 组件,该组件定义了一个 form 内联。也就是说,如果您的目标是即时生成此SimpleForm,我认为这是不可能的和/或不切实际的。此外,模态不应该知道子状态。模态应该只关心打开和关闭。
  • 你可以在这里测试我的代码codesandbox.io/s/lp5p20mx1m这是动态内容。根视图的模态内容可以随时随地注入。您重用了整个模态 - 我正在做两个,我认为它将有更多重复的代码,所以我希望任何子视图从根视图继承模态包装器而不做更多浪费的代码,如显示/隐藏模态......只有一个模态一个应用程序的实例。
猜你喜欢
  • 2013-02-20
  • 1970-01-01
  • 2020-02-19
  • 1970-01-01
  • 2022-01-15
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多