What is the reason for binding arrow function in javascript? [duplicate] - javascript

This question already has answers here:
How does technique for making a copy of a function works in JavaScript (Storybook example)
(2 answers)
Closed last month.
Going through the storybook documentation
essentially this part caught my eye:
const Template = (args) => ({
//👇 Your template goes here
});
export const Primary = Template.bind({});
So I got curious, what is the reason for calling bind on an arrow function? I tried it and the only effect I've seen is that it is hiding the function implementation from toString.
Primary.toString()
> 'function () { [native code] }'

It seems completely pointless at first, but it's a way to create a new function that does the same as the original one. In another example further down on that page, you can see
const Template = (args) => ({
//👇 Your template goes here
});
ArrayInclude = Template.bind({})
ArrayInclude.parameters = { controls: { include: ['foo', 'bar'] } };
RegexInclude = Template.bind({})
RegexInclude.parameters = { controls: { include: /^hello*/ } };
ArrayExclude = Template.bind({})
ArrayExclude.parameters = { controls: { exclude: ['foo', 'bar'] } };
RegexExclude = Template.bind({})
RegexExclude.parameters = { controls: { exclude: /^hello*/ } };
which creates multiple bound function objects from the same Template and assigns different .parameters to them.
The same could be achieved with
const Primary = Template.bind();
const Primary = Template.bind(null);
const Primary = (args) => Template(args);
// etc

Related

Javascript use decorator to change static class field value?

Is it possible for a JS field decorator to change its value?
A simplified use case would be something like this:
const addItem = (newValue) => {
return function (target) {
target.value.push(newValue);
};
};
class Test {
#addItem(4)
static values = [1,2,3];
}
const test = new Test();
console.log(test.constructor.values) // [1,2,3,4]
Using the following experimental decorators:
'#babel/plugin-proposal-decorators',
{
version: '2018-09',
decoratorsBeforeExport: true,
},
End goal is to make a decorator to inject tailwind style sheets into a lit elements static styles. Currently using a mixin for this but just doing this for fun and to learn whats possible with decorators.
Update to Barmars comments
When trying to return a value from the inner function, I end up getting an error:
export const addItem = (value) => {
return function (target) {
return [value];
};
};
Uncaught TypeError: An element descriptor's .kind property must be either "method" or "field", but a decorator created an element descriptor with .kind "undefined"
Looking at the documentation, the variables getting passed to each of these functions doesn't seem to match either.
function logged(value, { kind, name }) {
if (kind === "field") {
return function (initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
}
When running that example, the 2nd parameter to logged() is undefined. "initialValue" also is an object, not the value:
Object { kind: "field", key: "styles", placement: "own", descriptor: {…}, initializer: value(), … }
Nicolo Ribaudo was able to help me over on Babel's discussions. The correct way to do this is to use the initializer function:
const addItem = (newValue) => {
return function (target) {
const { initializer } = target;
target.initializer = function () {
return [
...initializer.call(this),
newValue,
];
};
};
};
class Test {
#addItem(4)
static values = [1,2,3];
}
const test = new Test();
console.log(test.constructor.values) // [1,2,3,4]

Overwriting default values

I have a function which has a default configuration stored in an object. The function takes optional argument "options", if there is any configuration in the options argument, it should overwrite the default config.
This is my implementation:
const myFunction = (props) => {
const config = {
slopeModifier: 2,
minDistance: 30,
lockScrollIfHorizontal: true,
callback: null,
};
if (props.options) {
for (const property in options) {
config[property] = options[property];
}
}
};
export default myFunction;
in other file I would do something like this:
import myFunction
const options = {minDistance: 50, slope: 3};
myFunction({arg1, arg2, options});
Is this solution a good practice? Is there a standard way how to store default values and overwrite them with optional argument?
Also, I am getting an eslint(guard-for-in) warning, could my code cause any bugs?
Is there a standard way how to store default values and overwrite them with optional argument?
Yes, there's a standard way to do this, via Object.assign
const myFunction = (props) => {
const config = Object.assign({
slopeModifier: 2,
minDistance: 30,
lockScrollIfHorizontal: true,
callback: null,
}, props.options);
// ...
};
export default myFunction;
There's also a newer Rest/Spread syntax available, which Alberto's answer shows.
const config = {
slopeModifier: 2,
minDistance: 30,
lockScrollIfHorizontal: true,
callback: null,
... props.options
};
Is this solution a good practice?
Overriding default config this way is fine. My concern would be with putting other arguments on your options object. If your other arguments are required, you might want to make them official parameters in their own right, and have the options be a last (optional) parameter.
const myFunction = (arg1, arg2, props) => ...;
const options = {minDistance: 50, slope: 3};
myFunction(arg1, arg2, options);
Also, I am getting an eslint(guard-for-in) warning, could my code cause any bugs?
It could. As the docs point out, it's possible that someone could add properties to the prototype for whatever object type you pass in as your options, and you'd be picking up those prototype properties in addition to whatever properties were added directly to the object. This would be bad practice, but it's not unheard of:
// someone else's code, maybe in a third-party library that thought it was being clever.
Object.prototype.foo = "bar";
// Your code
for(const prop in {"1": 2}) { console.log(prop); }
Output:
1
foo
You could use the spread operator to override properties if the props.options exist in the function call, like this:
const myFunction = (props) => {
const config = {
slopeModifier: 2,
minDistance: 30,
lockScrollIfHorizontal: true,
callback: null,
...props.options
};
};
export default myFunction;
You are violating the Single Responsibility Principle, and are better off creating a class to set, update, and return your config code:
class configClass{
config = {
slopeModifier: 2,
minDistance: 30,
lockScrollIfHorizontal: true,
callback: null
}
getConfig(){
return this.config
}
updateConfig(options){
for(const property in options) {
this.config[property] = options[property]
}
}
}
//export default configClass
//import configClass as myClass
const myClass = new configClass()
const options = {minDistance: 50, slope: 3}
const obj = {arg1:"foo", arg2:"bar", options: options}
myClass.updateConfig(obj.options)
console.log(myClass.getConfig())
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
You can call this function without any parameters and the default values will be applied:
const myFunction = ({
slopeModifier = 2,
minDistance = 30,
lockScrollIfHorizontal = true,
callback = null
} = {}) => {
...
};
export default myFunction;
On your other file, you can call the function like this:
import myFunction
myFunction({minDistance: 10}); // or myFunction();
Example
const myFunction = ({
slopeModifier = 2,
minDistance = 30,
lockScrollIfHorizontal = true,
callback = null
} = {}) => {
console.log(slopeModifier, minDistance);
};
myFunction();
myFunction({minDistance: 10});

What does const { target: { files } } = event mean in javascript? [duplicate]

This question already has answers here:
Destructuring deep properties
(4 answers)
Closed 3 years ago.
I am using Firebase with React, and using a Firebase File uploader and I'm using a module that uses this syntax
const { target: { files } } = event;
What does that do?
Below is the full code
I tried googling destructuring, but no luck, can someone confirm if it is ?
customOnChangeHandler = (event) => {
const { target: { files } } = event;
const filesToStore = [];
files.forEach(file => filesToStore.push(file));
this.setState({ files: filesToStore });
}
It is a destructuring assignment:
const { target: { files } } = event;
// equals to:
// const files = event.target.files;
This is es6 destructuring feature, it allows to destruct (pick) a field from an object.

What's the cleanest fp way to get a property pointed by another property

Given an object that may be null and may have the following properties:
{
templateId: "template1",
templates: {
template1: "hello"
}
}
How would you get the template in a failsafe way? (templateId might not be defined, or the template it reffers might be undefined)
I use ramda and was trying to adapt my naive version of the code to use something like a Maybe adt to avoid explicit null/undefined checks.
I'm failing to come up with an elegant and clean solution.
naive ramda version:
const getTemplate = obj => {
const templateId = obj && prop("templateId", obj);
const template = templateId != null && path(["template", templateId], obj);
return template;
}
this does work but I would like to avoid null checks, as my code has a lot more going on and it would be really nice to become cleaner
Edit
I get from severall answers that the best is to ensure clean data first.
That's not allways possible though.
I also came up with this, which I do like.
const Empty=Symbol("Empty");
const p = R.propOr(Empty);
const getTemplate = R.converge(p,[p("templateId"), p("templates")]);
Would like to get feedback regarding how clean and how readable it is (and if there are edge cases that would wreck it)
As others have told you, ugly data precludes beautiful code. Clean up your nulls or represent them as option types.
That said, ES6 does allow you to handle this with some heavy destructuring assignment
const EmptyTemplate =
Symbol ()
const getTemplate = ({ templateId, templates: { [templateId]: x = EmptyTemplate } }) =>
x
console.log
( getTemplate ({ templateId: "a", templates: { a: "hello" }}) // "hello"
, getTemplate ({ templateId: "b", templates: { a: "hello" }}) // EmptyTemplate
, getTemplate ({ templates: { a: "hello" }}) // EmptyTemplate
)
You can continue to make getTemplate even more defensive. For example, below we accept calling our function with an empty object, and even no input at all
const EmptyTemplate =
Symbol ()
const getTemplate =
( { templateId
, templates: { [templateId]: x = EmptyTemplate } = {}
}
= {}
) =>
x
console.log
( getTemplate ({ templateId: "a", templates: { a: "hello" }}) // "hello"
, getTemplate ({ templateId: "b", templates: { a: "hello" }}) // EmptyTemplate
, getTemplate ({ templates: { a: "hello" }}) // EmptyTemplate
, getTemplate ({}) // EmptyTemplate
, getTemplate () // EmptyTemplate
)
Above, we start to experience a little pain. This signal is important not to ignore as it warns us we're doing something wrong. If you have to support that many null checks, it indicates you need to tighten down the code in other areas of your program. It'd be unwise to copy/paste any one of these answers verbatim and miss the lesson everyone is trying to teach you.
Here is an ADT approach in vanilla Javascript:
// type constructor
const Type = name => {
const Type = tag => Dcons => {
const t = new Tcons();
t[`run${name}`] = Dcons;
t.tag = tag;
return t;
};
const Tcons = Function(`return function ${name}() {}`) ();
return Type;
};
const Maybe = Type("Maybe");
// data constructor
const Just = x =>
Maybe("Just") (cases => cases.Just(x));
const Nothing =
Maybe("Nothing") (cases => cases.Nothing);
// typeclass functions
Maybe.fromNullable = x =>
x === null
? Nothing
: Just(x);
Maybe.map = f => tx =>
tx.runMaybe({Just: x => Just(f(x)), Nothing});
Maybe.chain = ft => tx =>
tx.runMaybe({Just: x => ft(x), Nothing});
Maybe.compk = ft => gt => x =>
gt(x).runMaybe({Just: y => ft(y), Nothing});
// property access
const prop =
k => o => o[k];
const propSafe = k => o =>
k in o
? Just(o[k])
: Nothing;
// auxiliary function
const id = x => x;
// test data
// case 1
const o = {
templateId: "template1",
templates: {
template1: "hello"
}
};
// case 2
const p = {
templateId: null
};
// case 3
const q = {};
// case 4
const r = null; // ignored
// define the action (a function with a side effect)
const getTemplate = o => {
const tx = Maybe.compk(Maybe.fromNullable)
(propSafe("templateId"))
(o);
return Maybe.map(x => prop(x) (o.templates)) (tx);
};
/* run the effect,
that is what it means to compose functions that may not produce a value */
console.log("case 1:",
getTemplate(o).runMaybe({Just: id, Nothing: "N/A"})
);
console.log("case 2:",
getTemplate(p).runMaybe({Just: id, Nothing: "N/A"})
);
console.log("case 3:",
getTemplate(q).runMaybe({Just: id, Nothing: "N/A"})
);
As you can see I use functions to encode ADTs, since Javascript doesn't support them on the language level. This encoding is called Church/Scott encoding. Scott encoding is immutable by design and once you are familiar with it, its handling is a piece of cake.
Both Just values and Nothing are of type Maybe and include a tag property on which you can do pattern matching.
[EDIT]
Since Scott (not the encoding guy from just now) and the OP asked for a more detailed reply I extended my code. I still ignore the case where the object itself is null. You have to take care of this in a preceding step.
You may think this is overengineered - with certainty for this contrived example. But when complexity grows, these functional style can ease the pain. Please also note that we can handle all kinds of effects with this approach, not just null checks.
I am currently building an FRP solution, for instance, which is essentially based on the same building blocks. This repetition of patterns is one of the traits of the functional paradigm I would not want to do without anymore.
You can use R.pathOr. Whenever any part of the path isn't available, a default value is returned. For example:
const EmptyTemplate = Symbol();
const getTemplateOrDefault = obj => R.pathOr(
EmptyTemplate,
[ "templates", obj.templateId ],
obj
);
A collection of tests can be found in this snippet. The example shows that pathOr handles all (?) "wrong" cases quite well:
const tests = [
{ templateId: "a", templates: { "a": 1 } }, // 1
{ templates: { "a": 1 } }, // "empty"
{ templateId: "b", templates: { "a": 1 } }, // "empty"
{ templateId: null, templates: { "a": 1 } }, // "empty"
{ templateId: "a", templates: { } }, // "empty"
{ templateId: "a" } // "empty"
];
Edit: To support null or undefined inputs, you could compose the method with a quick defaultTo:
const templateGetter = compose(
obj => pathOr("empty", [ "templates", obj.templateId ], obj),
defaultTo({})
);
Try this,
const input = {
templateId: "template1",
templates: {
template1: "hello"
}
};
const getTemplate = (obj) => {
const template = obj.templates[obj.templateId] || "any default value / simply remove this or part";
//use below one if you think templates might be undefined too,
//const template = obj.templates && obj.templates[obj.templateId] || "default value"
return template;
}
console.log(getTemplate(input));
You can use a combination of && and || to short-circuit an expression.
Also, use [] (instead of .) with objects to get the value if the key is stored in a variable.
Complete check
const getTemplate = (obj) => {
const template = obj && obj.templateId && obj.templates && obj.templates[obj.templateId] || "default value"
return template;
}

use state instead of outside class variable?

I want to use my data which keep in my state instead of the outside class variable (I mean languages if u look at the code below)
In the getSuggestion I change languages.filter(lang.... to this.state.myState.filter(lang... but it's not work
It seem like this.state.myState can't be reach
The error appear at the line return inputLength === 0 ? [] : this.state.myState.filter(lang => in the getSuggestion
import Autosuggest from 'react-autosuggest';
// Imagine you have a list of languages that you'd like to autosuggest.
const languages = [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
},
];
// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : languages.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
};
// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion.name}
</div>
);
class Example extends React.Component {
constructor() {
super();
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: []
myState: [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
}
] ,
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Type a programming language',
value,
onChange: this.onChange
};
// Finally, render it!
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
It looks like you are attempting to access this in a function outside of your class, so your this will not be defined in getSuggestions.
If getSuggestions needs to be defined outside of your class, you'll need to make two changes:
define getSuggestions as a normal function as opposed to an arrow function so that we can set getSuggestions's this object manually.
Use Function.prototype.call() to call getSuggestions with a specified value for this.
I wrote up a few examples to show my point. First, notice in the example below, where getSuggestions is an arrow function and it is called by method of Example class:
const getThis = () => {
console.log(this === window || this === undefined); // this is coerced to window or undefined (Strict Mode)
};
class Example {
onSuggestionsFetchRequested() {
console.log(this); // prints correctly
getThis();
};
}
var x = new Example();
x.onSuggestionsFetchRequested(); // prints true once
In getThis, the this object will either be Window or undefined (Strict Mode).
To make the setup above work, we make the two changes proposed above:
const getThis = function() { // change #1: use function() {} vs. () => {}
console.log(this); // correct value for this
};
class Example {
onSuggestionsFetchRequested() {
console.log(this);
getThis.call(this); // change #2: use call()
};
// NOTE: in your code, define onSuggestionsFetchRequested using arrow function syntax like so:
// onSuggestionsFetchRequested = () => { ... };
// I did not use the arrow syntax above so that this example would run in the browser,
// but you would need to in order to use onSuggestionsFetchRequested in callbacks.
}
var x = new Example();
x.onSuggestionsFetchRequested();
Note: I made a small modification above to make the snippet run in the browser. Namely, I defined onSuggestionsFetchRequest as a class method as opposed to a class property (using arrow function syntax) as you have in your code. For your use case, you'll want to keep onSuggestionsFetchRequest defined using arrow function syntax so that it can be used in callbacks.

Categories