我创建了两个组件,一个是Stateful(CurrentProjects),另一个是无状态(PopUpPanel)。在无状态组件中,我使用了Office UI Fabric反应组件Panel
。
Panel有一个属性OnDismiss,当单击面板上的关闭图标时会触发该属性。我通过CurrentProject组件将函数作为属性传递给PopUpPanel组件。但是,当我单击关闭图标时,CurrentProject组件上的函数将被触发两次。
所以我的问题是它为什么要两次调用它。我的代码如下
CurrentProjects组件
import * as React from 'react';
import {
DocumentCard,
DocumentCardActions,
DocumentCardActivity,
DocumentCardLocation,
DocumentCardPreview,
DocumentCardTitle,
IDocumentCardPreviewProps,
IDocumentCardActivityProps,
Panel,
PanelType,
Image,
Persona,
IPersonaProps,
PersonaSize
} from 'office-ui-fabric-react';
import styles from './CurrentProjects.module.scss';
import { ICurrentProjectsProps } from './ICurrentProjectsProps';
import { ICurrentProjectsState } from './ICurrentProjectsState';
import { escape, } from '@microsoft/sp-lodash-subset';
import { ImageFit } from 'office-ui-fabric-react/lib/Image';
import { TestImages } from "../../../../common/TestImages";
import { IProject } from '../IProject';
import PopUpPanel from "../PopUpPanel/PopUpPanel";
import IPopUpPanelProps from "../PopUpPanel/IPopUpPanelProps";
const img = require('../../../../images/avatar-kat.png');
export default class CurrentProjects extends React.Component<ICurrentProjectsProps, ICurrentProjectsState> {
public constructor(props:ICurrentProjectsProps){
super(props);
this.state = {
showPanel:false,
currentProject: null
};
this.onDismiss = this.onDismiss.bind(this);
}
private onCardClick(id:number, event:React.SyntheticEvent<HTMLElement>):void{
//console.log("event=>",event.currentTarget);
//console.log("Id=>",id);
//debugger;
this.setState((prevState:ICurrentProjectsState)=>{
const project = this.props.projects.filter(proj=>proj.ID===id)[0];
return {
showPanel: !prevState.showPanel,
currentProject: project
};
});
}
private onDismiss():void{
//debugger;
this.setState((prevState:ICurrentProjectsState)=> {
return {
showPanel: false //!prevState.showPanel
};
});
}
public render(): JSX.Element {
const project:IProject = this.state.currentProject;
const previewProps: IDocumentCardPreviewProps = {
previewImages: [
{
name: 'My Project',
previewImageSrc: TestImages.documentPreview,
imageFit: ImageFit.contain,
width: 250,
height: 100
},
],
};
const element:JSX.Element[] = this.props.projects === undefined ? []: this.props.projects.map((project,index) => {
const style = {
width: project.PercentageComplete + "%",
};
let dueDate: string = "";
if(project.DueDate)
{
const dueDateObj = new Date(project.DueDate.toString());
dueDate = dueDateObj.toLocaleDateString('en-gb',{year: '2-digit', month: 'short', day: 'numeric'});
}
return (
<DocumentCard className={styles.myCard} key={project.ID} onClick={this.onCardClick.bind(this,project.ID)}>
<div className={styles.priorityDiv + " " + styles[this.getProjectPriorityClassName(project.Priority)]}>
<DocumentCardPreview { ...previewProps } />
<div className={styles.projectDetails}>
<DocumentCardTitle
title={project.Client}
shouldTruncate={ false }
showAsSecondaryTitle={true}
/>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>Title:</span> {project.Title}</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>BU:</span> {project.BusinessUnit}</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}><span className='ms-font-mi'>Due Date:</span> {dueDate}</div>
<div className={styles.seperatorDiv}>
{/* <hr/> */}
<div className={styles.progressBarDiv}><div style={style}></div></div>
<span>{project.PercentageComplete}%</span>
</div>
<div className={styles.detailsDiv + ' ms-fontSize-mi'}>{project.RequestType}</div>
</div>
</div>
</DocumentCard>
);
});
const popUpPanelProps:IPopUpPanelProps = {
project: project,
showPanel: this.state.showPanel,
//onDismiss: this.onDismiss
};
return (
<div className={styles.currentProjects}>
{element}
<PopUpPanel {...popUpPanelProps} onDismiss={this.onDismiss} />
</div>
);
}
private getProjectPriorityClassName(priority:string): string {
switch(priority)
{
case "Low" :
return "priorityLow";
case "Medium" :
case "Moderate" :
return "priorityMedium";
case "High" :
return "priorityHigh";
default:
return "";
}
}
private getProgressBarColor(progress:number): string {
if(progress <= 30) {
return "red";
} else if(progress >30 && progress <=70) {
return "orange";
} else if(progress >70) {
return "green";
}
}
}
PopUpPanel组件
import * as React from 'react';
import {
Panel,
PanelType,
Image,
Persona,
IPersonaProps,
PersonaSize
} from 'office-ui-fabric-react';
import styles from '../ProjectCardBoard/CurrentProjects.module.scss';
import IPopUpPanelProps from './IPopUpPanelProps';
import { escape, } from '@microsoft/sp-lodash-subset';
import { ImageFit } from 'office-ui-fabric-react/lib/Image';
import { TestImages } from "../../../../common/TestImages";
import { IProject } from '../IProject';
const img = require('../../../../images/avatar-kat.png');
const PopUpPanel: React.SFC<IPopUpPanelProps> = (props:IPopUpPanelProps) => {
if(!props.project)
return null;
const project = props.project;
// const onDismiss = ()=> {
// this.props.onDismiss();
// };
return (<Panel
isOpen={ props.showPanel }
type={ PanelType.smallFluid }
onDismiss={ props.onDismiss }>
<div className={styles.ProjectDetailsPanel + " ms-Grid"}>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-md3 ms-lg2 ms-xl2 ms-xxl2 ms-xxxl1">
<Image src={String(img)}
imageFit={ImageFit.cover}
className={styles.ProjectIconDP}
alt='Some Alt' />
</div>
<div className="ms-Grid-col ms-md6 ms-lg8 ms-xl7 ms-xxl8 ms-xxxl9">
<p className="ms-fontSize-s ms-fontWeight-semibold">{project.Client}</p>
<p className="ms-fontSize-mi">{project.Title} <span className="ms-fontSize-s"> | </span> {project.BusinessUnit} <span className="ms-fontSize-s"> | </span>{project.RequestType}</p>
</div>
<div className="ms-Grid-col ms-md3 ms-lg2 ms-xl3 ms-xxl2 ms-xxxl2">
<p>Total Contract Value</p>
<p><strong>{project.TotalContractValue}</strong></p>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Bid Team".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-mdPush5 ms-md2 ms-lg2 ms-xl1 ms-textAlignCenter">
<div className={styles.peopleHeaderDiv}>
<h3 className={styles.peopleHeading}>{"Core Team".toUpperCase()}</h3>
</div>
{project.CoreTeam && project.CoreTeam.map((member)=>{
const personaProps:IPersonaProps = {
imageUrl: member.ImgSrc,
imageInitials: member.Title.split(" ").reduce((prevVal,currentVal,index)=>{
return prevVal += currentVal.charAt(0);
},""),
primaryText: member.Title,
secondaryText: member.Designation,
tertiaryText: member.PhoneNo,
size:PersonaSize.size72
};
return <Persona {...personaProps} />;
})}
<div className={styles.peopleHeaderDiv}>
<h3 className={styles.peopleHeading}>{"Contributors".toUpperCase()}</h3>
</div>
{project.Contributors && project.Contributors.map((member)=>{
const personaProps:IPersonaProps = {
imageUrl: member.ImgSrc,
imageInitials: member.Title.split(" ").reduce((prevVal,currentVal,index)=>{
return prevVal += currentVal.charAt(0);
},""),
primaryText: member.Title,
secondaryText: member.Designation,
tertiaryText: member.PhoneNo,
size:PersonaSize.size72
};
return <Persona {...personaProps} />;
})}
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Reason For Status".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.ReasonForStatus}} />
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Win Strategy".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.WinStrategy}} />
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12">
<h3>{"Key Actions".toUpperCase()}</h3>
<div className={styles.lineSeperator}></div>
</div>
</div>
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-md12"
dangerouslySetInnerHTML={{__html:project.KeyActionsOrNextSteps}} />
</div>
</div>
</Panel>);
};
export default PopUpPanel;
注意:如果我在PopupPanel组件中将行if(!props.project)
更改为if(!props.project || !props.showPanel)
,则只触发一次事件。