【问题标题】:Force stop container subscribtion (Meteor+React+CreateContainer)强制停止容器订阅(Meteor+React+CreateContainer)
【发布时间】:2017-04-10 16:35:49
【问题描述】:

通过react-router切换路由时,如何在客户端手动停止订阅并清理集合?

事实是,如果我转到订阅了组件的页面,例如“前 10 条新闻”页面,发布了所有新闻,我看到,作为执行正常搜索的新闻组件的容器集合首先找到订阅最后一页的对象。

export default createContainer((props)=>{
    let {limitCount, userId} = props;
    let query = (userId)?{'userId':userId}:{};
    var handle = Meteor.subscribe('news_preview', query,limitCount);
    var posts = Post.find(query, {sort:{createdAt:-1}}).fetch(); //Here, the container finds documents to which another component has been signed.
    var needWait = !!handle.ready()&&!!posts; 
    return {
      needWait:!needWait,
      posts:posts
    }
  }, PostList)

过了一会儿,容器将完成其成员资格,并将给我们相关的对象... 如何检查链接前一个容器时是否已停止订阅并删除对象?

详细说明。附逻辑说明

PostListContainer 这是一个 NewsList 组件的示例,带有无限滚动和订阅容器。只是检测滚动,并将limitCount传递给子组件。

export default class PostListContainer extends Component{
  constructor(props){
    super(props)
    this.state={
      limitCount:10,
    }
    this.handleScroll = this.handleScroll.bind(this);
  }
  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll, false);
  }
  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll, false);
  }
  handleScroll(event) {
    let documentHeight = $(document).height(); //jquery is bad idea, i know
    let windowHeight = $(window).height();
    let scrollPosition = $(window).scrollTop();
    let isLoading = this.refs.PostList.data.needWait;
    if(scrollPosition+windowHeight>=documentHeight && !isLoading){
      this.uppendSkipCount()
    }

  }
  uppendSkipCount(){
    let limitCount = this.state.limitCount;
    this.setState({
      limitCount:limitCount+5
    });
  }
  render(){
    let userId = (this.props.userId)?this.props.userId:undefined;
    return(
      <div>
        <PostList ref="PostList" limitCount={this.state.limitCount} userId={userId} />
      </div>
    )
  }
}

帖子列表 该组件获取属性、订阅和渲染子组件。

export  class PostList extends Component {
    constructor(props){
      super(props);
    }
    postList(){
      return(
        <Masonry>
          {this.props.posts.map((post)=>(
            <div className="col-xs-12 col-sm-6 col-md-6 col-lg-4" key={post._id}>
              <PostPreview  post={post}/>
            </div>
          ))}
        </Masonry>
      )
    }
    render() {
        let content = (this.props.posts)?this.postList():undefined;
        let loading = (this.props.needWait)?<Loading />:undefined;
        return(
          <div>
            {content}
            {loading}
          </div>
        )
    }
  }

export default createContainer((props)=>{
    let {limitCount, userId} = props;
    let query = (userId)?{'userId':userId}:{};
    var handle = Meteor.subscribe('news_preview', query,limitCount);
    var posts = Post.find(query, {sort:{createdAt:-1}}).fetch();
    var needWait = !!handle.ready()&&!!posts;
    return {
      needWait:!needWait,
      posts:posts
    }
  }, PostList)

而且效果很好。但是,如果我从包含例如 BestNews 组件的页面进入页面,我将在第一次迭代中从该页面获取 Posts 对象:(

export class BestNews extends Component {
    constructor(props){
      super(props);
    }

    componentWillUnmount(){
     this.props.handle.stop(); / looks like it help
    }
    goToPage(post){
      browserHistory.push(`/news/item/${post._id}`);
    }
    bestPosts(){
      return bestPosts = this.props.posts.map((post)=>{
        return(
          <div key={post._id}>
          <ListItem
            onTouchTap={()=>this.goToPage(post)}
            leftAvatar={<Avatar src={post.author().getAvatar().link()} />}
            rightIcon ={<ActionChromeReaderMode />}
            primaryText={post.title}
            secondaryText={post.description}
            />
          <Divider />
          </div>
        )
      });
    }
    render() {
        let content = (this.props.needWait)?<Loading />:this.bestPosts();
        return (
          <div className="box-margin">
            <Paper zDepth={1}>
                <div className="row middle-xs center-xs">
                    <div className="col-xs-12">
                        <h2 style={{fontWeight:'300'}}>Интересные новости</h2>
                    </div>
                </div>
                <div className="row start-xs">
                    <div className="col-xs-12">
                        <List>
                            <Divider />
                            {content}
                        </List>
                    </div>
                </div>
            </Paper>
          </div>
          )

    }
  }

export default createContainer((props)=>{
    var handle = Meteor.subscribe('best_news');
    var posts = Post.find({}, {sort:{likes:-1}, limit:5}).fetch();
    var needWait = !handle.ready() && !posts;
    return {
      handle:handle,
      needWait:needWait,
      posts:posts
    }
  }, BestNews)

【问题讨论】:

  • Meteor 中没有内置机制,详见this issue。该线程建议了一些替代方法,例如将元数据添加到某些出版物(例如,您的“热门帖子”)并对其进行查询。
  • 我找到了解决方案。这很明显。但不知何故,在我看来,还有一个更好的。在这种情况下,我们需要使用容器的子组件。使用方法 componentWillUnmount() 我们可以手动停止订阅。

标签: reactjs meteor react-router


【解决方案1】:

我找到了解决办法。

这很明显。但不知何故,在我看来,还有更好的。

在这种情况下,我们需要使用容器的子组件。使用方法 componentWillUnmount() 我们可以手动停止订阅。

什么是负面的?事实上,我们需要在子组件中调用订阅对象,然后手动停止订阅。并且在每个组件中都这样做,这可能会导致类似的问题。

问题的答案——为什么我们不能等待容器的订阅完成,然后——显示内容。答案是——有可能,但只要你没有实现无限滚动逻辑。否则,每次用户向下滚动时,内容都会丢失。只要订阅完成。

这很有趣,但我一直认为容器会停止订阅。我错了吗?

【讨论】:

  • 容器在卸载时停止订阅。
  • 在我的情况下 - 不:(我不知道为什么。一切正常,但如果我手动停止订阅。
  • 然后显示更多代码或将简单复制上传到 GitHub。
  • 我用代码更新了问题...你能看一下吗?
  • 看起来很合理,但我对 ReactRouter 并不是很熟悉。在渲染之前,您无需等待订阅准备就绪。此外,调用stop() 时,订阅需要很短的时间来删除文档,因此您得到一些不正确的数据也就不足为奇了。您可以尝试使用在订阅停止时调用的onStop 回调进行订阅(注意:不是在删除数据时,而是在将unsub DDP 消息发送到服务器时)并跟踪发生了什么,以确保容器确实要求取消订阅。
【解决方案2】:

手动停止订阅的解决方案不好。你明白为什么。

看看这个相当不错的解决方案。 我们需要保护自己免受这样一个事实,即当您访问一个组件时,我们可以获得与其他组件无关的数据。是的类型具有相同的数据,但是 - 与其他组件。它可以和 s 开一个残酷的玩笑。我想你明白为什么了。

同时,我们不能在每次用户向下滚动并加载数据时设置上限“加载”。因为在订阅的时候,所有的数据都会消失。带有命名订阅的选项您也不满意。例如,如果您使用 Jagi 的 Astronomy ORM。

所以。我们只需要简单的拦截从容器中取值,一旦完成就在组件的状态下记录第一次订阅的事实。

export  class PostList extends Component {
    constructor(props){
      super(props);
      this.state={
        firstSubscribtion:false
      }
    }
    componentWillReceiveProps(nextProps){
      if(!this.state.firstSubscribtion && nextProps.handleReady){
        this.setState({
          firstSubscribtion:true
        });
      }
    }
    postList(){
      return(
        <Masonry>
          {this.props.posts.map((post)=>(
            <div className="col-xs-12 col-sm-6 col-md-6 col-lg-4" key={post._id}>
              <PostPreview  post={post}/>
            </div>
          ))}
        </Masonry>
      )
    }
    render() {
        let content = (this.props.posts && this.state.firstSubscribtion)?this.postList():undefined;
        let loading = (this.props.needWait)?<Loading />:undefined;
        return(
          <div>
            {content}
            {loading}
          </div>
        )
    }
  }

export default createContainer((props)=>{
    let {limitCount, userId} = props;
    let query = (userId)?{'userId':userId}:{};
    var handle = Meteor.subscribe('news_preview', query,limitCount);
    var posts = Post.find(query, {sort:{createdAt:-1}}).fetch();
    var needWait = !!handle.ready()&&!!posts;
    return {
      handleReady:!!handle.ready(),
      needWait:!needWait,
      posts:posts
    }
  }, PostList)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 2017-07-13
    相关资源
    最近更新 更多