Fixing two way binding for react signals #518
Replies: 4 comments 9 replies
-
Now preact signals works by babel plugin which checks const text = signal('')
// no `.value` access inside the component - will not be tracked
const A = () => <input type="text" {...bindInput(text)} /> const text = signal('')
/**
* @useSignals
* not sure that it will be correctly tracked, because signal probably unwraps while setting value to actual DOM
**/
const A = () => <input type="text" {...bindInput(text)} /> |
Beta Was this translation helpful? Give feedback.
-
I don't think patching all of React's types to accept signals is within the scope. It'd be way too much maintenance. You certainly could create a directive if you wished, but that'd be something for user code. Here's a Preact implementation of a directive. To be clear, a directive would be something like this: |
Beta Was this translation helpful? Give feedback.
-
Note: I was not using the |
Beta Was this translation helpful? Give feedback.
-
Your first code snippet is the correct one. You just need to add the hook to make your component reactive: import {useSignals} from '@preact/signals-react/runtime';
function MyComponent() {
useSignals(); // enable automatic re-rendering
return (
<input type="text" value={text.value} onChange={(e) => { text.value = e.currentTarget.value }} />
);
} Alternatively, you can use the Babel plugin that does this for you. |
Beta Was this translation helpful? Give feedback.
-
The preact is an awesome library that solved a big issue I had with react, lack of signals. This library is single handedly convincing me to move from svelte to react. While playing around with it I found that I had an issue with two way binding an input field.
First test:
The above did not work because even though the value of the signal changed successfully, it doesnt reflect back to the field and update it to the new value, ie. the value of the input field is stuck on the original text.value
The Problem:
Second test:
The above worked successfully! but it is throwing the following misleading error
Type 'Signal<string>' is not assignable to type 'string | number | readonly string[] | undefined'. Type 'Signal<string>' is missing the following properties from type 'readonly string[]': length, concat, join, slice, and 19 more.ts(2322) index.d.ts(3325, 9): The expected type comes from property 'value' which is declared here on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'
Im calling it misleading because it acually works, and it successfully does the two way binding exactly as I expect, where the value of the input field and the signal updates when I type something in the input box.
The Solution:
Solution 1:
First thing that came to my mind was to fix the types, since im not really a typescript or react expert, it took me a while to reach this solution but it works fine for me for now. I added an index.d.ts file in src and added the following code to it, it is a bit ugly, but it fixes the type issue, allowing Signal to be used as a value in the input field
This solution was kindof ugly to me since I was making an assertion about the types that might be false, again, im not an expert on this so it felt wrong to do that.
Solution 2:
After some googling, I found a post that recommends creating a custom directive, so I did. In it I can infer the type of the value inside the signal, and use it to assert the type of the value, I believe I can safely do that since this is what Signals do in the end (would love if someone better than me could verify this)
it can then be used simply like so
I would have loved to make this into a pull request, but Im not really sure where I should make these changes in the project. Plus I dont think either of the solutions are perfect so maybe it is better for someone from the preact team might take inspiration from this and implement something even better.
Thank you for you hard work!
Beta Was this translation helpful? Give feedback.
All reactions