ส่วนประกอบที่ซ้อนกันควรอ่านเอนทิตีจากร้านค้าใน Flux ในระดับใด


89

ฉันเขียนแอปใหม่เพื่อใช้ Flux และฉันมีปัญหาในการดึงข้อมูลจากร้านค้า ฉันมีส่วนประกอบมากมายและพวกมันก็ทำรังมากมาย บางอันมีขนาดใหญ่ ( Article) บางอันมีขนาดเล็กและเรียบง่าย ( UserAvatar, UserLink)

ฉันกำลังดิ้นรนกับลำดับชั้นขององค์ประกอบที่ฉันควรอ่านข้อมูลจากร้านค้า
ฉันลองสองวิธีที่รุนแรงซึ่งฉันไม่ชอบ:

ส่วนประกอบเอนทิตีทั้งหมดอ่านข้อมูลของตนเอง

ส่วนประกอบแต่ละรายการที่ต้องการข้อมูลบางส่วนจาก Store จะได้รับเพียง ID เอนทิตีและดึงข้อมูลเอนทิตีด้วยตัวเอง
ตัวอย่างเช่นArticleมีการส่งผ่านarticleId, UserAvatarและจะถูกส่งผ่านUserLinkuserId

แนวทางนี้มีข้อเสียที่สำคัญหลายประการ (กล่าวถึงภายใต้ตัวอย่างโค้ด)

var Article = React.createClass({
  mixins: [createStoreMixin(ArticleStore)],

  propTypes: {
    articleId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      article: ArticleStore.get(this.props.articleId);
    }
  },

  render() {
    var article = this.state.article,
        userId = article.userId;

    return (
      <div>
        <UserLink userId={userId}>
          <UserAvatar userId={userId} />
        </UserLink>

        <h1>{article.title}</h1>
        <p>{article.text}</p>

        <p>Read more by <UserLink userId={userId} />.</p>
      </div>
    )
  }
});

var UserAvatar = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    userId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      user: UserStore.get(this.props.userId);
    }
  },

  render() {
    var user = this.state.user;

    return (
      <img src={user.thumbnailUrl} />
    )
  }
});

var UserLink = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    userId: PropTypes.number.isRequired
  },

  getStateFromStores() {
    return {
      user: UserStore.get(this.props.userId);
    }
  },

  render() {
    var user = this.state.user;

    return (
      <Link to='user' params={{ userId: this.props.userId }}>
        {this.props.children || user.name}
      </Link>
    )
  }
});

ข้อเสียของแนวทางนี้:

  • เป็นเรื่องน่าผิดหวังที่มีส่วนประกอบ 100s ที่อาจสมัครเป็นสมาชิกร้านค้า
  • เป็นการยากที่จะติดตามว่าข้อมูลได้รับการอัปเดตอย่างไรและเรียงลำดับอย่างไรเนื่องจากแต่ละองค์ประกอบดึงข้อมูลอย่างอิสระ
  • ถึงแม้ว่าคุณจะอยู่แล้วมีกิจการในรัฐที่คุณถูกบังคับให้ผ่าน ID เพื่อเด็กที่จะดึงมันอีกครั้ง (หรืออื่น ๆ ที่ทำลายความมั่นคง)

ข้อมูลทั้งหมดจะถูกอ่านครั้งเดียวที่ระดับบนสุดและส่งต่อไปยังส่วนประกอบ

เมื่อฉันเบื่อกับการติดตามจุดบกพร่องฉันพยายามให้ข้อมูลทั้งหมดที่ดึงมาอยู่ในระดับบนสุด อย่างไรก็ตามสิ่งนี้พิสูจน์แล้วว่าเป็นไปไม่ได้เพราะสำหรับบางเอนทิตีฉันมีการซ้อนกันหลายระดับ

ตัวอย่างเช่น:

  • CategoryมีUserAvatarของคนที่มีส่วนร่วมในหมวดหมู่ที่;
  • ArticleอาจมีหลายCategorys

ดังนั้นหากฉันต้องการดึงข้อมูลทั้งหมดจากร้านค้าในระดับ an Articleฉันจะต้อง:

  • ดึงบทความจากArticleStore;
  • ดึงหมวดหมู่บทความทั้งหมดจากCategoryStore;
  • ดึงข้อมูลผู้มีส่วนร่วมของแต่ละหมวดหมู่จากUserStore;
  • ส่งผ่านข้อมูลทั้งหมดไปยังส่วนประกอบ

ยิ่งน่าผิดหวังมากขึ้นเมื่อใดก็ตามที่ฉันต้องการเอนทิตีที่ซ้อนกันอย่างลึกซึ้งฉันจะต้องเพิ่มโค้ดในแต่ละระดับของการซ้อนกันเพื่อส่งต่อไป

สรุป

ทั้งสองแนวทางดูเหมือนมีข้อบกพร่อง ฉันจะแก้ปัญหานี้อย่างสง่างามที่สุดได้อย่างไร

วัตถุประสงค์ของฉัน:

  • ร้านค้าไม่ควรมีจำนวนสมาชิกที่บ้าคลั่ง มันโง่มากที่แต่ละคนUserLinkจะฟังUserStoreหากส่วนประกอบของผู้ปกครองทำเช่นนั้น

  • หากองค์ประกอบหลักได้ดึงวัตถุบางอย่างจากร้านค้า (เช่นuser) ฉันไม่ต้องการให้ส่วนประกอบที่ซ้อนกันต้องดึงข้อมูลอีกครั้ง ฉันควรจะส่งผ่านอุปกรณ์ประกอบฉากได้

  • ฉันไม่ควรดึงเอนทิตีทั้งหมด (รวมถึงความสัมพันธ์) ที่ระดับบนสุดเพราะจะทำให้การเพิ่มหรือลบความสัมพันธ์ซับซ้อน ฉันไม่ต้องการแนะนำอุปกรณ์ประกอบฉากใหม่ในทุกระดับการซ้อนทุกครั้งที่เอนทิตีที่ซ้อนกันได้รับความสัมพันธ์ใหม่ (เช่นหมวดหมู่ได้รับ a curator)


1
ขอบคุณสำหรับการโพสต์สิ่งนี้ ฉันเพิ่งโพสต์คำถามที่คล้ายกันมากที่นี่groups.google.com/forum/…ทุกสิ่งที่ฉันเห็นได้จัดการกับโครงสร้างข้อมูลแบบแบนแทนที่จะเป็นข้อมูลที่เกี่ยวข้อง ฉันแปลกใจที่ไม่เห็นแนวทางปฏิบัติที่ดีที่สุดในเรื่องนี้
uberllama

คำตอบ:


37

คนส่วนใหญ่เริ่มต้นด้วยการฟังร้านค้าที่เกี่ยวข้องในส่วนประกอบมุมมองตัวควบคุมที่อยู่ใกล้กับด้านบนสุดของลำดับชั้น

ต่อมาเมื่อดูเหมือนว่าอุปกรณ์ประกอบฉากที่ไม่เกี่ยวข้องจำนวนมากกำลังถูกส่งต่อผ่านลำดับชั้นไปยังส่วนประกอบที่ซ้อนกันอย่างลึกซึ้งบางคนจะตัดสินใจว่าเป็นความคิดที่ดีที่จะให้องค์ประกอบที่ลึกกว่ารับฟังการเปลี่ยนแปลงในร้านค้า สิ่งนี้นำเสนอการห่อหุ้มโดเมนปัญหาที่ดีขึ้นซึ่งกิ่งก้านที่ลึกกว่านี้ของโครงสร้างส่วนประกอบนั้นเกี่ยว มีข้อโต้แย้งที่ดีสำหรับการทำเช่นนี้อย่างรอบคอบ

อย่างไรก็ตามฉันชอบฟังที่ด้านบนเสมอและเพียงแค่ส่งผ่านข้อมูลทั้งหมด บางครั้งฉันจะใช้สถานะทั้งหมดของร้านค้าและส่งผ่านลำดับชั้นเป็นวัตถุชิ้นเดียวและฉันจะทำสิ่งนี้กับร้านค้าหลายแห่ง ดังนั้นฉันจะมีเสาสำหรับArticleStoreสถานะและอีกสถานะหนึ่งสำหรับUserStoreสถานะของ ฯลฯ ฉันพบว่าการหลีกเลี่ยงมุมมองคอนโทรลเลอร์ที่ซ้อนกันลึก ๆ จะรักษาจุดเข้าที่เป็นเอกพจน์สำหรับข้อมูลและรวมการไหลของข้อมูล มิฉะนั้นฉันมีแหล่งข้อมูลหลายแหล่งและอาจแก้ไขจุดบกพร่องได้ยาก

การตรวจสอบประเภททำได้ยากกว่าด้วยกลยุทธ์นี้ แต่คุณสามารถตั้งค่า "รูปร่าง" หรือพิมพ์เทมเพลตสำหรับวัตถุขนาดใหญ่ที่มี PropTypes ของ React ได้ ดู: https://github.com/facebook/react/blob/master/src/core/ReactPropTypes.js#L76-L91 http://facebook.github.io/react/docs/reusable-components.html#prop - การตรวจสอบ

โปรดทราบว่าคุณอาจต้องการวางตรรกะของการเชื่อมโยงข้อมูลระหว่างร้านค้าในร้านค้าด้วยกันเอง ดังนั้นคุณArticleStoreอาจและรวมถึงผู้ที่เกี่ยวข้องกับทุกบันทึกจะให้ผ่าน การทำเช่นนี้ในมุมมองของคุณดูเหมือนเป็นการผลักตรรกะไปที่เลเยอร์มุมมองซึ่งเป็นแนวทางปฏิบัติที่คุณควรหลีกเลี่ยงเมื่อทำได้waitFor()UserStoreArticlegetArticles()

คุณอาจถูกล่อลวงให้ใช้transferPropsTo()และหลาย ๆ คนก็ชอบทำเช่นนี้ แต่ฉันชอบที่จะเก็บทุกอย่างไว้อย่างชัดเจนเพื่อให้อ่านง่ายและบำรุงรักษาได้

FWIW ความเข้าใจของฉันคือ David Nolen ใช้แนวทางที่คล้ายกันกับ Om framework ของเขา (ซึ่งค่อนข้างเข้ากันได้กับFlux ) โดยมีจุดเข้าข้อมูลเดียวบนโหนดรูท - สิ่งที่เทียบเท่าใน Flux จะมีเพียงมุมมองคอนโทรลเลอร์เดียวเท่านั้น ฟังทุกร้าน สิ่งนี้ทำให้มีประสิทธิภาพโดยใช้shouldComponentUpdate()และโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปซึ่งสามารถเปรียบเทียบได้โดยการอ้างอิงด้วย === สำหรับโครงสร้างข้อมูลไม่เปลี่ยนรูปชำระเงินของดาวิดMoriหรือของ Facebook ไม่เปลี่ยนรูป-js ความรู้ที่ จำกัด ของฉันเกี่ยวกับ Om ส่วนใหญ่มาจากThe Future of JavaScript MVC Frameworks


4
ขอบคุณสำหรับคำตอบโดยละเอียด! ฉันได้พิจารณาส่งต่อภาพรวมทั้งหมดของสถานะร้านค้า อย่างไรก็ตามสิ่งนี้อาจทำให้ฉันมีปัญหาอย่างสมบูรณ์แบบเนื่องจากการอัปเดต UserStore ทุกครั้งจะทำให้บทความทั้งหมดบนหน้าจอแสดงผลอีกครั้งและ PureRenderMixin จะไม่สามารถบอกได้ว่าบทความใดที่ต้องอัปเดตและไม่สามารถระบุได้ สำหรับข้อเสนอแนะที่สองของคุณฉันก็คิดเช่นกัน แต่ฉันไม่เห็นว่าจะรักษาความเท่าเทียมกันในการอ้างอิงบทความได้อย่างไรหากผู้ใช้บทความถูก "ฉีด" ในการเรียก getArticles () แต่ละครั้ง นี่อาจทำให้ฉันมีปัญหาที่สมบูรณ์แบบอีกครั้ง
Dan Abramov

1
ฉันเห็นประเด็นของคุณ แต่มีวิธีแก้ไข ประการแรกคุณสามารถเช็คอิน shouldComponentUpdate () ได้หากอาร์เรย์ของ ID ผู้ใช้สำหรับบทความมีการเปลี่ยนแปลงซึ่งโดยพื้นฐานแล้วเป็นเพียงการใช้ฟังก์ชันและพฤติกรรมที่คุณต้องการใน PureRenderMixin ในกรณีนี้
fisherwebdev

3
ถูกต้องและอย่างไรก็ตามฉันจะต้องทำก็ต่อเมื่อฉันแน่ใจว่ามีปัญหาเกี่ยวกับความสมบูรณ์ซึ่งไม่ควรเป็นกรณีสำหรับร้านค้าที่อัปเดตสูงสุดหลายครั้งต่อนาที ขอขอบคุณอีกครั้ง!
Dan Abramov

8
ขึ้นอยู่กับประเภทของแอปพลิเคชันที่คุณกำลังสร้างการรักษาจุดเข้าเพียงจุดเดียวจะส่งผลเสียทันทีที่คุณเริ่มมี "วิดเจ็ต" เพิ่มเติมบนหน้าจอที่ไม่ได้อยู่ในรูทเดียวกันโดยตรง หากคุณพยายามทำด้วยวิธีนี้อย่างจริงจังโหนดรูทนั้นจะมีความรับผิดชอบมากเกินไป KISS และ SOLID แม้ว่าจะเป็นฝั่งไคลเอ็นต์ก็ตาม
Rygu

37

วิธีการที่ฉันมาถึงคือการให้แต่ละองค์ประกอบรับข้อมูล (ไม่ใช่ ID) เป็นเสา หากส่วนประกอบที่ซ้อนกันบางส่วนต้องการเอนทิตีที่เกี่ยวข้องก็ขึ้นอยู่กับองค์ประกอบหลักที่จะเรียกคืน

ในตัวอย่างของเราArticleควรมีarticleเสาซึ่งเป็นวัตถุ (น่าจะดึงมาจากArticleListหรือArticlePage)

เนื่องจากArticleต้องการแสดงผลUserLinkและUserAvatarสำหรับผู้เขียนบทความจะสมัครรับข้อมูลUserStoreและคงauthor: UserStore.get(article.authorId)สถานะไว้ จากนั้นจะแสดงผลUserLinkและด้วยนี้UserAvatar this.state.authorหากพวกเขาต้องการที่จะผ่านมันลงไปมากขึ้นพวกเขาก็ทำได้ ไม่มีส่วนประกอบย่อยที่จะต้องดึงข้อมูลผู้ใช้นี้อีกครั้ง

เพื่อย้ำ:

  • ไม่มีส่วนประกอบใดที่เคยได้รับ ID เป็นเสา ส่วนประกอบทั้งหมดได้รับวัตถุตามลำดับ
  • หากส่วนประกอบย่อยต้องการเอนทิตีเป็นความรับผิดชอบของผู้ปกครองในการดึงข้อมูลและส่งต่อเป็นส่วนประกอบ

สิ่งนี้ช่วยแก้ปัญหาของฉันได้ดีทีเดียว ตัวอย่างโค้ดที่เขียนขึ้นใหม่เพื่อใช้แนวทางนี้:

var Article = React.createClass({
  mixins: [createStoreMixin(UserStore)],

  propTypes: {
    article: PropTypes.object.isRequired
  },

  getStateFromStores() {
    return {
      author: UserStore.get(this.props.article.authorId);
    }
  },

  render() {
    var article = this.props.article,
        author = this.state.author;

    return (
      <div>
        <UserLink user={author}>
          <UserAvatar user={author} />
        </UserLink>

        <h1>{article.title}</h1>
        <p>{article.text}</p>

        <p>Read more by <UserLink user={author} />.</p>
      </div>
    )
  }
});

var UserAvatar = React.createClass({
  propTypes: {
    user: PropTypes.object.isRequired
  },

  render() {
    var user = this.props.user;

    return (
      <img src={user.thumbnailUrl} />
    )
  }
});

var UserLink = React.createClass({
  propTypes: {
    user: PropTypes.object.isRequired
  },

  render() {
    var user = this.props.user;

    return (
      <Link to='user' params={{ userId: this.props.user.id }}>
        {this.props.children || user.name}
      </Link>
    )
  }
});

สิ่งนี้ช่วยให้ส่วนประกอบด้านในสุดโง่ แต่ไม่ได้บังคับให้เราซับซ้อนนรกจากส่วนประกอบระดับบนสุด


เพียงเพื่อเพิ่มมุมมองด้านบนของคำตอบนี้ ตามแนวคิดแล้วคุณสามารถพัฒนาแนวความคิดเกี่ยวกับความเป็นเจ้าของได้เนื่องจากการรวบรวมข้อมูลจะสรุปในมุมมองส่วนใหญ่อันดับต้น ๆ ว่าใครเป็นเจ้าของข้อมูลจริง ๆ (ดังนั้นการฟังที่เก็บ) ตัวอย่างไม่กี่ตัวอย่าง: 1. AuthData ควรเป็นของส่วนประกอบของแอปการเปลี่ยนแปลงใด ๆ ใน Auth ควรเผยแพร่เป็นอุปกรณ์ประกอบฉากให้กับลูกทั้งหมดโดยส่วนประกอบของแอป 2. ArticleData ควรเป็นของ ArticlePage หรือรายการบทความตามที่แนะนำในคำตอบ ตอนนี้ลูกของ ArticleList ทุกคนสามารถรับ AuthData และ / หรือ ArticleData จาก ArticleList ได้ไม่ว่าองค์ประกอบใดจะดึงข้อมูลนั้นมาจริง
Saumitra R. Bhave

2

วิธีแก้ปัญหาของฉันง่ายกว่ามาก ทุกองค์ประกอบที่มีสถานะของตัวเองได้รับอนุญาตให้พูดคุยและฟังร้านค้า เหล่านี้เป็นส่วนประกอบที่เหมือนคอนโทรลเลอร์มาก คอมโพเนนต์ที่ซ้อนกันลึกกว่าซึ่งไม่คงสถานะ แต่ไม่อนุญาตให้แสดงผลสิ่ง พวกเขาได้รับเฉพาะอุปกรณ์ประกอบฉากสำหรับการแสดงผลที่บริสุทธิ์เหมือนการรับชมเท่านั้น

วิธีนี้ทุกอย่างจะไหลจากส่วนประกอบที่มีสถานะเป็นส่วนประกอบไร้สัญชาติ การรักษาสถานะไว้เป็นจำนวนน้อย

ในกรณีของคุณบทความจะเป็นสถานะดังนั้นการพูดคุยกับร้านค้าและ UserLink ฯลฯ จะแสดงผลดังนั้นจึงจะได้รับ article.user เป็น prop


1
ฉันคิดว่าสิ่งนี้จะใช้ได้ถ้าบทความมีคุณสมบัติวัตถุผู้ใช้ แต่ในกรณีของฉันมีเพียง userId (เนื่องจากผู้ใช้จริงถูกเก็บไว้ใน UserStore) หรือฉันพลาดประเด็นของคุณ? คุณจะแบ่งปันตัวอย่างหรือไม่?
Dan Abramov

เริ่มต้นการดึงข้อมูลสำหรับผู้ใช้ในบทความ หรือเปลี่ยนแบ็กเอนด์ API ของคุณเพื่อทำให้ ID ผู้ใช้ "ขยายได้" คุณจึงได้ผู้ใช้พร้อมกัน ไม่ว่าจะด้วยวิธีใดก็ตามเพื่อให้ส่วนประกอบที่ซ้อนกันอยู่ลึกลงไปมีมุมมองที่เรียบง่ายและพึ่งพาอุปกรณ์ประกอบฉากเท่านั้น
Rygu

2
ฉันไม่สามารถเริ่มต้นการดึงข้อมูลในบทความได้เนื่องจากจำเป็นต้องแสดงผลร่วมกัน สำหรับ API ที่ขยายได้เราเคยมี แต่พวกเขามีปัญหามากในการแยกวิเคราะห์จากร้านค้า ฉันยังเขียนห้องสมุดเพื่อแบนคำตอบด้วยเหตุผลนี้
Dan Abramov

0

ปัญหาที่อธิบายไว้ในปรัชญา 2 ข้อของคุณมักเกิดกับแอปพลิเคชันหน้าเดียว

พวกเขาจะพูดถึงสั้น ๆ ในวิดีโอนี้: https://www.youtube.com/watch?v=IrgHurBjQbgและ Relay ( https://facebook.github.io/relay ) ได้รับการพัฒนาโดย Facebook เพื่อเอาชนะการแลกเปลี่ยนที่คุณอธิบาย

วิธี Relay เป็นมากเป็นศูนย์กลางข้อมูล เป็นคำตอบสำหรับคำถาม "ฉันจะรับเฉพาะข้อมูลที่จำเป็นสำหรับแต่ละองค์ประกอบในมุมมองนี้ในแบบสอบถามเดียวไปยังเซิร์ฟเวอร์ได้อย่างไร" และในเวลาเดียวกันรีเลย์ตรวจสอบให้แน่ใจว่าคุณมีการเชื่อมต่อระหว่างโค้ดเพียงเล็กน้อยเมื่อส่วนประกอบที่ใช้ในหลายมุมมอง

ถ้ารีเลย์ไม่ใช่ตัวเลือก "ส่วนประกอบเอนทิตีทั้งหมดอ่านข้อมูลของตัวเอง" น่าจะเป็นแนวทางที่ดีกว่าสำหรับฉันสำหรับสถานการณ์ที่คุณอธิบาย ฉันคิดว่าความเข้าใจผิดใน Flux คือร้านค้าคืออะไร แนวคิดของร้านค้าไม่ได้เป็นสถานที่ที่เก็บโมเดลหรือคอลเล็กชันของวัตถุ ร้านค้าคือสถานที่ชั่วคราวที่แอปพลิเคชันของคุณใส่ข้อมูลก่อนที่จะแสดงผลมุมมอง เหตุผลที่แท้จริงคือการแก้ปัญหาการอ้างอิงข้อมูลที่ไปในร้านค้าต่างๆ

สิ่งที่ Flux ไม่ได้ระบุคือความสัมพันธ์ของร้านค้ากับแนวคิดของแบบจำลองและการรวบรวมวัตถุ (a la Backbone) ในแง่นั้นคนบางคนกำลังสร้างร้านฟลักซ์เป็นสถานที่ที่จะรวบรวมวัตถุประเภทเฉพาะที่ไม่ได้ล้างตลอดเวลาที่ผู้ใช้เปิดเบราว์เซอร์ไว้ตลอดเวลา แต่อย่างที่ฉันเข้าใจฟลักซ์นั่นไม่ใช่สิ่งที่เก็บ ควรจะเป็น

วิธีแก้ปัญหาคือการมีเลเยอร์อื่นที่คุณจัดเก็บเอนทิตีที่จำเป็นในการแสดงผลมุมมองของคุณ (และอาจมากกว่านั้น) และอัปเดตอยู่เสมอ หากคุณเป็นเลเยอร์ที่เป็นโมเดลนามธรรมและคอลเลกชันก็จะไม่เป็นปัญหาหากคุณต้องค้นหาส่วนประกอบย่อยอีกครั้งเพื่อรับข้อมูลของตนเอง


1
เท่าที่ฉันเข้าใจวิธีการของ Dan ในการเก็บรักษาวัตถุประเภทต่างๆและการอ้างอิงผ่านรหัสช่วยให้เราเก็บแคชของวัตถุเหล่านั้นและอัปเดตได้ในที่เดียวเท่านั้น สิ่งนี้จะเกิดขึ้นได้อย่างไรหากคุณมีบทความหลายบทความที่อ้างอิงถึงผู้ใช้คนเดียวกันแล้วชื่อผู้ใช้จะถูกอัปเดต วิธีเดียวคืออัปเดตบทความทั้งหมดด้วยข้อมูลผู้ใช้ใหม่ นี่เป็นปัญหาเดียวกับที่คุณได้รับเมื่อเลือกระหว่าง document กับ relational db สำหรับแบ็กเอนด์ของคุณ คุณคิดอย่างไรเกี่ยวกับเรื่องนี้? ขอขอบคุณ.
Alex Ponomarev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.