2017-10-27 2 views
0

Ich habe Probleme mit Abonnement und Reagieren, vielleicht mache ich es nicht richtig.Meteor/React - Update-Abonnement bei Statuswechsel

Hier ist das Problem: ich möchte eine Seite mit einer Filmliste erstellen, die von einer Mongo-Sammlung zur Verfügung gestellt wird, gibt es auch einen Genre-Filter und eine Schaltfläche "mehr laden".

Wenn ich nur mehr laden, aktualisiere ich die Publikation, um bestehende Artikel zu überspringen und neue zu liefern, funktioniert es gut.

Wenn ich mein Genre Filter ändern, ich meine Publikation einfach mit diesem Filter aktualisieren, funktioniert auch ...

Aber wenn ich, dass zwei Aktionen zusammen zu tun, zum Beispiel: Laden mehr, filtere dann nach Genre, Das Ergebnis sieht schlecht aus und scheint die alten Ergebnisse beizubehalten. Die Seitennumerierung wird nicht auf den Standardwert zurückgesetzt.

Hier ist eine vereinfachte Version meines Codes.

Server-Seite Veröffentlichung:

Meteor.publish('movies.published', function (sort = null, skip = 0, limit = 20, filters = {}) { 

    if (! sort || typeof sort !== 'object') { 
    sort = {releaseDate: -1}; 
    } 

    let params = Object.assign({ 
    webTorrents: { 
     $exists: true, 
     $ne: [] 
    }, 
    status: 1, 
    }, filters); 

    console.log('----------------Publishing--------------------'); 

    let cursor = Movies.find(params, { 
    sort: sort, 
    skip: skip, 
    limit: limit 
    }); 

    // Test publication 
    // Count is always good 
    console.log(cursor.fetch().length); 

    return cursor; 

}); 

Client-Seite Komponente:

import React from "react"; 
import TrackerReact from "meteor/ultimatejs:tracker-react"; 

import Movies from "/imports/api/movies/movies.js"; 
import Genres from "/imports/api/genres/genres.js"; 

const itemPerPage = 1; // let's say 1 item to simplify our test 
const defaultOrder = 'releaseDate'; 

export default class Home extends TrackerReact(React.Component) { 

    constructor(props) { 
    super(props); 

    let options = Object.assign(Meteor.settings.public.list.options, {genres: Genres.find()}); 

    this.state = { 
     subscription: { 
     movies: Meteor.subscribe('movies.published', {[defaultOrder]: -1}, 0, itemPerPage), 
     }, 
     filters: {}, 
     sort: defaultOrder, 
     options: options, 
    }; 
    } 

    componentWillUnmount() { 
    this.state.subscription.movies.stop(); 
    } 

    filterByGenre(event) { 
    let filterGenre = { 
     ['genres.slug']: event.target.value !== '' ? event.target.value : {$ne: null} 
    }; 
    this.updateFilters(filterGenre); 
    } 

    updateFilters(values) { 

    // Merge filters 
    let filters = Object.assign(this.state.filters, values); 
    console.log('updating filters', filters); 

    // Update subscription with filters values 
    // here i must reset the pagination 
    this.state.subscription.movies.stop(); 
    let newSubscription = Object.assign({}, this.state.subscription, { 
     movies: Meteor.subscribe('movies.published', {[this.state.sort]: -1}, 0, itemPerPage, filters), 
    }); 

    this.setState({ 
     subscription: newSubscription, 
     filters: filters, 
    }) 

    } 

    loadMore(skip) { 

    // Add an item 
    let newSubscription = Object.assign({}, this.state.subscription, { 
     movies: Meteor.subscribe('movies.published', {[this.state.sort]: -1}, skip, itemPerPage, this.state.filters), 
    }); 

    this.setState({ 
     subscription: newSubscription, 
    }); 

    } 

    render() { 

    if (! this.state.subscription.movies.ready()) { 
     return (<div>loading...</div>); 
    } 

    // Get our items 
    let movies = Movies.find().fetch(); 

    return (
      <div> 

       <div className="container-fluid"> 

      {/* Filters */} 
      <form className="form form-inline"> 
      <div className="form-group m-r m-2x"> 
       <select className="form-control" value={this.state.filters['genres.slug'] && this.state.filters['genres.slug']} onChange={this.filterByGenre.bind(this)}> 
       <option value="">Genres</option> 
       {this.state.options.genres.map((genre, key) => { 
        return <option key={key} value={genre.slug}>{genre.name}</option> 
       })} 
       </select> 
      </div> 
      </form> 

      <hr /> 

      {/* list */} 
      <div className="row list"> 
      {movies.map((movie) => { 
       return (
       <div key={movie._id} className="col-xs-2">{movie.title}</div> 
      ) 
      })} 
      </div> 

      {/* Load more */} 
      <button id="load-more" type="button" className="btn btn-sm btn-info" onClick={this.loadMore.bind(this, movies.length)}> 
      Load more 
      </button> 

       </div> 

     <div className="col-xs-3 pull-right text-right">{movies.length} movies</div> 

     <hr /> 

      </div> 
    ) 

    } 

} 

Hier ist ein besserer Weg, das zu tun? Danke für Ihre Hilfe!

Antwort

0

Ich gründete, was das Problem war. Wenn ich die Methode loadMore verwende, stoppe ich nicht das alte Abonnement (ich möchte alte Elemente behalten und die neuen hinzufügen), das Problem ist, dass "addMore" -Abonnement in meinem Abonnement-Array beibehalten wurde (siehe Meteor.default_connection._subscriptions). Also, wenn ich Filter ändern schließe ich nur die letzte "loadMore" Abonnement ...

2 Lösungen:

  • Schleife durch Meteor.default_connection._subscriptions, alte "LoadMore" Abonnements zu schließen.
  • Behalten Sie eine Kopie der alten Elemente in einem Array und füge sie mit neuen, nachdem Abonnement aktualisiert wurde, dass was ich getan habe.

Aktualisiert Client-Code:

import React from "react"; 
import TrackerReact from "meteor/ultimatejs:tracker-react"; 

import Movies from "/imports/api/movies/movies.js"; 
import Genres from "/imports/api/genres/genres.js"; 

const itemPerPage = 1; 
const defaultOrder = 'releaseDate'; 

export default class Home extends TrackerReact(React.Component) { 

    constructor(props) { 
    super(props); 

    let options = Object.assign(Meteor.settings.public.list.options, {genres: Genres.find()}); 

    this.state = { 
     subscription: { 
     movies: Meteor.subscribe('movies.published', {[defaultOrder]: -1}, 0, itemPerPage), 
     moviesCount: Meteor.subscribe('movies.count'), 
     }, 
     skip: 0, 
     filters: {}, 
     sort: defaultOrder, 
     options: options, 
    }; 

    // our local data 
    this.data = []; 
    this.previous = []; 
    } 

    componentWillUnmount() { 
    this.state.subscription.movies.stop(); 
    } 

    filterByGenre(event) { 
    let filterGenre = { 
     ['genres.slug']: event.target.value !== '' ? event.target.value : {$ne: null} 
    }; 
    this.updateFilters(filterGenre); 
    } 

    updateFilters(values) { 

    // Merge filters 
    let filters = Object.assign(this.state.filters, values); 

    // Update subscription (reset pagination) 
    this.state.subscription.movies.stop(); 
    this.state.subscription.moviesCount.stop(); 
    let newSubscription = Object.assign({}, this.state.subscription, { 
     movies: Meteor.subscribe('movies.published', {[this.state.sort]: -1}, 0, itemPerPage, filters), 
     moviesCount: Meteor.subscribe('movies.count', filters), 
    }); 

    this.setState({ 
     subscription: newSubscription, 
     filters: filters, 
     skip: 0, 
    }); 

    } 

    loadMore() { 

    // Keep a copy of previous page items 
    this.previous = this.data; 

    // Update subscription 
    this.state.subscription.movies.stop(); 
    let newSubscription = Object.assign({}, this.state.subscription, { 
     movies: Meteor.subscribe('movies.published', {[this.state.sort]: -1}, this.previous.length, itemPerPage, this.state.filters) 
    }); 

    this.setState({ 
     subscription: newSubscription, 
     skip: this.previous.length, 
    }); 

    } 

    getMovies() { 

    // Wait subscription ready to avoid replacing items 
    if (! this.state.subscription.movies.ready()) { 
     return this.previous; 
    } 

    // Get new data and merge with old ones 
    let newData = Movies.find().fetch(); 
    this.data = this.previous.concat(newData); 

    // Reset previous array 
    this.previous = []; 

    return this.data; 
    } 

    render() { 

    if (! this.state.subscription.movies.ready() && ! this.previous.length) { 
     return (<div>loading...</div>); 
    } 

    // Get our items 
    let movies = this.getMovies(); 

    return (
     <div> 

     <div className="container-fluid"> 

      {/* Filters */} 
      <form className="form form-inline"> 
      <div className="form-group m-r m-2x"> 
       <select className="form-control" value={this.state.filters['genres.slug'] && this.state.filters['genres.slug']} onChange={this.filterByGenre.bind(this)}> 
       <option value="">Genres</option> 
       {this.state.options.genres.map((genre, key) => { 
        return <option key={key} value={genre.slug}>{genre.name}</option> 
       })} 
       </select> 
      </div> 
      </form> 

      <hr /> 

      {/* list */} 
      <div className="row list"> 
      {movies.map((movie) => { 
       return (
       <div key={movie._id} className="col-xs-2">{movie.title}</div> 
      ) 
      })} 
      </div> 

      {/* Load more */} 
      <div className="row"> 
      <div className="col-xs-12 text-center"> 
       {Counts.get('movies.count') > movies.length && 
       <button id="load-more" type="button" className="btn btn-sm btn-info" onClick={this.loadMore.bind(this)}> 
        Load more 
       </button> 
       } 
      </div> 
      </div> 

     </div> 

     <div className="col-xs-3 pull-right text-right">{Counts.get('movies.count')} movies</div> 

     <hr /> 

     </div> 
    ) 

    } 

} 

Ich hoffe, es wird Ihnen helfen!

Verwandte Themen