Are there any issues in vue3 when using reactive function on data that also has methods (to get namespaced data and methods in template)? - vuejs3

Is it ok to use reactive function on data that also has methods. The reason for doing it is that otherwise I would have to use .value in template. My goal is to have nested (namespaced) method names in template as well. Would there be another way to get nested (namespaced) method and data names?
Excerpt from code (full example in codepen link below):
setup() {
const feature1 = function () {
const feature1Name = Vue.ref("feature1Default");
const feature1ChangeName = function () {
console.log("called", "feature1ChangeName");
feature1Name.value = "feature1Changed";
};
return { feature1Name, feature1ChangeName };
};
return { feature1: Vue.reactive(feature1()) }
}
https://codepen.io/realmerx/pen/abZqQYL?editors=1010

The functions are unnecessary, it is just a matter of formatting preference
instead of
const feature1 = function() {
const feature1Name = Vue.ref("feature1Default");
const feature1ChangeName = function() {
console.log("called", "feature1ChangeName");
feature1Name.value = "feature1Changed";
};
return { feature1Name, feature1ChangeName };
};
you could do
const feature1 = {
feature1Name: Vue.ref("feature1Default"),
feature1ChangeName: function() {
console.log("called", "feature1ChangeName");
feature1.feature1Name.value = "feature1Changed";
}
};
...but the important part in there seems to be that the ref is encapsulated in a reactive, which I suspect might add some overhead on the processing of reactivity.
You can get around that with a single reactive. You won't need to wrap it in additional reactivity or a function, and the code will work the same way (without any changes needed to the template). Note that the .value is missing from the assignment in the feature2ChangeName method.
const feature2 = Vue.reactive({
feature2Name: "feature2Default",
feature2ChangeName: () => {
feature2.feature2Name = "feature2Changed";
}
});
return {
feature2
};

Related

Share processed state across components

I have three components. My state has a property named state.selected.
Currently in my mapStateToProps I am doing this in all three components:
function mapStateToProps(state, ownProps) {
return { selected:state.selected }
}
In each presentational component I then do the same processing called getSelectedDisplays. This function does some processing based on what is selected.
var PresentaionalComponent_1 = React.createClass({
render: function() {
var displays = getSelectedDisplays();
// custom processing on `displays` for coponent 1
}
})
var PresentaionalComponent_2 = React.createClass({
render: function() {
var displays = getSelectedDisplays();
// custom processing on `displays` for coponent 2
}
})
var PresentaionalComponent_3 = React.createClass({
render: function() {
var displays = getSelectedDisplays();
// custom processing on `displays` for coponent 3
}
})
No control over parent component
I was hoping to avoid wrapping the three components in an extra div as my only need was to pass to them the result of getSelectedDisplays. I was hoping to avoid this:
React.createElement(OverContainer)
and OverContainer would be the only one receiving state.selected and it would then do getSelectedDisplays then it will render the three components with it as a prop:
var OverPresentaional = React.createClass({
render: function() {
var { selected } = this.props;
var display = getSelectedDisplays(selected);
return React.createElement('div', {},
React.createElement(PresentaionalComponent_1, { display });
React.createElement(PresentaionalComponent_2, { display });
React.createElement(PresentaionalComponent_3, { display });
);
}
}});
Is this possible without wrapping them in a parent div?
You can create a selector, that will encapsulate getting the data from the state, and computing derived properties:
export const getSelectedDisplays = (state) => {
const selected = state.selected;
const selectedDisplays = // whatever logic you need to get selectedDisplays from selected
return {
selectedDisplays;
};
};
Now for each component:
import { getSelectedDisplays } from 'selectorFile';
function mapStateToProps(state, ownProps) {
return getSelectedDisplays(state);
}
var PresentaionalComponent_1 = React.createClass({
render: function() {
var displays = this.props.selectedDisplays;
// custom processing on `displays` for coponent 1
}
})
etc...
The only problem is, that getting the data, and the logic will be performed 3 times, instead of ones. To solve that, you can create a memoized selector, that will cache and return the same result, if the supplied params (state in this case) haven't changed. Reselect is a library the creates memoized selectors for you.

arguments.callee.caller does not work anymore with ES6 [duplicate]

I have an EventListener that listens to the entire document and records keystrokes, but I want to remove this Listener when certain conditions are met.
The following is a snippet of my code:
document.addEventListener('keyup', function(e) {
var letter_entered = String.fromCharCode(e.keyCode).toLowerCase();
player.makeGuess(letter_entered);
if(player.win_status === true || player.lose_status === true) {
document.removeEventListener('keyup', arguments.callee, false);
}
});
This works, however according to the Mozilla Developer Docs this method has been deprecated.
I'm aware that I can simply name the function, but is there an alternative that would allow me to continue using the unnamed function?
Use the following process:
Create a variable
Assign an anonymous function to the variable
Invoke it with the variable reference
The anonymous function references itself using the variable name
Use it as such:
var foo = function(e)
{
"use strict";
console.log(e);
document.removeEventListener('keyup', foo, false);
}
document.addEventListener('keyup', foo);
You can solve this problem easily using the y combinator:
function y(f) {
return function () {
return f.bind(null, y(f)).apply(this, arguments);
};
}
Now you can rewrite your code as follows:
document.addEventListener("keyup", y(function (callee, e) {
player.makeGuess(String.fromCharCode(e.keyCode).toLowerCase());
if (player.win_status || player.lose_status) document
.removeEventListener("keyup", callee);
}));
That's all folks.
Use another anonymous function as a wrapper to store a named function (callee shim) to your original function:
document.addEventListener('keyup', (function(e)
{
var aFunction = function()
{
var letter_entered = String.fromCharCode(e.keyCode).toLowerCase();
player.makeGuess(letter_entered);
};
if(player.win_status === true || player.lose_status === true)
{
document.removeEventListener('keyup', window, false);
}
else
{
aFunction();
}
}
), false);
References
Strict Mode is Coming to Town
ES3.1:arguments.callee
Recursion with anonymous (inline) functions in XPath 3.0
Recursion with anonymous (inline) functions in XPath 3.0, Part II

Keep reactive helper from updating a meteor template

I'm looking at how to turn off reactivity within a template helper function. I only want the data rendered when the template is initially populated and not when the underlying data changes.
My current helper has two reactive variables: one is a Session.get() and the other is a Collection.findOne(). I've tried to wrap the Session.get() in a Tracker.nonreactive() call and set the reactive option to false on the Collection.findOne() but I'm still seeing the reactive behavior.
Template.characterSkills.helpers({
data : function () {
var characterID = Tracker.nonreactive(function () {
return Session.get("currentCharacterID");
});
if(characterID) {
var record = Characters.findOne(
{ _id : characterID },
{
reactive : false,
fields : { skills : 1 }
}
);
if(record && record.skills)
return record.skills;
}
}
});
I've been trying to work this issue for about half a day now. Any help would be greatly appreciated.
You're using the name data for your helper which is reserved. So if you have another helper or an iron router route with data in it, or this template is a subtemplate of another template its likely to redraw too.
Try renaming it to something else.
You can use a closure to find and set the record when the template is rendered and then return a static object in the template helper:
Template.characterSkills.helpers({
dataItem: function(){
return record.get();
}
});
Template.characterSkills.onCreated(function(){
record.set(Session.get("currentCharacterID"));
});
var record = function(){
var local = {};
return {
set: function(characterId){
if ( characterID) local = Characters.findOne({_id: characterID},{fields : { skills : 1 }});
else local = null;
},
get: function(){
return local;
}
}();
Although even this feels like too much work. Surely there's an easier way.

Async call generates " Error: Can't wait without a "fiber", even with _wrapAsync

I've been having a problem using an RSS parser in meteor. It's an async call, so it needs ot be wrapped, however it still doesn't seem to work. I presume this is because the anonymous on('readable' function is outside the fiber, but I can't see how to resolve it.
var FeedParser = Meteor.require('feedparser');
var request = Meteor.require('request');
function getBlog(url, parameter, id){
request(parameter)
.on('error', function (error) {
console.error(error);
})
.pipe(new FeedParser())
.on('error', function (error) {
console.error(error);
})
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
});
}
var wrappedGetBlog = Meteor._wrapAsync(getBlog);
Meteor.methods({
blog: function (url, parameter, id) {
console.log('parsing blog');
var items = wrappedGetBlog(url, parameter, id);
}
});
Meteor._wrapAsync() expects the wrapped function to return error and result to a callback. Your function, getBlog(), does not do that so _wrapAsync is not the right approach.
I have wrapped that function before but used a Future.
That approach allowed me to call feedparser from a Meteor.method(), which doesn't allow async functions, but you are also trying to do an insert inside the readable event. I think that insert will complain if it is not in a fiber. Maybe like this would also be necessary:
var r = request( parameter );
r.on( 'response' , function(){
var fp = r.pipe( new FeedParser() ); //need feedparser object as variable to pass to bindEnvironment
fp.on('readable', Meteor.bindEnvironment(
function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
}
, function( error ){ console.log( error );}
, fp // variable applied as `this` inside call of first function
));
});
Fibers is another option...
var Fiber = Npm.require( "fibers" );
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Fiber( function(){
Items.insert(new_item);
Fiber.yield();
}).run();
}
});

Reactive collection query in a template

I have a Template named movies, that has a method that returns a list of objects from a collection.
The query to generate that list of objects is created dynamically using data from another template method.
I would like to re-render the template, or just the components associated with that specific template method, whenever the filter data changes.
Here are the two methods used:
Template.movies.filter = function () {
if (Session.equals("filter", undefined)) {
return {};
}
return Session.get("filter");
};
Template.movies.movies = function () {
return Movies.find(Template.movies.filter(), {sort: [["Poster", "desc"]]});
};
On the HTML side it's a simple {{#each movies}}{{> movie}}{{/each}} to show the results from the movies method.
The problem is that when Session.get("filter") changes and therefore so does Template.movies.filter(), the HTML component relying on Template.movies.movies() data won't be updated with the new query results.
How would I achieve that behavior?
The easiest way is to just make a javascript function that both helpers utilize:
var getFilter = function() {
if (Session.equals("filter", undefined)) {
return {};
}
return Session.get("filter")
}
Template.movies.filter = function() { return getFilter(); }
Template.movies.movies = function () {
return Movies.find(getFilter(), {sort: [["Poster", "desc"]]});
};
This will react as you expect.

Resources