停止在React onclick处理程序中传播

时间:2019-02-14 21:05:14

标签: reactjs onclick propagation

我的反应传播存在问题。 我使用此方法在

内呈现带有p标签的div标签。
private renderTags(tag: Tags, index: number) {
    return <div>
        <div  onClick={(e) => { e.stopPropagation(); this.collectTags(tag); }}>
            <p className={styles.tag}># {tag.title} <i className="ms-Icon ms-Icon--CirclePlus"></i></p>
        </div>
    </div>

}

该方法从渲染器中被调用,如下所示:

<div className={styles.tagsContainer}>
                {this.state.items.slice(0, 12).map((w, index) => this.renderTags(w, index))}
            </div>

如您所见,renderTags方法调用了数组中的每个项目。

第一种方法的想法是,当用户单击将元素发送到数组的一个元素时,问题是当我单击这些元素中的一个时,所有元素都会发送到数组。我通过将类名添加到clicked元素中进行测试(仅出于检查原因),当我单击其中一个元素时,所有元素都获得类名,我可以看到出现了相同的行为。

如何停止这种传播?顺便说一下,这是听到点击并将其放置在数组中的方法:

 private collectTags(newTag: Tags): any {
    //this.setState({ savingSettings: true, tagActive: true });

    let selectedTags: Tags[] = this.state.selectedTags;

    selectedTags.push(newTag);
    this.setState({
        selectedTags: selectedTags,
        hideSaveButton: false
    });


    return selectedTags;

}

更新

更好地发布整个代码:

    import * as React from 'react';
import { CacheManager } from "../../common/CacheManager";
import { ITagsDataProvider } from "../../interfaces/ITagsDataProvider";
import Tags from "./Tags";
import styles from './TagsContainer.module.scss';

import { Dialog, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';

export interface ITagsContainerProps {
    provider: ITagsDataProvider;
}

export interface ITagsContainerState {
    items: Tags[];
    allTags: Tags[];
    selectedTags: Tags[];
    savingSettings: boolean;
    currentTagsIndex: number;
    activeTile: number;
    hideDialog: boolean;
    hideSaveButton: boolean;

}

export default class TagsContainer extends React.Component<ITagsContainerProps, ITagsContainerState> {
    private readonly cacheKey = "TagsLinks";

    constructor(props: ITagsContainerProps) {
        super(props);
        this.state = {
            items: [],
            allTags: [],
            selectedTags: [],
            savingSettings: false,
            currentTagsIndex: -1,
            activeTile: -1,
            hideDialog: true,
            hideSaveButton: true
        }
    }

    public componentDidMount(): void {
        var cacheManager = new CacheManager();
        var cachedValue = cacheManager.get(this.cacheKey);

        //If there are cached values update the state
        if (cachedValue) {
            this.setState({
                items: cachedValue,
                allTags: [],
                savingSettings: false,
                currentTagsIndex: -1
            });

            return;
        }

        this.props.provider.getAllTags().then((tags) => {
            if (tags != null) {
                cacheManager.set(this.cacheKey, tags);
            }

            this.setState({
                items: tags,
                allTags: [],
            });
        });



    }



    private renderTags(tag: Tags, index: number) {
        return <div>
            <div  onClick={(e) => this.onTagClick(tag, e)}>
                <p className={styles.tag}># {tag.title} <i className="ms-Icon ms-Icon--CirclePlus"></i></p>
            </div>
        </div>

    }

    private onTagClick(tag: Tags, e: React.MouseEvent<HTMLDivElement>) {
        e.stopPropagation();
        this.collectTags(tag);
    }

    private collectTags(newTag: Tags): any {

        this.setState({
            selectedTags: {
                ...this.state.selectedTags,
                newTag
            },
            hideSaveButton: false
        });


    }


    private saveSettings(): void {

         let sTags = this.state.selectedTags;

        this.setState({
            items: sTags
        });
        console.log('SELECTED TAG ' + this.state.items);
       var cacheManager = new CacheManager();
        cacheManager.set(this.cacheKey, sTags);

        this.props.provider.saveSettingsData(sTags).then(() => {
            this.setState({
                savingSettings: false
            });
        });

    }


    // Render the tags in the dialog box
    private onRenderDialog = (tag: Tags, index: number): JSX.Element => {
        return (
            <div className={styles.tag} onClick={(e) => { e.stopPropagation(); this.collectTags(tag); }}>
                <span># {tag.title} <i className="ms-Icon ms-Icon--CirclePlus"></i></span>
            </div>
        )
    }

    public render(): JSX.Element {
        return <div className={styles.tagCloud}>
            <div>
                <h1>What are you interested in?</h1>
                <p>We'll show you more stories from the topics you pick below</p>
            </div>
            <div>
                <div className={styles.tagsContainer}>
                    {this.state.items.slice(0, 12).map((t, index) => this.renderTags(t, index))}
                </div>
                <div>
                    <a className={styles.allItemsLink} href="#" onClick={this._showDialog}>View all topcis</a>
                </div>
                <div>

                   { this.state.hideSaveButton === false ? <DefaultButton 
                        text="Done"
                        style={{ backgroundColor: '#ff0033', color: '#ffffff' }}
                        onClick={(e) =>{e.stopPropagation(); this.saveSettings()}}
                    /> : null} 

                </div>
            </div>

            <Dialog
                hidden={this.state.hideDialog}
                onDismiss={this._closeDialog}
                containerClassName={'ms-dialogMainOverride ' + styles.textDialog}
                modalProps={{
                    isBlocking: true,
                }}>

                <div className={styles.tagsDialogContainer}>
                    {this.state.allTags.map((t, index) => this.onRenderDialog(t, index))}
                </div>
                <DialogFooter>
                    <DefaultButton
                        style={{ backgroundColor: '#ff0033', color: '#ffffff' }}
                        onClick={this._closeDialog} 
                        text="Done"
                    />
                </DialogFooter>
            </Dialog>
        </div>
    }

    private _showDialog = (): void => {
        this.setState({ hideDialog: false });

        this.props.provider.getAllTags().then((items) => {
            this.setState({ allTags: items });
        })
    };

    private _closeDialog = (): void => {
        this.setState({ hideDialog: true });

    }
}

最好的问候 美国

1 个答案:

答案 0 :(得分:0)

首先,您需要创建单独的事件处理方法,例如onTagClick

private renderTags(tag: Tags, index: number) {
    return <div>
        <div onClick={e => this.onTagClick(e, tag)}>
            <p className={styles.tag}># {tag.title} 
                <i className="ms-Icon ms-Icon--CirclePlus"></i>
            </p>
        </div>
    </div>

}

private onTagClick(tag: Tags, e: React.MouseEvent<HTMLElement>) {
    e.stopPropagation(); 
    this.collectTags(tag);
}

另一个问题-您正在直接更改状态,这在React中是不允许的。

// Here you creating the link to array named `selectedTags`.
let selectedTags: Tags[] = this.state.selectedTags;

// and here you mutating your state directly
selectedTags.push(newTag);

只需在添加新项目或使用价差运算符之前复制阵列即可。

private collectTags(newTag: Tags): any {
    this.setState({
        selectedTags: {
            ...this.state.selectedTags,
            newTag
        },
        hideSaveButton: false
    });
}

此外,不要忘记将上下文绑定到构造函数中的collectTags方法。

constructor(props) {
    super(props);
    ...some code if you have...
    this.collectTags = this.collectTags.bind(this);
}

希望有帮助。