trying to render a list. nothing showing on screen - meteor

Using Meteor and React. Trying to render a list of data from the server onto the client. the server's data looks like this:
Searches.insert({text: data.items[i].snippet.title});
if(Meteor.isClient) {
Searches = new Meteor.Collection('searches');
Meteor.subscribe('allSearches');
}
....
renderTasks(){
return this.data.searches.map((searches) => {
return <SearchResultItem searches={searches} />;
});
},
....
<ul>
{this.renderTasks()}
</ul>
....
SearchResultItem = React.createClass({
render(){
return
<li>
{this.props.searches.text}
</li>
}
});

You need to provide an unique key-prop to your dynamic child elements
Dynamic Children
The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key:
render: function() {
var results = this.props.results;
return (
<ol>
{results.map(function(result) {
return <li key={result.id}>{result.text}</li>;
})}
</ol>
);
}
(REF: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children)
In your case:
renderTasks(){
return this.data.searches.map((searches, i) => {
return <SearchResultItem key={i} searches={searches} />;
});
}

Related

Data Not Rendering Using Next Js

I'm trying to use data from an API following the steps provided in the Next docs. My data does not render, this is my first foray into React so I'm not sure what I'm missing. Would somebody mind pointing out my error please?
export default function Home({ items }) {
console.log(items) // Items listed in the console fine
return (
<ul>
{items.map((item, index) => {
<li key={index}>{item.description}</li>
})}
</ul>
)
}
export async function getStaticProps() {
// Get Data Here
return { props: {
items
}}
};
As you can see by my note above, the console lists the items as expected. I can also see the items array in the React Dev Tools in chrome.
The below console.logs as expected but again nothing is rendered to the browser.
export default function Home({items}) {
return (
<ul>
{items.map((item, index) => {
console.log(item.description);
<li key={index}>{item.description}</li>
})}
</ul>
)
}
{items.map((item, index) => {
console.log(item.description);
<li key={index}>{item.description}</li>
})}
should be:
{items.map((item, index) => {
console.log(item.description);
return <li key={index}>{item.description}</li>
})}

Search and Sort on the same page

I'm trying to implement sort and search to my items, so i started with sort and it works:
Template
<button class="sort">Sort</button>
{{#each cvs}}
{{> Interviu}}
{{/each}}
JS:
Template.Interviuri.onCreated(function () {
var self = this
self.autorun(function () {
self.sortOrder = new ReactiveVar(-1)
})
Template.Interviuri.helpers({
cvs() {
const instance = Template.instance()
return Cvs.find({}, { sort: { createdAt: instance.sortOrder.get() } })
},
})
Template.Interviuri.events({
'click .sort'(event, instance) {
instance.sortOrder.set(instance.sortOrder.get() * -1)
Next i wanted to implement Search on the same page. So the best way i could found was EasySearch.
But using EasySearch, it means i must change the way my items are being displayed. And then the sort doesn't work anymore.
Template
<div class="searchBox pull-right">
{{> EasySearch.Input index=cvsIndex attributes=searchAttributes }}
</div>
{{#EasySearch.Each index=cvsIndex }}
{{> Interviu}}
{{/EasySearch.Each}}
Collection
CvsIndex = new EasySearch.Index({
collection: Cvs,
fields: ['name'],
engine: new EasySearch.Minimongo()
})
JS
cvsIndex: () => CvsIndex,
How can i have both search and sort working at the same time?
With EasySearch you can use two methods on your index, namely getComponentDict() and getComponentMethods().
With getComponentDict() you can access search definition and options:
index.getComponentDict().get('searchDefinition');
index.getComponentDict().get('searchOptions');
You also have the corresponding setters to change the search definition/option.
getComponentMethods has mehods like
index.getComponentMethods().loadMore(integer);
index.getComponentMethods().hasMoreDocuments();
index.getComponentMethods().addProps(prop, value);
index.getComponentMethods().removeProps([prop])
From that you can set your prop, say index.getComponentMethods().addProp('sort', -1) and then on the index definition, in your MongoDB engine, set the sort from that prop:
index = new EasySearch.index({
// other parameters
engine: new EasySearch.MongoDB({
sort: function(searchObject, options) {
if(options.search.props.sort) {
return parseInt(options.search.props.sort);
}
return 1;
}
})
});
See EasySearch Engines for more info.

React + Meteor keyed object warning

How do I get rid of this warning? If I delete task.created there is no warning. Can't figure this one out. See my code example below:
Warning: Any use of a keyed object should be wrapped in React.addons.createFragment(object) before being passed as a child.
ShowAllPosts = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
// This function knows how to listen to Meteor's reactive data sources,
// such as collection queries
return {
// Returns an array with all items in the collection
tweets: Posts.find().fetch().reverse()
}
},
render() {
var showHTML = this.data.posts.map(function (task) {
return (
<div key={task._id}>
<img className="profile-pic" src="images/puppy.jpeg" />
{task.content}
<br />
Date: {task.created}, Get Link, id: {task._id}
<hr />
</div>
);
});
return (
<ul>
<hr />
{/* Access the data from getMeteorData() on this.data */}
{ showHTML }
</ul>
);
}
});
I assume that task.created is a Date object.
React isn't casting dates to strings automatically, so you need to write something like {task.created.toString()} or use package like moment to format your date.

The most concise and elegant way to break the reactivity of helpers in meteor

I want to use my collection players in an helper... for several reasons I would like this collection will be not reactive. I would like just a first call to the database to display the collection. I tried to use reactivates:false option but in this case the collection remains empty after loading and nothing display.
Template.myGame.helpers({
players: function () {
return Players.find({}, {reactive: false});
}
})
<ul>
{{#each players}}
{{> player}}
{{/each}}
</ul>
You're effectively looking for a way to return data non-reactively, but only once it's ready. This can be achieved with a subscription handle (assuming you've removed "autopublish").
playersSub = Meteor.subscribe('players', ...);
Template.myGame.helpers({
players: function() {
if (playersSub.ready()) {
return Players.find({}, {reactive: false});
} else {
return [];
}
}
});
NB - it is actually possible to get the subscription readiness (non-reactively) without having a handle, by using Players._connection._subscriptions.[SUB_ID].ready, but I wouldn't recommend this as it's not part of the public API.

Meteor - Helper comparing two different cursors

I'm using a template helper that returns a comparison between a specific cursor and every iteration of the documents from another cursor. The 'inside' value is stored inside the 'City' collection.
I know that storing a unique 'inside' value on each of the documents inside the 'Places' collection would solve this problem, but you only can be 'inside' one place in the each 'City', this would be a performance issue.
Helpers:
Template.listPlaces.helpers({
places: function () {
return Places.find({});
},
insidePlace: function () {
return City.findOne({_id: this._id}).inside === places._id;
}
]);
Template:
<ul>
{{#each places}}
{{#if insidePlace}}Active{{else}}Inactive{{/if}}
{{/each}}
</ul>
I know a solution would be to have a cursor observer running that would update a Session variable with the 'inside' value each time City.inside gets updated, but I would like to know if there is a better solution.
Have you considered using a transform?
Template.listPlaces.helpers({
places: function () {
var transform = function(doc) {
var city = Cities.findOne({_id: doc._id});
doc.insidePlace = (city.inside == doc._id)
return doc;
}
return Places.find({}, {transform: transform});
},
]);

Resources