select value is not reflected if the list of options are loaded at a later time
Describe the bug
I am loading a list of select options from an API so the options get populated a bit after the element is rendered. The value of the select is already set but is not reflected after the list is loaded.
Your Example Website or App
https://playground.solidjs.com/anonymous/afff844c-d8cb-40ab-b368-7b0fe268ab6d
Steps to Reproduce the Bug or Issue
- create a
- set its value
- use <For> to populate its options from an array
- load the array of options using a timeout so that is populated after the render
Expected behavior
I expect the value of the select to be reflect the correct option regardless of when that option is rendered
Screenshots or Videos
No response
Platform
- Browser: [Chrome]
Additional context
No response
You can make it work with either:
- Update
state.colorto different value, so that the<select>gets notified of update, like so:
const state = createMutable({
color: undefined, //undefined, so later it we can assign different value
options: [];
})
setTimeout(() => {
state.options.push(Color.Red)
state.options.push(Color.Blue)
state.color = Color.Blue
}, 1000)
- A bit more implicit, but subscribe to
state.options.lengthchange in<select>'svalue:
return <select value={state.options.length && state.color} ... >
- Or similarly you could use index instead of value of your color (but this requires new object for
state.options, as we don't subscribe to it's length):
const state = createMutable({
colorIndex: 1,
options: [];
})
setTimeout(() => {
state.options = [Color.Red, Color.Blue]
}, 1000)
return <select value={state.options[state.colorIndex]} ... >
- Take bit different approach and instead of using
valueon<select>, useselectedon<option>with use ofcreateSelector(orstate.color === o): https://playground.solidjs.com/anonymous/85464d7a-2dfa-479d-93fb-8e93f04b745d
But I wonder if it's possible to make it work for your code
@maciek50322 thank you for the detailed workarounds. Since this works automatically with Vue and React it would be nice to have one less gotcha for devs making the transition to Solid.
We don't re-render like React or Vue so there has to be some level of different expectations here. My usual go to is how VanillaJS works. If you have a select that has a value and options are added later does it work?
https://playground.solidjs.com/anonymous/70df1286-3b10-48c6-9b5a-c7c2ae93d018 Apparently not.. value has to always be reset after options change.
There is a big difference to me between things that are conceptually synchronous not working as that makes no sense(order shouldn't be your concern), and things that happen asynchronously.
I do think select is an awkward design. The fact it forces you to set its value again is tedious. I'd be willing to look at solving this but it starts with being able to come up with a vanilla approach. The problem is we will not be re-rendering, and reactively these things can be changed independently as they can exist in different files even with no connection. They can be wrapped in components even or rendered using Dynamic.
https://playground.solidjs.com/anonymous/70df1286-3b10-48c6-9b5a-c7c2ae93d018 Apparently not.. value has to always be reset after options change.
Interesting thing here is that it won't change even if you do
select.value = Color.Red
At the end, after adding options. I assumed solid does some custom magic for select value prop (similar to svg props), value isn't html's attribute for select, as mentioned in thread here https://stackoverflow.com/a/44798498
We don't use attributes for value and always we use the property.. That works. Also If you move the select.value into the timeout then it works. But we have no way of know we need to do that. The problem is the value must be set after the option is present otherwise it won't show it even if an option of the same name is added later. That is weird DOM behavior but also has us in a weird place unless we want to maybe like listen to DOM events.. like create mutation observer maybe? All we can do is declaratively make sure it runs last. Which we can do. But that doesn't help if someone tries to add options after the fact without setting the value as well.
Something forgotten this days, is that you can just tell which value is selected when creating the options.
https://playground.solidjs.com/anonymous/425eb7c6-1a16-49ed-8eaf-f49cb207dd86
The only thing I have changed, is to add a selected attribute to the option as in
<option value={o} selected={state.color === o}>