Possible bug: It looks like
Labeling this as a wontfix for now, since, from my tests, it seems that it can't reliably reflect an updated value that is not a string. Here's the test:
describe('with <select> (complex values)', () => {
const store = applyMiddleware(thunk)(createStore)(combineReducers({
testForm: formReducer('test'),
test: modelReducer('test', {
foo: 2,
}),
}));
class CrazyValue {
constructor(val) {
this.value = val;
}
}
const field = TestUtils.renderIntoDocument(
<Provider store={store}>
<Field model="test.foo">
<select>
<option value={{ one: 1 }} />
<option value={2} />
<option value="three" />
<optgroup>
<option value={['four']} />
<option value={[5, 'five']} />
<option value={new CrazyValue(6)} />
</optgroup>
</select>
</Field>
</Provider>
);
const select = TestUtils.findRenderedDOMComponentWithTag(field, 'select');
const options = TestUtils.scryRenderedDOMComponentsWithTag(field, 'option');
it('should select the option that matches the initial state of the model', () => {
assert.isTrue(options[1].selected);
assert.isFalse(options[0].selected);
assert.equal(select.value, 2);
});
it('should dispatch a change event when changed', () => {
TestUtils.Simulate.change(options[0]);
assert.deepEqual(
store.getState().test.foo,
{ one: 1 });
});
it('should select the appropriate <option> when model is externally changed', () => {
store.dispatch(actions.change('test.foo', ['four']));
assert.isTrue(options[3].selected);
assert.equal(select.value, 'four');
assert.deepEqual(
store.getState().test.foo,
['four']);
});
it('should work with <optgroup>', () => {
TestUtils.Simulate.change(options[5]);
assert.isTrue(options[5].selected);
assert.deepEqual(
store.getState().test.foo,
new CrazyValue(6));
});
});
The last test fails.
For the meantime, I'm going to respect the original behavior of the DOM <select> and <option> components and keep the values as strings. I might make an exception for numbers and booleans, however.
Numbers and booleans would help here - and/or a note in the docs.
Just ran into this myself, and having a giant debate with myself of the various methods to handle selecting objects in a collection with a <select>.
Having numbers on <option value={}> would allow me to use ES6 Map objects with numbers as the keys, reducing the need to convert them all over the place.
@Psykar it's difficult because the event emitted by a native <option> is always going to convert the option's value prop into a string. I'll keep trying to explore solutions, perhaps similar to how radio buttons are already behaving (you can have complex values for radio buttons).
@Psykar we use option value as the key in an Object/map, so to load object we got state.data[e.target.value]
We haven't seen a single issue with that
@igor-im yeah - if you're using objects that's probably only working because object keys are coerced into strings. I've got lots of options, it's just that the 'nicest' one isn't possible due to the native <option> event's being turned into strings. There doesn't seem to be an easy way to solve this in the general case this as @davidkpiano mentions.
Without this, the nicest one seems to be adding a parser={value => parseInt(value)} to the controls in question, which isn't _too_ bad.
Closing due to inactivity - this is best done with custom components that can properly handle complex data types in <select> - preferably with some sort of hash map in the component's state.
Most helpful comment
Closing due to inactivity - this is best done with custom components that can properly handle complex data types in
<select>- preferably with some sort of hash map in the component's state.