【问题标题】:React props/state not passing to modal window反应道具/状态不传递到模态窗口
【发布时间】:2018-09-02 07:06:55
【问题描述】:

我的第一个 StackOverflow 问题,困扰了我大约 4 个小时!

我正在构建一个包含图片库部分的反应页面。在桌面站点上,当用户单击图像时,它会启动一个 Bootstrap 模式弹出窗口,其中应显示单击的绘画的详细信息(标题、大小、类型和媒体)和更大的图像。

我已经让模态框工作了(不需要像 react-bootstrap 这样的特殊库),但是无论单击哪个图像,模态框都只显示集合中第一张图像的图像和详细信息。

我尝试了各种 props 和 state 的排列,尝试了函数式和 class 组件,但似乎没有任何效果。

奇怪的是,如果我查看 chrome 开发工具 react DOM 中的每个组件,每个图像的道具都设置正确,即使在呈现模式的类中也是如此……但在实际呈现的页面中,它不起作用.

在我的代码中,我们有一个 Gallery 组件,包含许多 Holder 组件,每个 Holder 包含一个 PortfolioItem,而 PortfolioItem 包含一个名为 More 的组件,用于处理模态。由于每个 Holder 组件都定义了道具,因此这些道具会被传递给每个 PortfolioItem(当我在图库中获取正确的图像和细节时,它会起作用),我只希望这些相同的道具显示在模态中。

import React from 'react';
import ReactDOM from 'react-dom';


class PortfolioItem extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        url: props.url,
        title: props.title,
        type: props.type,
        size: props.size,
        media: props.media
    };
}

render() {
    return (
        <div className="ot-portfolio-item">
            <More url={this.state.url} title={this.state.title} type={this.state.type} size={this.state.size}
                  media={this.state.media}/>
            <figure className="effect-border">
                <img src={this.state.url} className="img-responsive portfolio-img" alt={this.state.title}/>
                <figcaption>
                    <h2 className="paintingTitle">{this.state.title}</h2>
                    <p className="paintingDetail">{this.state.type}, {this.state.size}, {this.state.media}</p>
                    <a data-toggle="modal" data-target="#Modal"> </a>
                </figcaption>
            </figure>
        </div>
    );
}
}

class More extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        url: props.url,
        title: props.title,
        type: props.type,
        size: props.size,
        media: props.media
    };
}

render() {
    return (
        <div className="modal fade" id="Modal" aria-labelledby="Modal-label">
            <div className="modal-dialog">
                <div className="modal-content">
                    <div className="modal-header">
                        <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span
                            aria-hidden="true">&times;</span></button>
                        <h4 className="modal-title">{this.state.title}</h4>
                    </div>
                    <div className="modal-body">
                        <img src={this.state.url} alt={this.state.title} className="img-responsive"/>
                        <div className="modal-works">
                            <span>{this.state.type}</span>
                            <span>{this.state.size}</span>
                            <span>{this.state.media}</span>
                        </div>
                    </div>
                    <div className="modal-footer">
                        <button type="button" className="close btn btn-default" data-dismiss="modal"
                                aria-label="Close">Close
                        </button>
                    </div>
                </div>
            </div>
        </div>
    );
}
}


const Holder = (props) => {
return (
    <div className="col-sm-6 col-md-4 col-0-gutter holder">
        <PortfolioItem url={props.url} title={props.title} type={props.type} size={props.size} media={props.media}/>
    </div>
);
};

const Gallery = () => {
return (
    // When adding a new painting, just copy a <Holder/> in correct orientation section, and change the props!
    <div>
        {/* Landscape */}
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/girl.jpg" title="#Disengaged" type="Acrylic"
                size="24 by 16 inches" media="Board"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/swimming.jpg" title="Submerge" type="Acrylic"
                size="24 by 16 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/staithes.jpg" title="Staithes 2015" type="Acrylic"
                size="24 by 16 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/ladies.jpg" title="Nederlandes Meisjes" type="Acrylic"
                size="24 by 16 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/park.jpg" title="Family Stroll" type="Acrylic"
                size="24 by 16 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/mexican.jpg" title="Tree Of Life" type="Acrylic"
                size="12 by 10 inches" media="Canvas"/>

        {/* Square */}
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/face.jpg" title="Attitudes Of Colour" type="Acrylic"
                size="19.5 by 19.5 inches" media="Board"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/lee.jpg" title="Lee" type="Acrylic"
                size="19.5 by 19.5 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/boat.jpg" title="Drift Away" type="Acrylic"
                size="12 by 12 inches" media="Board"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/cindyself.jpg" title="Prizeworthy" type="Acrylic"
                size="19.5 by 19.5 inches" media="Canvas"/>

        {/* Portrait */}
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/diving.jpg" title="Ascent" type="Acrylic"
                size="32 by 40 inches" media="Ply"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/bluehair.jpg" title="Untitled" type="Acrylic"
                size="10 by 12 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/beach.jpg" title="Namaste" type="Acrylic"
                size="19.5 by 23.5 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/danielle.jpg" title="Concrete Jungle" type="Acrylic"
                size="19.5 by 23.5 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/africa.jpg" title="Untitled" type="Acrylic"
                size="19.5 by 23.5 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/sealady.jpg" title="Aqua Blue" type="Acrylic"
                size="32 by 40 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/violinman.jpg" title="Nightfall" type="Acrylic"
                size="11 by 14 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/indiangirl.jpg" title="Transition" type="Acrylic"
                size="19.5 by 23 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/indianboys.jpg" title="Backpacker's Tales" type="Acrylic"
                size="32 by 40 inches" media="Canvas"/>
        <Holder url="https://dgtqqzvj8521c.cloudfront.net/indianwoman.jpg" title="Journey" type="Acrylic"
                size="19.5 by 23.5 inches" media="Board"/>
    </div>
);
};

const App = () => {
return (
    <Gallery/>
);
};

ReactDOM.render(<App/>, document.getElementById('root'));

任何指针将不胜感激!

【问题讨论】:

  • 不回答您的问题,而是对您的代码发表评论。不要在组件中使用组件状态,只需使用this.props.,因为您不会在任何地方更改状态。
  • 谢谢,是的,我最初确实是这样做的,我认为状态的使用只是我在尝试解决问题时尝试的东西。

标签: javascript reactjs ecmascript-6


【解决方案1】:

您正在将组件的 props 保存到其状态中 - 这违反了 React 的哲学(维护单一事实来源)并且会经常导致错误。

当一个 React 组件的 props 或它的状态发生变化时,它会重新渲染,但构造函数不会重新运行。采用这个简化的组件:

class Simple extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: props.name
    }
  }

  render() {
     return (
       <div>{this.state.name}</div>
     );
  }
}

当我们最初渲染&lt;Simple name="Alex" /&gt; 然后“Alex”被设置为状态时,组件渲染并且一切看起来都很好。当它变为&lt;Simple name="Steve" /&gt; 时会发生什么? props 更新,因此组件重新渲染。不幸的是,组件 state 没有改变,所以渲染的输出是一样的。

我们如何解决这个问题?我们保持一个事实来源。这意味着任何一条信息都存储在应用程序中的一个位置,而且只有一个位置。在 Simple 组件的情况下,这个地方在 props 中。我们应该重构它以使用 render 方法中的道具:

class Simple extends React.Component {   
  render() {
     return (
       <div>{this.prop.name}</div>
     );
  }
}

现在我们已经做到了,它甚至不再需要成为一个类:

const Simple = ({ name }) => (
  <div>{name}</div>
);

您现在应该进行此重构,因为它会在现在或将来破坏您的应用程序。

查看您的具体问题,问题很可能是您最终得到多个 id 为“#Modal”的 div。您创建的每个 More 元素都会创建一个具有该 ID 的 div。要将您的代码简化为更简单的形式,让我们使用:

const PortfolioItem = ({ name, url }) => (
  <div>
    <a data-toggle="modal" data-target="#Modal"> </a>
    <More name={name} url={url}
  </div>
);

const More = ({ name, url }) => (
  <div id="Modal">
    <a href={url}>{name}</a>
  </div>
);

我们真正需要的是为每个模态设置一个唯一的 id,这样就不会混淆哪个被触发了。我们可以简单地通过使用 name 属性作为 id 来做到这一点:

const PortfolioItem = ({ name, url }) => (
  <div>
    <a data-toggle="modal" data-target={name}> </a>
    <More name={name} url={url}
  </div>
);

const More = ({ name, url }) => (
  <div id={name}>
    <a href={url}>{name}</a>
  </div>
);

您也许可以找到更合适的道具用作 ID,或者您可以使用使用数组索引或随机字符的解决方案。确切的 id 无关紧要 - 它们只需要对于每对 PortfolioItem 和 More 都是唯一的。

同样值得注意的是,这不是在 React 中处理模态的惯用方式。通常,模式的打开/关闭状态存储在 React 本身中,并使用单击处理程序进行处理。如果您想在这条道路上走得更远,那么 react-modal 库是一条很好的道路。

【讨论】:

  • 谢谢!这是一个非常有用的演练。我已经学习 React 大约三天了,所以这些概念开始变得有意义了。当这个用例再次出现时,我会检查 react-modal。
【解决方案2】:

您的问题可能归结为您正在渲染 &lt;More /&gt; 组件并且在模态的 id 内始终相同。

因此,您的行 &lt;a data-toggle="modal" data-target="#Modal"&gt; &lt;/a&gt; 将始终以 id="Modal" 为目标。我怀疑您呈现的 html 实际上是无效的,因为您的 id 在页面上必须是唯一的。

如果您更改每个渲染模式的 ID,然后确保 data-target 链接到正确的,那么它应该可以工作。

【讨论】:

    【解决方案3】:

    尝试从每个组件中删除这种块代码,因为您不需要:

    ```js

    constructor(props) {
        super(props);
        this.state = {
            url: props.url,
            title: props.title,
            type: props.type,
            size: props.size,
            media: props.media
        };
    }
    

    ```

    举例:

    render() {
    let {url, title, size, type, media} = this.props;
        return (
            <div className="ot-portfolio-item">
                <More url={url} title={title} type={type} size={size}
                      media={media}/>
                <figure className="effect-border">
                    <img src={url} className="img-responsive portfolio-img" alt={title}/>
                    <figcaption>
                        <h2 className="paintingTitle">{title}</h2>
                        <p className="paintingDetail">{type}, {size}, {media}</p>
                        <a data-toggle="modal" data-target="#Modal"> </a>
                    </figcaption>
                </figure>
            </div>
        );
    }
    

    在您的组件中直接使用prop.url。让我们看看。

    干杯!

    【讨论】:

      【解决方案4】:

      Modal 必须只渲染一次!然后您将数据传递给该 Modal onClick

      class App extends React.Component {
       state = {
        data: {},
       }
      sendData = (data) => {
       this.setState({ data });
      }
      
      render() {
       return (
        <div>
          <More data={this.state.data} />
          <Gallery sendData={this.sendData} />
        </div>
       );
       }
      }
      

      【讨论】:

        【解决方案5】:

        当您使用标签时,您不必使用 datta-toggle,而是使用 href(从引导文档中获取)

        注意:对于元素,省略 data-target 并使用 href="#modalID" 代替

        【讨论】:

          猜你喜欢
          • 2019-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-22
          • 2018-01-13
          • 2019-11-30
          • 2018-03-08
          • 2018-07-10
          相关资源
          最近更新 更多