ตอบสนองการติดตั้งส่วนประกอบสองครั้ง

ภายในส่วนเล็กๆ ของแอปพลิเคชัน React/Redux/ReactRouterV4 ของฉัน ฉันมีลำดับชั้นของส่วนประกอบดังต่อไปนี้

- Exhibit (Parent)
-- ExhibitOne
-- ExhibitTwo
-- ExhibitThree

ภายในรายการย่อยของ Exhibit มีเส้นทางที่เป็นไปได้ที่แตกต่างกันประมาณ 6 เส้นทางที่สามารถแสดงผลได้เช่นกัน ไม่ต้องกังวล ฉันจะอธิบายด้วยโค้ดบางส่วน

นี่คือองค์ประกอบการจัดแสดงหลักของฉัน:

export class Exhibit extends Component {
  render() {
    const { match, backgroundImage } = this.props

    return (
      <div className="exhibit">
        <Header />
        <SecondaryHeader />

        <div className="journey"
          style={{
            color: 'white',
            backgroundImage: `url(${backgroundImage})`,
            backgroundSize: 'cover',
            backgroundRepeat: 'no-repeat',
            backgroundPosition: 'center-center'
          }}>

          <Switch>
            <Route path={`${match.url}/exhibit-one`} component={ExhibitOne} />
            <Route path={`${match.url}/exhibit-two`} component={ExhibitTwo} />
            <Route path={`${match.url}/exhibit-three`} component={ExhibitThree} />
            <Redirect to="/" />
          </Switch>
        </div>
      </div>
    )
  }
}

โดยพื้นฐานแล้ว หน้าที่ของมันก็คือการแสดงองค์ประกอบย่อยของการจัดแสดง และตั้งค่าภาพพื้นหลัง

นี่คือหนึ่งในองค์ประกอบย่อย ExhibitOne:

export default class ExhibitOne extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    const { match } = this.props

    return (
      <div className="exhibit-one">
        <Switch>
          <Route path={`${match.url}/wall-one`} component={ExhibitHOC(WallOne)} />
          <Route path={`${match.url}/wall-two`} component={ExhibitHOC(WallTwo)} />
          <Route path={`${match.url}/wall-three`} component={ExhibitHOC(WallThree)} />
          <Route path={`${match.url}/wall-four`} component={ExhibitHOC(WallFour)} />
          <Route path={`${match.url}/wall-five`} component={ExhibitHOC(WallFive)} />
          <Route path={`${match.url}/wall-six`} component={ExhibitHOC(WallSix)} />
        </Switch>
      </div>
    )
  }
}

เพื่อลดการพิมพ์ ฉันจึงตัดสินใจรวมส่วนประกอบต่างๆ ไว้ในองค์ประกอบลำดับที่สูงกว่า ซึ่งมีวัตถุประสงค์เพื่อส่งการดำเนินการที่จะตั้งค่าภาพพื้นหลังที่เหมาะสมในองค์ประกอบหลัก Exhibit ระดับบนสุด

นี่คือองค์ประกอบลำดับที่สูงกว่า:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions/wall-background-image'

export default function(ComposedComponent) {
  class ExhibitHoc extends Component {

    componentDidMount = () => this.props.setBackgroundImage(`./img/exhibit-one/${this.getWall()}/bg.jpg`)

    getWall = () => {
      // this part isnt important. it is a function that determines what wall I am on, in order to set
      // the proper image.
    }

    render() {
      return <ComposedComponent />
    }
  }

  return connect(null, actions)(ExhibitHoc);
}

ในการโหลด ExhibitOne ครั้งแรก ฉันเห็นว่าผู้สร้างแอ็กชัน setBackgroundImage ดำเนินการสองครั้งโดยดูที่ Redux Logger ในคอนโซล ความโน้มเอียงเริ่มแรกของฉันที่จะใช้componentDidMountเป็นเพราะฉันคิดว่าการใช้มันจะจำกัดผู้สร้างแอคชั่นให้ดำเนินการเพียงครั้งเดียว นี่คือภาพหน้าจอของบันทึก:

ป้อนคำอธิบายรูปภาพที่นี่

ฉันคิดว่าฉันอาจเข้าใจผิดว่า Higher Order Components ทำงานอย่างไร หรืออาจเป็น React Router V4 บางประเภท อย่างไรก็ตาม ความช่วยเหลือใดๆ จะได้รับการชื่นชมอย่างมากว่าทำไมการดำเนินการนี้ถึงสองครั้ง


person Dan Zuzevich    schedule 01.03.2018    source แหล่งที่มา


คำตอบ (3)


ปัญหาคือ component prop ที่นี่คือแอปพลิเคชันฟังก์ชัน ซึ่งให้คลาสใหม่ในการเรนเดอร์แต่ละครั้ง สิ่งนี้จะทำให้ส่วนประกอบก่อนหน้านี้ยกเลิกการต่อเชื่อมและส่วนประกอบใหม่ที่จะเมานต์ (ดู เอกสารสำหรับ react-router สำหรับข้อมูลเพิ่มเติม) โดยปกติคุณจะใช้ render prop เพื่อจัดการสิ่งนี้ แต่สิ่งนี้จะไม่ทำงานกับส่วนประกอบที่มีลำดับสูงกว่า เนื่องจากส่วนประกอบใดๆ ที่สร้างขึ้นด้วยแอปพลิเคชัน HOC ระหว่างการเรนเดอร์จะถูกต่อเชื่อมใหม่ในระหว่างการกระทบยอดของ React อยู่ดี

วิธีแก้ปัญหาง่ายๆ คือการสร้างส่วนประกอบของคุณนอกคลาส ExhibitOne เช่น:

const ExhibitWallOne = ExhibitHOC(WallOne);
const ExhibitWallTwo = ExhibitHOC(WallTwo);
..
export default class ExhibitOne extends Component {
  ..
          <Route path={`${match.url}/wall-one`} component={ExhibitWallOne} />
          <Route path={`${match.url}/wall-two`} component={ExhibitWallTwo} />
          ..
}

หรืออีกทางหนึ่ง ขึ้นอยู่กับสิ่งที่ wrapper ทำ อาจเป็นไปได้ที่จะประกาศให้เป็นส่วนประกอบปกติที่แสดงผล {this.props.children} แทนที่จะเป็นพารามิเตอร์ <ComposedComponent/> และล้อมส่วนประกอบในแต่ละ Route:

<Route path={`${match.url}/wall-one`}
       render={(props) => <Wrap><WallOne {...props}/></Wrap>}
/>

โปรดทราบว่าคุณจะต้องใช้ render แทน component เพื่อป้องกันการต่อเชื่อมใหม่ หากส่วนประกอบไม่ได้ใช้อุปกรณ์ประกอบการกำหนดเส้นทาง คุณสามารถลบ {...props} ได้ด้วย

person Oblosys    schedule 01.03.2018
comment
กำลังส่งโค้ดนี้บางส่วนลงในโปรแกรมแก้ไขข้อความของฉันตอนนี้ จะตอบกลับโดยเร็วที่สุด ›_‹ - person Dan Zuzevich; 01.03.2018
comment
ฉลาดหลักแหลม! ช่วยฉันด้วยความเจ็บปวดมากมายที่นี่ ตอนนี้คุณมีเหตุผลมากที่คุณอธิบายว่ามันให้คลาสใหม่ในการเรนเดอร์แต่ละครั้ง - person Dan Zuzevich; 01.03.2018
comment
ยินดีที่ได้ช่วย :-) - person Oblosys; 01.03.2018
comment
ดีใจที่ได้เห็นสิ่งนี้ยังคงช่วยเหลือผู้คนมาจนถึงทุกวันนี้ คำตอบที่ยอดเยี่ยม @Oblosys! - person Dan Zuzevich; 10.08.2020

ในปี 2020 สิ่งนี้มีสาเหตุมาจากองค์ประกอบ <React.StrictMode> ที่ล้อมรอบ <App /> ในแอป Create React เวอร์ชันใหม่ การลบส่วนประกอบที่ละเมิดออกจาก index.js แก้ไขปัญหาการเมานท์สองครั้งสำหรับส่วนประกอบทั้งหมดของฉัน ฉันไม่รู้ว่านี่เป็นเพราะการออกแบบหรืออะไร แต่มันน่ารำคาญและทำให้เข้าใจผิดที่เห็น console.logs() สองครั้งสำหรับทุกสิ่ง

person SacWebDeveloper    schedule 06.06.2020
comment
จับดี. ฉันไม่เคยสังเกตเห็นสิ่งนั้นมาก่อน ยื่นรายงานข้อผิดพลาดอาจจะ? - person Dara Java; 21.10.2020
comment
ฉลาดหลักแหลม! ช่วยฉันเกาหัวได้สองสามชั่วโมง! มันสมเหตุสมผลสำหรับฉันที่ StrictMode จะทำงานในลักษณะนี้ ดังนั้นฉันสงสัยว่ามันอาจจะถือเป็นจุดบกพร่องได้ อย่างไรก็ตาม การติดตั้งสองครั้งเป็นสิ่งที่ไม่คาดคิด โดยเฉพาะอย่างยิ่งเมื่อคุณไม่ทราบว่า <React.StrictMode> อยู่ที่นั่นด้วย... อาจเป็นการดีที่ได้เห็นผลลัพธ์ในคอนโซลระบุว่าเกิดอะไรขึ้น - person Rolandus; 13.04.2021

หากคุณใช้ 'Hidden Material UI React' มันจะติดตั้งส่วนประกอบของคุณทุกครั้งที่คุณเรียกใช้ ตัวอย่างเช่น ฉันเขียนข้อความด้านล่างนี้:

<Hidden mdDown implementation="css">
    <Container component="main" maxWidth="sm">
        {content}
    </Container>
</Hidden>
<Hidden smUp implementation="css">
    {content}
</Hidden>

มันเรียกใช้เนื้อหาทั้งสองในองค์ประกอบที่ซ่อนอยู่ทั้งสอง ฉันใช้เวลานานมาก

person Mohammad    schedule 23.12.2019