reactjsmeteorsubscriptionmeteor-react

Is it possible to pass data up to withTracker directly from it's lower order component?


I'm working in an existing codebase that uses the React, Meteor, and react-meteor-data combo.

Everything has been going relatively fine up until I tried implementing a search feature using withTracker, React Select, and Meteor's subscription functionality.

import { CollectionAPI } from '../arbitrary_meteormongo_collection';

export const WriteableConnectionToCollection = withTracker(props => {
    let connection = Meteor.subscribe('COLLECTION_NAME.searchByName', SEARCH_TEXT_HERE); 
    let isLoading = connection.ready();

    return {
        ...props,
        isLoading: isLoading,
        collection: CollectionAPI.find().fetch()
    }
})(PRESENTATIONAL_COMPONENT);

I've googled around and saw that a common solution for getting data to Meteor.subscribe is to use things like URL parameters, though as I am working in an existing codebase, this change would also need to be implemented in various locations.

Another way I have found is to pass the input field's value to the parent component by keeping track of the input field state in the parent component's state, though this is clearly breaking the principal of separation of concerns:

Parent Component

export const ParentComponent = React.createClass({
    getInitialState() {
        return {
            inputFieldValue: undefined
        }
    },

    onChange(change) {
        this.setState(inputFieldValue);
    },

    render() {
        return (
            <Search 
                onChange={this.onChange}
                inputFieldValue={this.state.inputFieldValue}
            />
    }
}

withTracker HOC

import { CollectionAPI } from '../arbitrary_meteormongo_collection';

export const WriteableConnectionToCollection = withTracker(props => {
    let connection = Meteor.subscribe('COLLECTION_NAME.searchByName', this.props.inputFieldValue); 
    let isLoading = connection.ready();

    return {
        ...props,
        isLoading: isLoading,
        collection: CollectionAPI.find().fetch()
    }
});

InputField Component

import { WriteableConnectionToCollection } from './connections/writeableconnection.js';

const InputFieldComponent = React.createClass({

    render() {
        <InputField 
            onInputChange={this.props.onChange}
        />
    }
}

export default WritableConnectionToCollection(InputFieldComponent);

Is this the only way to do things with this particular package/framework combo or is there a simpler way that I'm just not seeing?


Solution

  • As Christian Fritz had mentioned in a comment under my original question, I can use ReactiveVar to be able to pass input in and out of my connection component:

    export const WritableConnection = function (subscriptionName, collectionAPI) {
        /**
         *  ReactiveVar must be outside of withTracker. If the it was inside withTracker's scope, 
         *  anytime a user would use .set(ANY_VALUE), it would overwrite whatever was in it first,
         *  and then re-initialize.
         **/
        const input = new ReactiveVar(undefined);
    
        return withTracker(props => {
            const connection = Meteor.subscribe(subscriptionName, input.get());
            const isLoading = connection.ready();
    
            return {
                ...props,
                isLoading: isLoading,
                collection: collectionAPI.find().fetch(),
                setSearchText: (text) => input.set(text),
                getSearchText: () => input.get()
            }
        })
    }