反应道具/状态未传递到模态窗口 [英] React props/state not passing to modal window

查看:37
本文介绍了反应道具/状态未传递到模态窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有史以来第一个StackOverflow问题,我的脑袋已经动了大约4个小时了!

my first ever StackOverflow question, having been banging my head for about 4 hours!

我正在构建一个具有图像库部分的反应页面.在桌面站点上,当用户单击图像时,它将启动Bootstrap模态弹出窗口,该弹出窗口应显示所单击的绘画的详细信息(标题,大小,类型和媒体)和较大的图像.

I am building a react page that has an image gallery section. On the desktop site, when the user clicks on an image, it launches a Bootstrap modal pop up that should show the details of the clicked painting (title, size, type and media) and a larger image.

我已经使用了模态(不需要任何特殊的库,例如react-bootstrap),但是无论单击哪个图像,模态都只显示图像和集合中第一个图像的细节.

I have got the modal working (without needing any special library like react-bootstrap) but whichever image is clicked, the modal only shows the image and details of the first image in the set.

我尝试了道具和状态的各种排列,尝试了功能和类组件,但似乎没有任何效果.

I have tried all sorts of permutations of props and state, trying functional and class components, and nothing seems to work.

奇怪的是,如果我查看chrome dev工具中的每个组件对DOM的反应,则即使在渲染模式的类中,每个图像的道具也已正确设置...但是在实际渲染的页面中,它不起作用

Weirdly, if I look at each component in the chrome dev tools react DOM, the props for each image are set correctly, even in the class that renders the modal...but in the actual rendered page, it does not work.

在我的代码中,我们有一个Gallery组件,其中包含许多Holder组件,每个Holder包含一个PortfolioItem,而PortfolioItem包含一个名为More的组件,用于处理模态.当每个Holder组件定义道具时,这些道具将传递到每个PortfolioItem(当我在图库中获取正确的图像和详细信息时,它们会起作用),而我只希望这些相同的道具显示在模式中.

In my code, we have a Gallery component, containing many Holder components, each Holder contains a PortfolioItem, and the PortfolioItem contains a component called More that deals with the modal. As each Holder component defines props, these are passed down to each PortfolioItem (which works as I am getting the correct images and details in the gallery) and I just want those same props to show up in the modal.

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'));

任何指针将不胜感激!

推荐答案

您正在将组件的props保存到其状态-这违反了React的哲学(保持单一真相),并且经常会导致bug.

You are saving the component's props into its state - this is against a philosophy of React (maintaining a single source of truth) and will frequently cause bugs.

React组件的props或状态改变时将重新渲染,但不会重新运行构造函数.使用这个简化的组件:

A React component will re-render when either its props or its state changes, but the constructor will not be rerun. Take this simplified component:

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

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

当我们最初呈现< Simple name ="Alex"/> 时,则将"Alex"设置为状态,该组件将呈现并且一切看起来都很好.如果将其更改为<简单名称="Steve"/> ,那么会发生什么?道具会更新,因此该组件会重新渲染.不幸的是,组件的状态没有改变,因此呈现的输出是相同的.

When we initially render <Simple name="Alex" /> then "Alex" is set to the state, the component renders and everything seems fine. When this changes to <Simple name="Steve" /> then what happens? The props update, so the component re-renders. Unfortunately, the component state has not changed, so the rendered output is the same.

我们该如何解决?我们维护真理的一种来源.这意味着任何一条信息都存储在应用程序的一个位置中,并且只有一个位置.对于简单组件,此位置在道具中.我们应该重构它以在render方法中使用props:

How do we fix this? We maintain one source of truth. This means that any piece of information is stored in one, and only one, place in the application. In the case of the Simple component, this place is in the props. We should refactor it to use the props in the render method:

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

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

Now that we've done that it doesn't even need to be a class any more:

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

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

You should do this refactoring now, because it will break your app either now or in the future.

考虑到您的特定问题,该问题很可能以ID为"#Modal"的多个div结束.您创建的每个More元素都会创建一个具有该ID的div.要将代码分解为更简单的形式,请使用:

Looking at your specific problem, the issue is most likely that you end up with multiple divs with the id "#Modal". Every More element you create creates a div with that id. To distil your code down to a simpler form, let's use:

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,因此不会混淆哪个模式被触发.我们可以简单地通过使用名称prop作为id来做到这一点:

What we really need is to have a unique id for each modal, so there is no confusion as to which one gets triggered. We can do this simply by using the name prop as an 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>
);

您可能可以找到更合适的prop用作ID,或者可以使用使用数组索引或随机字符的解决方案.确切的ID无关紧要-对于每对PortfolioItem和更多对,它们都必须是唯一的.

You may be able to find a more appropriate prop to use as the ID, or you could use a solution that uses array indices, or random characters. The exact ids don't matter - they just need to be unique for each pair of PortfolioItem and More.

还值得注意的是,这不是在React中处理模态的惯用方式.通常,模态的打开/关闭状态存储在React自身中,并使用点击处理程序进行处理.如果您想沿着这条路走得更远,那么react-modal库是一条理想的走之路.

It is also worth noting that this is not an idiomatic way of handling modals in React. Usually the open/closed state of the modal is stored within React itself, and handled using click handlers. If you want to go more down this path, then the react-modal library is an excellent path down which to go.

这篇关于反应道具/状态未传递到模态窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆