Реагировать - правильный способ визуализации динамического контента? - PullRequest
0 голосов
/ 27 апреля 2019

Я хочу, чтобы модальное представление имело динамическое содержимое, добавляя в него компонент.

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 Любой правильный способ сделать динамический контент родительским?

1 Ответ

1 голос
/ 27 апреля 2019

Я не уверен, зачем вам нужно создавать родительский компонент Modal, когда вы можете сделать Modal простым повторно используемым child компонентом.

См. здесь для подробного объяснения того, как получить родительский элемент, который контролирует дочерний модал.


Однако, если у вас ДОЛЖЕН быть родительский компонент Modal, вы можете создать render prop для передачи props для использования его children.

Рабочий пример :

Edit Parent Modal

компоненты / Modal.js (parent компонент - здесь есть множество более мелких компонентов, которые были разделены для повторного использования и простоты понимания - они в основном простые div с некоторыми 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;

компоненты / Example.js (child компонент принимает isOpen, onCloseModal и onOpenModal от parent - при таком подходе, как вы заметите, есть дубликаты isOpen логика. Хотя этот подход дает вам полный контроль над родителем, он повторяется. Однако вы можете упростить свои компоненты, переместив логику кнопки «Открыть модальные» в родительский объект и передав опору типа <Modal btnTitle="Open Modal">, чтобы сделать это несколько гибко, НО вы все равно потеряете контроль над тем, что изначально отображается, когда isOpen равно false.)

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