强制停止容器订阅(Meteor + React + CreateContainer)

时间:2016-11-26 14:04:42

标签: reactjs meteor react-router

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

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

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

PostList 此组件获取属性,订阅和呈现子组件。

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)

2 个答案:

答案 0 :(得分:0)

我找到了解决方案。

很明显。但不知怎的,在我看来,有一个更好的。

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

什么是消极的?我们需要在子组件中传输订阅对象,然后手动停止订阅。并且要在每个组件中执行此操作,这可能会导致类似的问题。

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

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

答案 1 :(得分:0)

使用manualy停止订阅的解决方案并不好。你理解为什么。

看一下非常好的解决方案。 我们需要保护自己免受以下事实:当您访问组件时,我们可以获得与其他组件无关的数据。是类型具有相同的数据,但是 - 与其他组件。它可以与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)