-
Notifications
You must be signed in to change notification settings - Fork 126
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
solid-primitives destructure: Added new config option "normalize" #525
Conversation
Added new config option "smart" If true all values will be returned as reactive functions, meaning it will resolve values of type MaybeAccessor<T> to Accessor<T>. This helps a lot for library devs to provide a flexible interface to users and don't worry about having to make breaking changes later on and the handling of the destructured props is always as Accessor. About the naming feel free to rename it to anything you like. I was thinking about: normalize normalizeValues accessibleValues ... Maybe it would also make sense to make this the default behavior, but that would be a breaking change.
Added new config option "smart" If true all values will be returned as reactive functions, meaning it will resolve values of type MaybeAccessor to Accessor. This helps a lot for library devs to provide a flexible interface to users and don't worry about having to make breaking changes later on and the handling of the destructured props is always as Accessor. About the naming feel free to rename it to anything you like. I was thinking about: normalize normalizeValues accessibleValues ... Maybe it would also make sense to make this the default behavior, but that would be a breaking change.
🦋 Changeset detectedLatest commit: b56f253 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
I forgot to change the version in package.json @thetarnav |
Don't change the version. This does it: https://github.com/solidjs-community/solid-primitives/actions/workflows/release.yml |
I have changed the option to "normalize" and also added the changeset. |
it will now return all functions as they are and only memo static values. What I have realized is that once normalized is set to true, it doesn't matter if memo is true or false. The results are the same. So I decided to make it an option of memo. And instead of using exclusive types for the normalize config I extended the existing types to return a different type depending on the memo option.
Please document the option in the README.md. Thanks! |
And add yourself to the contributors in package.json :) |
added myself as contributor in package.json
hmm looks like I’ve missed the change when |
When I experimented with different config combinations, I realised that as soon as normalised was enabled it didn't matter if memo was true or false. The returned values were the same, so I decided to merge it with the memo prop to avoid confusion. I also discovered that the returned types were not correct for all combinations. That is all fixed now and also checked by the test I have added. What it does now it the end is memo static values and returning functions as they are. So the memo config option and now works like charm with all the other options 🤓 |
I have added tests to check for the combination of {normalize:true} vs {normalize:true,memo:true} removed the check for memo in the getter function. Moved the check if source is a function outside the getter but it still needs to be a function, otherwise tests fail. Wrapped it in a memo for optimization.
Please review the latest commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, besides the reactive callback stuff that I still need to think about and some minor stuff to correct and it can be merged I think.
packages/destructure/src/index.ts
Outdated
? (key: any) => () => source()[key] | ||
: (key: any) => () => source[key]; | ||
|
||
const _source = createMemo(() => (typeof source === "function" ? source() : source)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whats the reason for memoizing the source? in most cases the primitive is used with props object or a signal, where the source memoization is useless.
@@ -246,6 +247,142 @@ describe("destructure", () => { | |||
expect(updates.b).toBe(2); | |||
expect(updates.c).toBe(2); | |||
|
|||
dispose(); | |||
})); | |||
test("spread object normalize and deep", () => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can spit it to two different tests as you are testing two things. "@thetarnav's stuff"
and "@madaxen86's stuff"
. Could be more descriptive btw 😄
I'm aware what my test is doing (testing combining with the memo option enabled and disabled), so I'm guessing the second test is for a function source and deep: true?
const [_numbers, setNumbers] = createSignal({ | ||
a: 3, | ||
b: () => (toggle() ? 2 : 3), | ||
c: (a: number, b: number) => a * b, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you replace it with c: () => (a: number, b: number) => a * b
the types break, but tests still pass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have adjusted the types.
It became a bit messy but now the types are inferred correctly.
packages/destructure/src/index.ts
Outdated
else result[key] = memo ? createMemo(calc, undefined, options) : calc; | ||
else | ||
result[key] = | ||
memo && (!config.normalize || calc.length === 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is one thing I'm realizing about this.
const [callback, setCallback] = createSignal((a, b) => a + b)
const {cb} = destructure({get cb() { return callback() }})
setCallback(() => () => 69)
expect(cb()).toBe(69) // fail: still calls the original function
Even if we want to avoid cb()(1, 2)
syntax of normal destructure, maybe it should still support "reactive callbacks", the same props.callback(1, 2)
would if you passed callback={someSignal()}
in JSX.
Although Solid doesn't allow for reactive callbacks in on___
props, so it may be fine to restrict that as well.
Co-authored-by: Damian Tarnawski <[email protected]>
splitted tests for normalize
Another question about the callbacks. type Props = {
handleClick: (e: MouseEvent) => void
}
const logMousePos = (e: MouseEvent) => {
console.log("mouse", e.x, e.y)
}
// fine
const a = destructure<Props>({handleClick: logMousePos})
a.handleClick(e) // => works
// variadic params
const log = (...a) => {console.log(...a)}
// ts doen't mind
const b = destructure<Props>({handleClick: log}) // logs immediately
b.handleClick(e) // runtime error - tried to call undefined |
Good call. if (typeof accessedValue() === "function" && hasParams(accessedValue())) return accessedValue(); shouldn't we only return if the config normalize:true ? |
@thetarnav Any chance this moves forward or should I submit it as a new primitive? |
I'm sorry for not responding. Looking back at it, I'm still not convinced that this direction is the right one. Checking whether something is a signal or not goes against Solid's principles, it is intentionally designed to make such determinations difficult. When you need to resort to stringifying and testing a function's definition with regex look for keywords more common in callbacks than signals, it's a strong indicator that this may not be the best approach. It's not accurate, it's slow, and it's trying to be too clever. Unfortunately, the alternative I have doesn't satisfy me either. Solid's compiler treats props starting with I'm hesitant to introduce something that users won't have the convenience that it will not cause more problems, when the only one that they wanted to solve was less typing. |
Added new config option "normalize"
If true all values will be returned as reactive functions, meaning it will resolve values of type MaybeAccessor to Accessor.
This helps a lot for library devs to provide a flexible interface to users and don't worry about having to make breaking changes later on and the handling of the destructured props is always as Accessor.
Maybe it would also make sense to make this the default behavior, but that would be a breaking change.
Test for spread object smart passes.