React: How to reactively update subscriptions


#1

Hi guys,
I know this question was asked a couple times before, but even applying the other thread’s solutions results in some very weird behavior for me. I should add I’m still very new to React, and I miss Blaze dearly :sweat:

The basic situation is: I have a search form somewhere in my container. I want to adjust its subscriptions based on this search term input.

So what I’m doing right now is this:
I let the search term bubble up from the form component all the way to the container (using _.debounce(100) to not spam the server). I tried directly using this info in the createContainer call, but that didn’t really work out. So I read here in the forum, a clean approach is to wrap the reactive container in a normal container that handles the data. So it looks like this now:

const InnerListContainer = createContainer((props) => {
  const searchTerm = props.searchTerm;
  const changeSearchTerm = props.changeSearchTerm;
  const subscriptionHandle = Meteor.subscribe('books/search', searchTerm);

  return {
    books:
      Books.search( // Books.search is a wrapper around find that is also used in the publication
        searchTerm, {}, { sort: { englishName: 1 } }
      ).fetch(),
    booksReady: subscriptionHandle.ready,
    searchHandler: changeSearchTerm,
  };
}, ListPage);


export default class ListContainer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      searchTerm: '',
    };
  }

  changeSearchTerm(newTerm) {
    this.setState({ searchTerm: newTerm });
  }

  render() {
    const { searchTerm } = this.state;
    return <InnerListContainer
      searchTerm={searchTerm}
      changeSearchTerm={this.changeSearchTerm.bind(this)}
    />;
  }
}

That by itself also didn’t really work, though. Mysteriously, the more characters my search term had, the less likely it was to work. For strings longer than 1 character, it happened pretty much always that the subscription finished correctly, but the component never updated (and thus my loading indicator kept spinning forever).

So randomly, I found out how to make it work. I had to add two things to the publication function:

  1. this.unblock() using meteorhacks:unblock
  2. Meteor._sleepForMs(100); after the unblock.

Yup. Now it worked fine. However, when the search does not return any results, the loading indicator never vanishes, because again the component does not get updated, even though the subscription returned already.

So… The faster the subscription returns, the less it is likely that the component gets updated? What?

There must be some nice standard pattern how to do these reactive subscriptions… They were so nice and easy in Blaze using this.subscribe and a ReactiveVar! Can someone point me in the right direction or help me understand what’s going on?

Thanks!