【问题标题】:Stop propagation in React onclick handler在 React onclick 处理程序中停止传播
【发布时间】:2019-07-08 23:45:48
【问题描述】:

我在反应中的传播有问题。 我有这种方法可以在其中呈现带有 p-tags 的 div-tags:

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 方法。

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

我怎样才能阻止这种传播?顺便说一句,这是听到点击并将点击的元素放入数组中的方法:

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

    }
}

最好的问候 美国

【问题讨论】:

    标签: reactjs onclick propagation


    【解决方案1】:

    首先你需要为事件处理创建单独的方法,例如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);
    }
    

    希望对您有所帮助。

    【讨论】:

    • 谢谢,但现在我在调用 renderTags 的行中出现错误:{this.state.items.slice(0, 12).map((w, index) => this.renderTags (w, index))} 它说 TypeError: “this.state.items.slice is not a function”,我删除了切片但错误仍然存​​在:-(
    • 你能分享你定义和更新状态中的“项目”的代码部分吗?
    • 私人 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 }); }); }
    • 不知道有没有办法在cmets中插入缩进代码
    • @AmericoPerez 最好更新问题并添加格式化的代码:) 另外,在此处添加您的构造函数。
    猜你喜欢
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 2020-04-09
    • 2020-09-14
    • 2015-04-15
    相关资源
    最近更新 更多