Does Solid JS have an equivalent of React.useCallback? - solid-js

Solid has createMemo which I thought might work, but it says "The memo function should not change other signals by calling setters (it should be "pure").", which is not suitable for my use case.

Solid doesn't have useCallback because components only mount once and don't rerender, React has useCallback so that developers have another way to prevent rerendering.
createMemo's purpose is to cache the derived signal, such as retrieving a signal that runs an expensive fibonacci value.
const [count, setCount] = createSignal(0);
const fib = createMemo(() => fibonacci(count()));
As you noted, createMemo should not call other signal setters, this is so that Solid can optimize such as all memos can update at most once in response to a dependency change.
const [count, setCount] = createSignal(0);
const [lastName, setLastName] = createSignal('');
const fib = createMemo(() => {
setLastName(value) // DONT DO THIS, memo function should not call other signal setters, should be pure
return fibonacci(count());
});
If your use case calls for retrieving values and calling setters, that's what useEffect is used for.

Related

How to pass "read-only" props in SolidJS

I frequently pass properties to Solid components that are read-only for dependency injection of singleton services. See the simplified example below. However the SolidJS lint rules give me errors like: The reactive variable 'props.firestore' should be used within JSX, a tracked scope (like createEffect), or inside an event handler function. It seems totally reasonable to have some readonly props like this. Am I misusing props in SolidJS or not understanding something? Or are the ESLint rules not sophisticated enough? I can disable the lint warnings but don't want to do that if I'm doing something wrong here.
export const DocumentView = (props: { firestore: Firestore }) => {
const query = query(collection(props.firestore), "my-collection");
const [docs, setDocs] = createSignal([]);
const unsubscribeQuery = onSnapshot(query, (snapshot) => {
setDocs(() => snapshot);
});
onCleanup(() => unsubscribeQuery());
return (
<div>
<For each={docs}>{(i) => <p>{i}</p>}</For>{" "}
</div>
);
};
When the components compiled, props becomes the function arguments and they are passed as is. So, if you pass readonly values to a component, they will remain readonly.
import { Component } from 'solid-js';
export const DocumentView: Component<any> = (props) => {
return (
<div>{JSON.stringify(props.o)}</div>
);
};
import { template as _$template } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
const _tmpl$ = /*#__PURE__*/_$template(`<div></div>`, 2);
import { Component } from 'solid-js';
export const DocumentView = props => {
return (() => {
const _el$ = _tmpl$.cloneNode(true);
_$insert(_el$, () => JSON.stringify(props.o));
return _el$;
})();
};
Now, the problem with your code is using untracked reactive variables. By untract, we mean its dependencies can not be tracked by the Solid's runtime.
Solid tracks effects by creating a data structure where the inner scope will be owned by an outer scope, and this chain goes all the way up to the render function. That is why you get an error like below when you create effects outside this hierarchy.
SolidJS: "computations created outside
a `createRoot` or `render` will never be disposed."
This is for preventing memory leaks by releasing any resource created by an effect. You can read more about owners:
https://www.solidjs.com/docs/latest#runwithowner
However this check targets the untracked effects, not signals. So you can access signals anywhere, doesn't matter scope is tracked or not:
The related eslint rule limits the accessing of signals to the tracked scopes, probably as a safety measure.
"The reactive variable '{{name}}' should be used within JSX,
a tracked scope (like createEffect),
or inside an event handler function.",
This means you can use signal values inside a tracked scope. So, disabling the rule should not have any effect on how your components run.

Why can't I use local variable in a callback?

Let's say I've a stream() which returns Stream<int>. stream().listen returns StreamSubscription.
var subs = stream().listen((e) {
if (someCondition) subs.cancel(); // Error
});
I don't understand why is there an error, because by the time I start listening for events in the listen method, I would have definitely a valid object subs.
Note: I know this can be done by creating a StreamSubscription instance/top-level variable but why they have prevented the use of local variable like this?
We know that Stream.listen does not call its callback until after a value is returned, but the Dart compiler does not.
Consider the following function, which simply calls a callback and returns the result:
T execute<T>(T Function() callback) => callback();
Now, consider using it to assign a variable:
int myVariable = execute(() => myVariable + 1);
The problem here is that the given callback is called synchronously, before myVariable is assigned, but it tries to use myVariable to calculate a value!
To resolve this issue with your stream question, you can use the new late keyword. Using late tells the compiler that you know the variable will be assigned by the time it's accessed.
late final StreamSubscription<MyType> subscription;
subscription = stream().listen(/* ... */);
Likely because it's possible that subs will be used before it's assigned. We know that the callback passed to listen will be called on stream events, but it's also possible that the callback is called immediately and it's return value or a calculation done by it may be required for the return value of the function it was passed to.
Take this fakeFunc for instance, which I made an extension on the int class for convenience:
extension FakeListen on int {
int fakeFunc(int Function(int x) callback, int val) {
return callback(val);
}
}
The return value depends on the result of callback!
int subs = x.fakeFunc((e) {
print(e);
subs.toString();//error
return e + 1;
}, 5);
I can't use subs because subs will be guaranteed to not be exist at this point. It's not declared. This can be easily solved by moving the declaration to a separate line, but also forces you to make it nullable. Using late here won't even help, because subs won't exist by the time you try to use it in the callback.
Your scenario is different, but this is an example of where allowing that would fail. Your scenario involves a callback that is called asynchronously, so there shouldn't be any issues with using subs in the callback, but the analyzer doesn't know that. Even async-marked methods could have this issue as async methods run synchronously up until its first await. It's up to the programmer to make the right decision, and my guess is that this error is to prevent programmers from making mistakes.

How to make something variable something constant?

I have a variable list in flutter that won't change anymore once called. The question is: can I make this list constant?
Here is the code:
final number = new List<int>.generate(300, (i) => i + 1);
var rng = new Random();
final place = new List.generate(300, (_) => rng.nextInt(3));
final noteListStart = new List<Note>.generate(number.length, (i) => Note(number[i],place[i]));
final List<Note> noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
final initList = List<Note>.from(noteListStart)..addAll(noteListEnd);
List<Note> initNotes1() {
return
initList;
}
In the example above, initNotes1() needs to be constant after being called so I can use it easely somewhere else in the code.
Any help would be appreciated.
At this point, it is unclear what your actual question is.
Taken at face value, you are asking how you can compute a list at runtime and then, once the list is populated, convert it into a constant. Well the answer is this: you can't.
Constants are explicit values that are defined before the program has even compiled. By definition, you cannot create a constant from a computed or generated value (unless it is evaluated from simple expressions involving other values are themselves constant). This means you can't create a constant list full of random values - it is antithetical to the whole concent of what a "constant" is.
(Note: This explanation is a bit specific to Dart, but it is also common among compiled languages. This is different to the definition of a "constant" for an interpreted language such as Javascript, which uses the const keyword to merely refer to an immutable variable.)
If you didn't mean "constant" and merely meant "immutable", then you would mark your list as final which would achieve the same thing. As an extra added measure, you can create the list using List.unmodifiable to make it so its elements couldn't be changed either.
final rng = Random();
final _noteListStart = List.generate(startLength, (i) => Note(i + 1, rng.nextInt(3)));
final _noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
List<Note> noteList = List.unmodifiable([..._noteListStart, ..._noteListEnd]);
However, what it appears you are asking is not how to make a variable constant, but instead how to make a variable global. This is a question that is both easier and harder to answer.
It's easier because doing so is quite simple. Take the above code, rearrange it a bit, and put it into its own dart file which you can then import and use wherever you wanted:
// notes_list.dart
final _noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
List<Note> _initList(int startLength) {
final rng = Random();
final _noteListStart = List.generate(startLength, (i) => Note(i + 1, rng.nextInt(3)));
return List.unmodifiable([..._noteListStart, ..._noteListEnd]);
}
final List<Note> noteList = _initList(300);
// other_file.dart
import '/path/to/notes_list.dart';
void main() {
print(noteList);
}
(Note: The import is mandatory - you cannot make anything _truly_ global in Dart and eliminate the need to import it.)
On the flip side, this question is harder to answer because the practice of making global variables is frowned upon by many programmers. It belongs to a class of practices that leads to tightly coupled and difficult-to-test code which in turn results in programs that are near impossible to maintain and evolve. In many cases, global variables can be replaced entirely by another practice, such as dependency injection.

Are getter functions necessary for Protractor element locators

When using PageObjects for Protractor e2e tests, should you use getter functions for the element locator variables instead of having variables?
example:
public loginButton: ElementFinder = $('#login-submit');
public loginUsername: ElementFinder = $('#login-username');
Or should you use a getter function in the Page Object like this:
public get loginButton(): ElementFinder {
return $('#login-submit');
}
public get loginUsername(): ElementFinder {
return $('#login-username');
}
Is one approach better than another?
No getters needed, since protractor ElementFinder and ElementArrayFinder objects are lazy - no any searching for this element will be done until you will try to call some methods on them. Actually thats also a reason why you don't need to use await for protractor element search methods:
const button = $('button') // no await needed, no getter needed
console.log('Element is not yet tried to be searched on the page')
await button.click() // now we are sending 2 commands one by one - find element and second - do a click on found element.
http://www.protractortest.org/#/api?view=ElementFinder
ElementFinder can be used to build a chain of locators that is used to find an element. An ElementFinder does not actually attempt to find the element until an action is called, which means they can be set up in helper files before the page is available.
It's fine to use either but if you're going to transform or do something else to your element, then the getter function would be better in my opinion since that's the reason why we utilize mutators in the first place.
I'm not really sure which method is more convenient, if there is one.
I've been using protractor for a bit longer than 1 year now, and I always stored locators in variables.
What I'd normally do is:
const button = element(by.buttonText('Button'));
Then I'd create a function for interacting with the element:
const EC = protractor.ExpectedConditions;
const clickElement = async (element) => {
await browser.wait(EC.elementToBeClickable(element));
await element.click();
};
Finally, use it:
await clickElement(button);
Of course I store the locators and functions in a page object, and invoking/calling them in the spec file. It's been working great so far.

Dart: Is there a disadvantage to using const constructor?

There is an analyzer/lint check to warn me when it is possible to use a const constructor: https://dart-lang.github.io/linter/lints/prefer_const_constructors.html
(ie. using final a = const A(); instead of final a = A();)
I think to understand the advantages (there will only ever be one instance with the same constant values for a const constructor). But why isn't this the default? Since dart 2 the new can be omitted, so why didn't they change the definition of creating a new instance which can be created const simply as const instead of new? I assume there must be some disadvantage to having everything const?
(for example in a constant context like const [A()] it is actually the same as const [const A()], so why not everywhere)?
so why didn't they change the definition of creating a new instance which can be created const simply as const instead of new?
If you mean why doesn't final a = A(); automatically assume const A() if A has a const constructor:
Sometimes it is automatic:
const a = A();
in which case A's constructor is being invoked in a const context and doesn't need an extra const qualifier on the right-hand-side.
An explicit const expresses intent. For example, suppose you had:
final a = A(B());
where A and B have const constructors. Later, somebody makes a change:
final a = A(C());
where C does not have a const constructor. If const were automatic, then you would have no idea that a is no longer const. Maybe that's okay, but it also could suddenly have a negative impact on your application's performance, and without an explicit const qualifier, the impact of a local change could have a much wider scope than expected. (That said, explicit const qualifiers and automatically adding them aren't mutually exclusive.)
const can have downsides. const creates compile-time constants. If you have:
final a1 = A();
final a2 = A();
identical(a1, a2) is not true. If const A() were implicit, then identical(a1, a2) would be true, and maybe that's not a property that the code intended to have.
Compile-time constants live forever. The whole point is to have an object that can be reused instead of re-constructing it. The flipside is that won't be destroyed.