Closed kamiky closed 7 years ago
In React, the defaultValue
is only applied on the first render cycle. Since the first render has an empty value, pending a request, the defaultValue is applied as undefined
. Defaults are not considered for future renders so the second render, after receiving the options, will not apply the default.
To apply "initial" values after the first render, use the controlled component pattern and the value
prop.
@levithomason Could you elaborate on how to set a select dropdown to display a default value that is the first element of an array of objects that is fetched using an asynchronous request? I'm having a hard time with that. I used the following approach:
<Dropdown
selection
(...)
value={(this.props.selectedOption && this.props.selectedOption.value) || fetchedOptions[0].value}
/>
null
). The issue I have right now is when I go to a different page, then go back to the page with the select dropdown (with the state remaining the same) and a new set of options is fetched (not necessarily having the same values), the default value is not set because selectedOption is defined and is matching a selection for the previous set of options. How can I make sure to always make the dropdown select the first element in fetchedOptions by default, no matter what?
You should do your fetching in an async Redux action. Ideally the dropdown would have a single value assigned from Redux state. Use your reducer initial state for the default value, dispatch an action to fetch the options, and use actions to update the value as well. The idea is that there should be a single value assigned to the dropdown and Redux's initial state, reducer, and actions should manage that state as necessary.
Hope they helps!
@levithomason Thanks for the insight! But currently I'm facing a weird issue with the dropdown. I have my dropdown depending of the props of the wrapper container who haves the redux connection.
This is my dropdown code inside off TransactionRowComponent
<Dropdown
export default class TransactionRowComponent extends Component {
render () {
return (
<Dropdown
selection
onChange={(event, data) => {this.props.onChangeBusinessCenter(this.props.transactionId, data.value)} }
value={this.props.businessCenterId}
placeholder='Categoría'
options={this.props.businessCenters.map(bc => {
return {key: bc.id, text: bc.name, value: bc.id};
})}
/>
);
}
}
The weird thing is this, at first load if the Dropdown doesn't have a value, you can select one of the list and the text of that selection will be displayed on it like every dropdown does, but if the dropdown have a valid value on it, will be displayed with that option selected, that's ok, but when I try to change to another value, the text is never updated in the main box... but if I open the list options is selected internally...
So I really don't know what I'm doing wrong. This should be straightforward... might be related to my redux/state setup?
That component it's inside of another component (a table) called TransactionComponent
export default class TransactionComponent extends Component {
renderRows = () => {
return this.props.items.map(item => {
return <Table.Row key={item.id}>
<Table.Cell>{item.amount}</Table.Cell>
<Table.Cell>{item.description}</Table.Cell>
<Table.Cell>{moment(item.date).format('DD/MM/YYYY')}</Table.Cell>
<Table.Cell>{item.movement_code}</Table.Cell>
<Table.Cell>
<TransactionRowComponent
businessCenters={this.props.businessCenters}
transactionId={item.id}
businessCenterId={item.business_center.id}
onChangeBusinessCenter={this.props.onChangeBusinessCenter}
/>
</Table.Cell>
</Table.Row>
});
}
And this is the main container:
....
changeBusinessCenter = (transactionId, business_center_id) => {
this.props.actions.transaction.updateTransaction(transactionId, { business_center_id, assets:[] });
}
render () {
let transactions = <TransactionComponent
items={this.props.transaction.items}
businessCenters={this.props.business_center.items}
onChangeBusinessCenter={this.changeBusinessCenter}
/>;
transactions = this.props.transaction.items.length > 0 ? transactions : [];
return (
<Container fluid>
<Header as='h2'>Transacciones</Header>
<Grid>
<Grid.Column>
<label>Desde: </label><DatePicker selected={this.state.startDt} onChange={this.changeStartDt} />
</Grid.Column>
<Grid.Column>
<label>Hasta: </label><DatePicker selected={this.state.endDt} onChange={this.changeEndDt} />
</Grid.Column>
<Grid.Column>
<Button onClick={this.searchTransactions} primary>Buscar</Button>
</Grid.Column>
</Grid>
{ transactions }
</Container>
);
}
Any comment or feedback will be appreciated ;)
Ok... after trying updating the state I was able to get it working.. but the fix doesn't looks ok to me.. In summary, I need to use the state to maintain the changed value...
export default class TransactionRowComponent extends Component {
state = {
value:null,
}
onChange = (value) => {
this.setState({value});
this.props.onChangeBusinessCenter(this.props.transactionId, value)
}
render () {
let value = !this.state.value ? this.props.businessCenterId : this.state.value;
return (
<Dropdown
selection
fluid
onChange={(event, data) => {this.onChange(data.value)} }
value={value}
placeholder='Categoría'
options={this.props.businessCenters.map(bc => {
return {key: bc.id, text: bc.name, value: bc.id};
})}
/>
);
}
}
@gustavonecore this is more a React question in general. Your assertion that using the state (either in React or something like Redux) to maintain values is correct.
The question is not related to the original bug report. Instead of commenting on old, closed bugs, please open new reports if you have a bug. For usage questions like this, Stack Overflow is a better place to post for help.
defaultValue is first undefined and then assigned to the first options, which result to an empty default value
Steps
fetch options via request or just assign defaultValue to undefined then to the first options (with delay)
var value if (this.state.options.length > 0) { value = this.state.options[0].value } return ( <Dropdown placeholder={this.props.placeholder} options={this.state.options} onChange={this.props.onChange} name={this.props.model} search selection onSearchChange={this.onSearch.bind(this)} defaultValue={value} />
Expected Result
First option shown on the dropdown
Actual Result
Empty dropdown
Version
0.63.6
Testcase
Fork this to show the issue: http://codepen.io/levithomason/pen/ZpBaJX