Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request/discussion: pass in methods during component construction #284

Closed
TehShrike opened this issue Feb 9, 2017 · 9 comments
Closed
Milestone

Comments

@TehShrike
Copy link
Member

I have run into some cases where I want my components to be able to be passed methods at construction time.

I can't require/import the library inside the component, because it's a stateful module that the app should compose with the component.

I can't use events in these cases, because I need to capture the return value of the methods.

So far, I've gotten around this by passing in the functions on the data object, as with the svelte-state-renderer here.

This works fine when calling them from the template, but it looks pretty clunky in the lifecycle functions and custom methods - I have to call something like this.get('dispatch')(action).then(...) instead of this.dispatch(action).then(...).

This is a bit of an annoyance. It would be nice if I could pass in dispatch or makePath methods when constructing the component.

Is it worth it? I'm not sure, what do you think?

@Rich-Harris
Copy link
Member

Could you do it this way?

import Widget from './Widget.html';

Widget.prototype.dispatch = function dispatch ( action ) {
  // ...
};

const widget = new Widget({...});

@TehShrike
Copy link
Member Author

No, because different instantiations of the same widget could get different stateful dispatch or makePath functions passed to them depending on the environment (for testing, each unit test is going to pass in its own sandboxed version).

I can add functions to the component on the line right after I instantiate it, but then those functions aren't available during the first render if they're called within the template.

I might be able to work something out by making my own custom component instantiating function that fiddles with prototypes and makes a custom constructor for each instantiation.

@evs-chris
Copy link
Contributor

What about making the prototype dispatch a facade for return this.get('dispatcher')(action)?

@TehShrike
Copy link
Member Author

I'm not big on adding prototypes to the original constructor because

  1. they're global - where is it appropriate to add them? Should I add them in every place where I import the component? Every single unit test file?
  2. I have a lot of components - every view that the router mounts is a different component.

I would probably end up with a file that would import every single component that I mount as a view, and add the boilerplate prototype methods to each one. And then I would call that file from my app's entry point, plus every unit test file.

It would get the job done, but would be a bit of an ugly setup step.

@Rich-Harris
Copy link
Member

I guess you would have to do...

const widget = Object.create( Widget.prototype );
widget.dispatch = function dispatch ( action ) {...};

Widget.call( widget, {
  target: document.querySelector( '.target' )
});

...and so on.

@TehShrike
Copy link
Member Author

Ah yeah, I was just trying to work out the correct way to finagle those prototypes together :-)

So yeah, that accomplishes what I want - the ability to add custom methods at instantiation time.

I can toss that in a module and call instantiateWithMethods(Widget, options, methods) everywhere instead of using new Widget({ methods, ...options }).

It is a bit sad to have to add that extra indirection at instantiation - I'd have to make the choice whether I liked that better or worse than using this.get('dispatch')(action) in components, and take the lesser of two awkwardnesses.

I'd still like to be able to pass in custom methods straight to the constructor - but of course, that doesn't mean that it's worth adding.

@TehShrike
Copy link
Member Author

For future reference:

function instantiateWithMethods(Component, options, methods) {
	const coolPrototype = Object.assign(Object.create(Component.prototype), methods)
	return Component.call(coolPrototype, options)
}

@Rich-Harris Rich-Harris added this to the N/A milestone Mar 1, 2017
@Rich-Harris
Copy link
Member

I'll close this issue as it's unlikely we'll add this as an officially supported API, and because it's possible (if a little unsightly) to do without any changes since components are just JS objects with prototypes. Thanks

@TehShrike
Copy link
Member Author

This way of adding methods before instantiation doesn't seem to work any more - in this code:

const augmented = Object.assign(Object.create(Component.prototype), methods)
return Component.call(augmented, options)

Component.call returns undefined.

TehShrike added a commit to TehShrike/svelte-state-renderer that referenced this issue May 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants