undefined collection name Meteor with React - meteor

This Meteor React client code produces browser console error:
ReferenceError: CarsCol is not defined
Any idea why? Thanks
//cars.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { composeWithTracker } from 'react-komposer';
import { ListItems } from '../containers/myList.jsx';
const composer = (props, onData) => {
const sub = Meteor.subscribe('carsCol');
if (sub.ready()) {
const cars = CarsCol.find().fetch(); //<------- error line
onData(null, {cars});
}
};
const Container = composeWithTracker(composer) (ListItems);
ReactDOM.render(<Container />, document.getElementById('react-root'));
//publications.js
const CarsCol = new Mongo.Collection('carsCol');
Meteor.publish('carsCol', function(){
return CarsCol.find();
});

You've defined the collection on the server only, you want to define the collection in both places.
What I would do is define your collection in a separate file and import that.
/api/collections/CarsCol
const CarsCol = new Mongo.Collection('carsCol');
Then in cars.jsx
import CarsCol from '../api/collections/CarsCol'

Related

Vue3 Cannot read globalProperties in component

I am converting an existing app from vue 2 to 3.
I am having troubles with accessing globalProperties in components. In some instances using this.$propertyname returns undefined
In my main.js file:
import { createApp } from 'vue'
import App from './App.vue'
import {
appPageLoader
} from '#/store/reactives'
function mount(el){
let app = createApp(App)
app.config.globalProperties.$pgLoader = appPageLoader
// This console log returns proper value
console.log('^^^', appAnnouncer, app.config.globalProperties.$pgLoader)
app.mount(el)
}
mount('#app')
appPageLoader.js
This file contains a reactive object which we can import and assign to globalProperties $pgLoader
import { reactive } from "vue";
let pgLoader = {
pageLoadAnchor: null,
setFocusToTop () {
if (this.pageLoadAnchor instanceof HTMLElement) {
this.pageLoadAnchor.focus()
} else {
console.log('Cannot setFocusToTop() - pageLoadAnchor not found!')
}
}
}
let ReactivePageLoader = reactive(pgLoader)
export default ReactivePageLoader;
app.vue
When trying to access globalProperties using this.$property, I get undefined in component:
import { getCurrentInstance } from "vue";
mounted() {
this.$pgLoader.pageLoadAnchor = this.$refs.pageLoadAnchor; <- this.$pgLoader returns undefined
getCurrentInstance().appContext.config.globalProperties.$pgLoader = this.$refs.pageLoadAnchor; <- works but cumbersome
},

idle-vue-3 mitt i.$emit is not a function

I am using Quasar 2 Vue 3. When I use idle-vue-3 with mitt in the following way:
import { createApp } from 'vue';
import IdleVue from "idle-vue-3";
import mitt from 'mitt';
import App from "../App.vue";
const app = createApp(App);
const emitter = mitt();
const idleTimeInMillis = 60000;
app.use(IdleVue, {
eventEmitter: emitter, // OR eventEmitter: emitter.emit,
store: Store,
idleTime: idleTimeInMillis,
startAtIdle: false
});
export default app;
I get i.$emit is not a function on the console. Any advice and insight is appreciated.
Remove eventEmitter: emitter,. It is not needed.

Rerendering on async fetch with react-mobx

I'm trying to use mobx-rest with mobx-rest-axios-adapter and mobx-react, and I have trouble making the component rerender upon async data retrieval.
Here's my data model, in state/user.js:
import { Model } from 'mobx-rest';
class User extends Model {
url() {
return '/me';
}
}
export default new User();
This is the React component, in App.js:
import React from 'react';
import { inject, observer } from 'mobx-react';
import { apiClient } from 'mobx-rest';
import createAdapter from 'mobx-rest-axios-adapter';
import axios from 'axios';
import { compose, lifecycle, withProps } from 'recompose';
const accessToken = '...';
const API_URL = '...';
const App = ({ user }) => (
<div>
<strong>email:</strong>
{user.has('email') && user.get('email')}
</div>
);
const withInitialise = lifecycle({
async componentDidMount() {
const { user } = this.props;
const axiosAdapter = createAdapter(axios);
apiClient(axiosAdapter, {
apiPath: API_URL,
commonOptions: {
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
});
await user.fetch();
console.log('email', user.get('email'));
},
});
export default compose(
inject('user'),
observer,
withInitialise,
)(App);
It uses recompose to get the user asynchronously from an API in componentDidMount(), and once available the component is supposed to show the user email. componentDidMount() prints the email once available.
Finally this is index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { Router } from 'react-router';
import App from './App';
import { user } from './state/user';
const documentElement = document.getElementById('ReactApp');
if (!documentElement) {
throw Error('React document element not found');
}
const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();
const stores = { user };
const history = syncHistoryWithStore(browserHistory, routingStore);
ReactDOM.render(
<Provider {...stores}>
<Router history={history}>
<App />
</Router>
</Provider>,
documentElement,
);
My problem is that the component doesn't rerender once the user is retrieved and the email is available, although the console log shows that it is returned ok in the async request. I've tried playing around with mobx-react's computed, but no luck. Any ideas?
I think it will work if you change your compose order of App.js:
export default compose(
inject('user'),
withInitialise,
observer,
)(App);
According to the MobX official document,
Tip: when observer needs to be combined with other decorators or
higher-order-components, make sure that observer is the innermost
(first applied) decorator; otherwise it might do nothing at all.

A valid React element (or null) must be returned

This Meteor React code is producing browser console error:
Warning: ListItems(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Exception from Tracker recompute function:
Any idea why? Thanks
//myList.jsx
import React from 'react';
const renderIfData = (listItems) => {
if (listItems && listItems.length > 0) {
return listItems.map((item) => {
return <li key={item._id}>{item.car}</li>;
});
} else {
return <p> No cars yet!</p>
}
};
export const ListItems = ({listItems}) => {
<ol>{renderIfData(listItems)}</ol>
};
//cars.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { composeWithTracker } from 'react-komposer';
import { ListItems } from '../imports/ui/myList.jsx';
import { CarsCol } from '../imports/api/collections.js';
const composer = (props, onData) => {
const sub = Meteor.subscribe('carsCol');
if (sub.ready()) {
const cars = CarsCol.find().fetch();
onData(null, {cars});
}
};
const Container = composeWithTracker(composer) (ListItems);
ReactDOM.render(<Container />, document.getElementById('react-root'));
Everything looks nice except this part:
return listItems.map((item) => {
return <li key={item._id}>{item.car}</li>;
});
The result of this operation is an array of elements, and React discourages it with exactly the kind of error you're receiving. In fact, in React 16, they promise to allow this, but you're likely using version 15. Anyway, I'd recommend returning a single root element everywhere, so the whole thing would look like
//myList.jsx
import React from 'react';
export const ListItems = ({listItems}) => {
if (listItems && listItems.length > 0) {
return (
<ol>
{listItems.map((item) => (
<li key={item._id}>{item.car}</li>
))}
</ol>
);
} else {
return (
<p>No cars yet!</p>
);
}
};
//cars.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { composeWithTracker } from 'react-komposer';
import { ListItems } from '../imports/ui/myList.jsx';
import { CarsCol } from '../imports/api/collections.js';
const composer = (props, onData) => {
const sub = Meteor.subscribe('carsCol');
if (sub.ready()) {
const cars = CarsCol.find().fetch();
onData(null, {cars});
}
};
const Container = composeWithTracker(composer) (ListItems);
ReactDOM.render(<Container />, document.getElementById('react-root'));

How do I pass Meteor Subscription Data into React Component Props using ES6

Given this subscription, and the React Component below, how do I pass the subscription data in as props 'searchTerms'? Most of the documentation I can find refers to using mixins, but as far as I understand this is an anti pattern in ES6. Thanks!
constructor() {
super();
this.state = {
subscription: {
searchResult: Meteor.subscribe("search", searchValue)
}
}
}
render() {
return (
<div>
<SearchWrapper
searchTerms={this.state.subscription.searchResult}
/>
</div>
)
}
There are a couple options when it comes to creating containers in Meteor. My personal favorite is react-komposer.
Here's what your container would look like using react-komposer. Note that a container is simply a component that just passes data, and in the case of Meteor, provides reactivity.
After npm install --save react-komposer, create a container using:
import { Meteor } from 'meteor/meteor';
import React from 'react';
import { composeWithTracker } from 'react-komposer';
import Component from '../components/Component.jsx';
import { Collection } from '../../api/collection/collection.js';
// Creates a container composer with necessary data for component
const composer = ( props, onData ) => {
const subscription = Meteor.subscribe('Collection.list');
if (subscription.ready()) {
const collection = Collection.find().fetch(); // must use fetch
onData(null, {collection});
}
};
// Creates the container component and links to Meteor Tracker
export default composeWithTracker(composer)(Component);
The standard way of doing this is to use the react-meteor-data package.
meteor add react-meteor-data
Then create a container as follows:
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import SearchWrapper from '../pages/SearchWrapper.jsx';
import { SearchResults } from '../../api/searchResults.js';
export default SearchResultContainer = createContainer(({ params }) => {
const { searchValue } = params;
const searchHandle = Meteor.subscribe('search', searchValue);
const loading = !searchHandleHandle.ready();
const results = SearchResults.find().fetch();
const resultsExist = !loading && !!list;
return {
loading,
results,
resultsExist,
};
}, SearchWrapper);
The returned object from the container is available as props in the wrapped component - SearchWrapper.

Resources