[javascript] 컴포넌트가 Flux의 Stores에서 엔티티를 읽어야하는 중첩 수준은 무엇입니까?

Flux를 사용하기 위해 앱을 다시 작성하고 있는데 Stores에서 데이터를 검색하는 데 문제가 있습니다. 나는 많은 구성 요소를 가지고 있으며 많이 중첩됩니다. 그들 중 일부는 크고 ( Article), 일부는 작고 단순합니다 ( UserAvatar, UserLink).

구성 요소 계층 구조에서 Stores에서 데이터를 읽어야하는 곳에서 어려움을 겪고 있습니다.
두 가지 극단적 인 접근 방식을 시도했지만 어느 쪽도 마음에 들지 않았습니다.

모든 엔티티 구성 요소는 자체 데이터를 읽습니다.

Store에서 일부 데이터가 필요한 각 구성 요소는 엔터티 ID 만 받고 자체적으로 엔터티를 검색합니다.
예를 들어, Article전달 articleId, UserAvatar그리고 UserLink전달됩니다 userId.

이 접근 방식에는 몇 가지 중요한 단점이 있습니다 (코드 샘플에서 논의 됨).

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

이 접근 방식의 단점 :

  • 잠재적으로 Stores를 구독하는 100 개의 구성 요소가 있다는 것은 실망 스럽습니다.
  • 각 구성 요소가 데이터를 독립적으로 검색 하기 때문에 데이터가 업데이트되는 방식과 순서를 추적하기어렵습니다 .
  • 이미 상태에있는 항목 이있을 수 있지만 해당 ID를 자식에게 전달해야하며, 자식은 다시 검색하거나 일관성을 깨뜨립니다.

모든 데이터는 최상위 수준에서 한 번 읽고 구성 요소로 전달됩니다.

버그 추적에 지쳤을 때 검색하는 모든 데이터를 최상위 수준에 두려고했습니다. 그러나 이것은 일부 엔티티의 경우 여러 수준의 중첩이 있기 때문에 불가능한 것으로 판명되었습니다.

예를 들면 :

  • A Category에는 UserAvatar해당 범주에 기여하는 사람들이 포함됩니다 .
  • 에는 s Article가 여러 개있을 수 있습니다 Category.

따라서의 수준에서 Stores의 모든 데이터를 검색 Article하려면 다음을 수행해야합니다.

  • 에서 기사 검색 ArticleStore;
  • 에서 모든 기사의 카테고리를 검색합니다 CategoryStore.
  • 에서 각 카테고리의 기여자를 별도로 검색합니다 UserStore.
  • 어떻게 든 모든 데이터를 구성 요소로 전달합니다.

더욱 실망스럽게도 깊이 중첩 된 엔터티가 필요할 때마다 각 중첩 수준에 코드를 추가하여 추가로 전달해야합니다.

합산

두 접근법 모두 결함이있는 것 같습니다. 이 문제를 어떻게 가장 우아하게 해결합니까?

내 목표 :

  • 상점에 엄청난 수의 구독자가 있어서는 안됩니다. 부모 구성 요소가 이미 그것을 UserLink하고 UserStore있다면 각 사람 이 듣는 것은 어리석은 일입니다.

  • 부모 구성 요소가 저장소 (예 :)에서 일부 개체를 검색 한 user경우 중첩 된 구성 요소가 다시 가져올 필요가 없습니다. 소품을 통해 전달할 수 있어야합니다.

  • 관계 추가 또는 제거가 복잡해지기 때문에 최상위 수준에서 모든 엔터티 (관계 포함)를 가져올 필요는 없습니다. 중첩 된 엔터티가 새 관계를 얻을 때마다 모든 중첩 수준에서 새 소품을 도입하고 싶지 않습니다 (예 : 범주가를 얻음 curator).



답변

대부분의 사람들은 계층 구조의 맨 위 근처에있는 컨트롤러보기 구성 요소에서 관련 저장소를 듣는 것으로 시작합니다.

나중에 관련없는 많은 소품이 계층 구조를 통해 일부 깊이 중첩 된 구성 요소로 전달되는 것처럼 보일 때 일부 사람들 은 더 깊은 구성 요소가 상점의 변경 사항을 수신하도록하는 것이 좋습니다. 이것은 구성 요소 트리의 더 깊은 분기에 관한 문제 도메인의 더 나은 캡슐화를 제공합니다. 이 일을 현명하게하기위한 좋은 주장이 있습니다.

그러나 나는 항상 상단에서 듣고 모든 데이터를 전달하는 것을 선호합니다. 때로는 매장의 전체 상태를 단일 객체로 계층 구조를 통해 전달하고 여러 매장에 대해이 작업을 수행 할 수도 있습니다. 그래서 나는 ArticleStore의 상태에 대한 prop 과의 상태 등에 대한 prop을 갖게 될 것입니다 UserStore. 깊이 중첩 된 컨트롤러 뷰를 피하는 것은 데이터에 대한 단일 진입 점을 유지하고 데이터 흐름을 통합한다는 것을 발견했습니다. 그렇지 않으면 데이터 소스가 여러 개 있고 디버깅하기 어려울 수 있습니다.

이 전략에서는 유형 검사가 더 어렵지만 React의 PropTypes를 사용하여 소품으로서의 대형 객체에 대해 “모양”또는 유형 템플릿을 설정할 수 있습니다. 참조 :
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 프레임 워크 ( Flux 호환 가능 )와 유사한 접근 방식을 취한다는 것입니다 .Flux 에서 이에 상응하는 것은 하나의 컨트롤러 뷰만 갖는 것입니다. 모든 상점을 듣고 있습니다. 이것은 shouldComponentUpdate()===와 함께 참조로 비교할 수있는 변경 불가능한 데이터 구조를 사용하여 효율적 입니다. 불변 데이터 구조의 경우 David ‘s mori 또는 Facebook의 immutable-js를 확인하십시오 . Om에 대한 나의 제한된 지식은 주로 JavaScript MVC 프레임 워크의 미래 에서 비롯됩니다 .


답변

내가 도착한 접근 방식은 각 구성 요소가 해당 데이터 (ID가 아님)를 소품으로받는 것입니다. 일부 중첩 된 구성 요소에 관련 엔터티가 필요한 경우이를 검색하는 것은 부모 구성 요소에 달려 있습니다.

이 예에서, Article을 가져야한다article 객체 ( ArticleList또는에서 검색 소품이ArticlePage .

Article또한 렌더링 UserLink하고 UserAvatar기사 작성자를 원하기 때문에 구독 하고 상태를 UserStore유지 author: UserStore.get(article.authorId)합니다. 그런 다음 렌더링됩니다.UserLinkUserAvatar이와 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>
    )
  }
});

이것은 가장 안쪽의 구성 요소를 어리석게 유지하지만 최상위 구성 요소에서 지옥을 복잡하게 만들지 않습니다.


답변

내 솔루션은 훨씬 간단합니다. 자체 상태를 가진 모든 구성 요소는 상점과 대화하고 청취 할 수 있습니다. 이들은 매우 컨트롤러와 유사한 구성 요소입니다. 상태를 유지하지 않고 렌더링 만하는 더 깊은 중첩 구성 요소는 허용되지 않습니다. 그들은 순수한 렌더링, 매우보기와 같은 소품 만받습니다.

이렇게하면 모든 것이 상태 저장 구성 요소에서 상태 비 저장 구성 요소로 흐릅니다. Stateful을 낮게 유지합니다.

귀하의 경우 Article은 상태 저장이므로 상점 및 UserLink 등과 대화하면 article.user를 prop으로 수신 할 수 있도록 렌더링됩니다.


답변

두 가지 철학에 설명 된 문제는 모든 단일 페이지 응용 프로그램에 공통적입니다.

https://www.youtube.com/watch?v=IrgHurBjQbg 및 Relay ( https://facebook.github.io/relay )는 이 비디오에서 간략하게 설명합니다. 설명하는 트레이드 오프를 극복하기 위해 Facebook에서 개발했습니다.

Relay의 접근 방식은 매우 데이터 중심적입니다. “서버에 대한 한 번의 쿼리에서이 뷰의 각 구성 요소에 필요한 데이터를 어떻게 얻습니까?”라는 질문에 대한 대답입니다. 동시에 Relay는 구성 요소가 여러 뷰에서 사용되는 경우 코드간에 결합이 거의 없도록합니다.

Relay가 옵션이 아닌 경우 “All entity components read their own data”는 설명하는 상황에 대해 더 나은 접근 방식으로 보입니다. Flux의 오해는 가게가 무엇인지라고 생각합니다. 상점의 개념은 모델이나 물건 모음이 보관되는 장소가 아닙니다. 스토어는 뷰가 렌더링되기 전에 애플리케이션이 데이터를 저장하는 임시 장소입니다. 이들이 존재하는 진짜 이유는 서로 다른 저장소에있는 데이터에 대한 종속성 문제를 해결하는 것입니다.

Flux가 지정하지 않는 것은 매장이 모델 및 객체 컬렉션 (백본)의 개념과 관련되는 방식입니다. 그런 의미에서 일부 사람들은 실제로 플럭스 스토어를 전체 시간 동안 플러시되지 않는 특정 유형의 객체 컬렉션을 놓을 장소로 만들고 있습니다. 사용자는 브라우저를 열어 두지 만 플럭스를 이해하기 때문에 그것은 스토어가 아닙니다. 있어야합니다.

해결책은 뷰를 렌더링하는 데 필요한 엔터티 (잠재적으로 더 많은 항목)가 저장되고 업데이트 된 상태로 유지되는 다른 레이어를 갖는 것입니다. 이 계층이 모델과 컬렉션을 추상화하는 경우 하위 구성 요소가 자체 데이터를 얻기 위해 다시 쿼리해야하는 경우 문제가되지 않습니다.


답변