【问题标题】:Since updating react-dom and react-router-dom packages: excessive li onClick behavior自从更新 react-dom 和 react-router-dom 包后:li onClick 行为过多
【发布时间】:2019-10-23 20:21:51
【问题描述】:

我继承了一个 react/node/prismic 应用程序,我们需要更新 prismic-reactjs 包,这导致需要更新其他一些包 - 总的来说,我们进行了更改:

棱镜反应:0.2.0 → 1.1.0

反应:15.6.1 → 16.0.0

webpack:3.12.0 → 4.39.2

react-dom: 15.6.1 → 16.0.0

react-router-dom: 4.1.2 → 5.0.1

extract-text-webpack-plugin(已弃用)→ mini-css-extract-plugin

然后由于启动本地服务器时出现新错误而删除了“withRouter()”的一次使用(但我在另一个分支中确认,单独进行该编辑不会产生以下症状)

上下文:我们有一个显示一组人的图标的 personMap,单击其中一个打开 PersonMapStory 模式,显示该人的故事,然后链接到其他人底部的列表。如果您向下滚动并单击其中一个链接,则顶部的故事会相应地替换(使用新人物的故事),下面的列表是其他所有人。在代码中,这些底部链接的按钮 onClick 行为是 setActivePerson() 到这个新人。

新错误:在这个新分支中,当我们单击底部链接时,它会为人员 #2 调用 setActivePerson(),但随后也会再次为人员 #1 调用 setActivePerson()! (从原来的 PersonMap 里返回 onClick 行为)。所以症状是您看到第一个故事,向下滚动并单击其他人的链接,但模式似乎根本没有更新(尽管它实际上更新了两次)。在当前/生产/工作分支中,它只更新一次给新人,就是这样。这种新行为听起来与this onClick 传播问题非常相似。我尝试将 stopPropagation() 添加到适当的方法中,但没有运气。

更多信息:在这个分支中,看起来从该模式内部的任何点击(即使我点击顶部显示的故事)都会调用来自原始人的 SetActivePerson()当 thisPersonIsQueued 为 true 时,PersonMap 行项目 onClick 行为。 (因此,如果我向下滚动并单击一个新人物,我们是 SetActivePerson 人物 #2,紧随其后的是 SetActivePerson 人物 #1,因此模态保持相同的故事并且永远不会更新)。在 PersonMap 状态(在 Chrome 开发人员工具/组件中查看)有一个 queuedUpPatient:通常在这种情况下(在原始/生产代码中)queuedUpPatient 为空,但在这个新分支中,queuedUpPatient 保留 Person #1 的值。 (或已清除并重新设置)。

调试器中的可能线索:在我们为 #2 调用 SetActivePerson 之后(这是在我们的常规/生产分支中发生的最后一步),在我们看到 setActivePerson 调用来自 #1 的人之前它是 onClick 行为,调试器带我通过 react-dom.development.js 中的几个方法,从 callCallback() 的最后一行开始,内部代码用于以下情况(见下文):

// Check that the browser supports the APIs we need to implement our special
// DEV version of invokeGuardedCallback
if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document 

关于可能发生的事情的其他想法以及我如何才能让它再次正常工作以便我们可以继续进行软件包升级?这是地图和模式的两个相关文件(下)。

PersonMap.js:

import Modal from 'react-modal'
import PropTypes from 'prop-types'
import React from 'react'

import PersonMapPoint from './PersonMapPoint'
import PersonMapStory from './PersonMapStory'
import PersonMapCallout from './PersonMapCallout'
import PersonMapLocator from './PersonMapLocator'
import PersonMapBackground from './PersonMapBackground'

const CUSTOM_STYLES = {
    content: {
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: '#fff',
        zIndex: 10,
        border: 'none'
    }
}

class PersonMap extends React.Component {
    constructor(props) {
        super(props)

        this.setActivePerson = this.setActivePerson.bind(this)
        this.setNoActivePerson = this.setNoActivePerson.bind(this)
        this.setQueuedUpPerson = this.setQueuedUpPerson.bind(this)
        this.setNoQueuedUpPerson = this.setNoQueuedUpPerson.bind(this)
        this.checkBlurEvent = this.checkBlurEvent.bind(this)
        this.setIsDesktop = this.setIsDesktop.bind(this)
        this.checkHasBeenScrolledTo = this.checkHasBeenScrolledTo.bind(this)
        this.setTopRef = this.setTopRef.bind(this)
        this.handleKeyPress = this.handleKeyPress.bind(this)

        this.state = {
            activePerson: null,
            queuedUpPerson: null,
            scrollPos: 0,
            isDesktop: false,
            hasBeenScrolledTo: false,
            lastQueuedPerson: null
        }
    }

    componentDidMount() {
        this.setIsDesktop()
        this.checkHasBeenScrolledTo()
        window.addEventListener('resize', this.setIsDesktop)
        window.addEventListener('scroll', this.checkHasBeenScrolledTo)
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.setIsDesktop)
        window.removeEventListener('scroll', this.checkHasBeenScrolledTo)
    }

    setTopRef(element) {
        this.topRef = element
    }

    setActivePerson(personName) {
        this.setState({
            activePerson: personName,
            scrollPos: window.scrollY
        })
        event.stopPropagation()
    }

    setNoActivePerson() {
        this.setState({
            queuedUpPerson: this.state.activePerson,
            activePerson: null
        }, () => {
            setTimeout(() => {
                window.scrollTo(0, this.state.scrollPos)
            }, 50)
        })
    }

    setQueuedUpPerson(personName) {
        this.setState({
            queuedUpPerson: personName,
            lastQueuedPerson: personName
        })
    }

    setNoQueuedUpPerson() {
        this.setState({
            queuedUpPerson: null
        })
        event.stopPropagation()
    }

    handleKeyPress(e, name) {
        if (e.key !== ' ' && e.key !== 'Enter') {
            return
        }
        this.setActivePerson(name)
    }

    checkBlurEvent(e) {
        if (Array.from(e.currentTarget.childNodes[0].childNodes).includes(e.relatedTarget)) {
            return
        }
        this.setNoQueuedUpPerson()
    }

    render() {
        return (
            <section className="slice-area person-map CONSTRAIN">
                <div className="person-map-headers">
                    <div className="person-map-headers-inner">
                        <h1 className="person-map-title">
                            {this.props.title}
                        </h1>
                        <p className="person-map-disclaimer">
                            {this.props.disclaimer}
                        </p>
                    </div>
                </div>
                <div
                    className={`person-map-list-wrap${ this.state.isDesktop ? ' --desktop' : '' }`}
                    ref={this.state.isDesktop && this.setTopRef}
                >
                    { this.state.isDesktop &&
                        <PersonMapBackground isVisible={this.state.hasBeenScrolledTo}/>
                    }
                    <ul className="person-map-list">
                        {
                            this.props.personStories.map((person) => {
                                const thisPersonIsQueued = this.state.queuedUpPerson === person.name
                                const thisPersonIsActive = this.state.activePerson === person.name
                                const thisPersonWasLastQueued = this.state.lastQueuedPerson === person.name

                                return (
                                    <li
                                        key={person.name} className={`person-map-list-item${thisPersonWasLastQueued ? ' --active' : ''}`}
                                        onMouseEnter={this.state.isDesktop ? () => this.setQueuedUpPerson(person.name) : null}
                                        onMouseLeave={this.state.isDesktop ? this.setNoQueuedUpPerson : null}
                                        onFocus={this.state.isDesktop ? () => this.setQueuedUpPerson(person.name) : null}
                                        onBlur={this.state.isDesktop ? this.checkBlurEvent : null}
                                        onClick={thisPersonIsQueued || !this.state.isDesktop ? () => this.setActivePerson(person.name) : null}
                                        onKeyPress={(e) => this.handleKeyPress(e, person.name)}
                                    >
                                        {
                                                <PersonMapLocator
                                                    x={person.x}
                                                    y={person.y}
                                                >
                                        }
                                        <Modal
                                            isOpen={thisPersonIsActive}
                                            onRequestClose={this.setNoActivePerson}
                                            style={CUSTOM_STYLES}
                                        >
                                            <PersonMapStory
                                                name={person.name}
                                                photo={person.photo_url}
                                                story={person.story}
                                                setNoActivePerson={this.setNoActivePerson}
                                                setActivePerson={this.setActivePerson}
                                                isActive={thisPersonIsActive}
                                                otherPersons={this.props.personStories.filter((item) => item.name !== person.name).map((item) => ({ name: item.name, photo: item.photo_url }))}
                                                isDesktop={this.state.isDesktop}
                                            />
                                        </Modal>
                                    </li>
                                )
                            })
                        }
                    </ul>
                </div>
            </section>
        )
    }
}

PersonMap.propTypes = {
    title: PropTypes.string,
    disclaimer: PropTypes.string,
    personStories: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string,
        photo_url: PropTypes.string,
        x: PropTypes.number,
        y: PropTypes.number,
        story: PropTypes.shape({
            title: PropTypes.string,
            link: PropTypes.string,
            content: PropTypes.arrayOf(PropTypes.shape({
                question: PropTypes.string,
                answer: PropTypes.string
            }))
        })
    }))
}
export default PersonMap

PersonMapStory.js:

import PropTypes from 'prop-types'
import React from 'react'

import PersonMapPerson from './PersonMapPerson'


class PersonMapStory extends React.Component {
    constructor(props) {
        console.log("PersonMapStory constructor")
        super(props)

        this.deactivateThisPerson = this.deactivateThisPerson.bind(this)
        this.check = 0
    }

    deactivateThisPerson(backPressed = false) {
        if (!backPressed) {
            history.back()
        }
        console.log("Set no active person ")
        this.props.setNoActivePerson()
    }

    setActivePerson(name) {
        this.props.setActivePerson(name)
        event.stopPropagation()
    }


    componentDidMount() {
        const loc = window.location.pathname.substr(1)
        this.setState({ location: loc })
        const openState = { modalOpen: true }
        history.pushState(openState, 'signup-open', loc)
      }

    render() {
        return (
            <div className={`person-map-story${this.props.isActive ? ' --active' : ''}${this.props.isDesktop ? ' --desktop' : ''}`}>
                <h2 className="person-map-story-title">
                    { this.props.story.title }
                </h2>
                <button className="person-map-story-close-button" onClick={() => {this.deactivateThisPerson(false)}}>
                    <span className="person-map-story-close-button-text">
                        Close Story
                    </span>
                </button>
                <div className="person-map-story-body">
                    <span className="person-map-story-person-wrap">
                        <PersonMapPerson
                            photo={this.props.photo}
                            name={this.props.name}
                        />
                    </span>
                    {
                        this.props.story.content.map((section) => {
                            if (section) {
                                return (
                                    <div key={section.question}>
                                        <h3 className="person-map-story-question">{section.question}</h3>
                                        <p className="person-map-story-answer">
                                            {section.answer}
                                        </p>
                                    </div>
                                )
                            }
                        })
                    }
                    {
                        this.props.story.link &&
                        <a  href={this.props.story.link}
                            target="_blank"
                            className="person-map-story-more-link header-and-text-link"
                        >  Read More Stories
                        </a>
                    }

                    <ul className="person-map-story-other-list">
                        {
                            this.props.otherPersons.map((person) => (
                                <li
                                    key={person.name}
                                    className="person-map-story-other-list-item"
                                >
                                    <button className="person-map-story-other-button" onClick={() => this.setActivePerson(person.name)}>
                                        <PersonMapPerson
                                            name={person.name}
                                            photo={person.photo}
                                            ctaText="View their story"
                                        />
                                    </button>
                                </li>
                            ))
                        }
                    </ul>
                </div>
            </div>
        )
    }
}

PersonMapStory.propTypes = {
    name: PropTypes.string,
    photo: PropTypes.string,
    story: PropTypes.shape({
        title: PropTypes.string,
        link: PropTypes.string,
        content: PropTypes.arrayOf(PropTypes.shape({
            question: PropTypes.string,
            answer: PropTypes.string
        }))
    }),
    setNoActivePerson: PropTypes.func,
    setActivePerson: PropTypes.func,
    isActive: PropTypes.bool,
    otherPersons: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string,
        photo: PropTypes.string
    })),
    isDesktop: PropTypes.bool
}

export default PersonMapStory

【问题讨论】:

  • 您在PersonMapStory setActivePerson() 中有event.stopPropagation()?看起来点击确实在传播,因为 Modal 是 &lt;li&gt; 的子项
  • 感谢安东尼!是的,这太奇怪了——我已经尝试将它添加到 PersonMapStory 的 setActivePerson 以及 PersonMap 的 setActivePerson 和 setNoQueuedUpPerson 中,但它仍在继续第二次调用。我已经更新了上面的代码,以防万一我弄错了?我是前端的新手...

标签: javascript reactjs react-dom


【解决方案1】:

PersonMapStory 中更新您的onClick 以访问event 对象:

<button className="person-map-story-other-button" onClick={e => this.setActivePerson(e, person.name)}>

并更改PersonMapStory 中的setActivePerson(name) 函数:

setActivePerson(event, name) {
    this.props.setActivePerson(name)
    event.stopPropagation()
}

【讨论】:

  • 就是这样!就是这样!就是这样!它的工作!我找到了相关位,但没有正确执行 - 非常感谢!!!
  • 快速跟进问题:导致此行为随着这些更新而改变的最可能原因是什么?我在更高的 react-dom 和 react-router-dom 版本中找不到任何相关的变化,我想知道这是否可能是我不得不删除“withRouter”的结果......
  • 更新:我确认如果我只删除“withRouter”并且不更新包版本,那么行为是相同的,因此它可能是两者之间的相互作用(或更新代码之一包),但代码本身不是更改
  • 反应事件可能发生了一些变化——你确实升级了一个主要版本
  • 男孩-通读,你是对的!那里有很多和全新的渲染代码。我这里还有一个症状,如果你有任何想法并想成为两次英雄:) stackoverflow.com/questions/58565713/…
猜你喜欢
  • 1970-01-01
  • 2017-10-04
  • 2018-02-25
  • 2020-04-29
  • 1970-01-01
  • 2020-03-30
  • 2017-09-26
  • 1970-01-01
  • 2022-06-17
相关资源
最近更新 更多