Open tanmaybhatt opened 6 years ago
I am also having this issue. However my use case is different. For example I have an extra option called 'Create a new team' which when selected performs another task but DOES NOT change the value property for the drop down.
It seems like selecting an options from the drop down is overriding value passed through value property. Please find my Dropdown component below.
<Dropdown
label='Current Team'
data={teams}
value={selectedTeam.name || ''}
baseColor={'rgba(255,255,255, .38)'}
textColor={'rgba(255,255,255, .87)'}
itemColor={'rgba(0,0,0, .54)'}
selectedItemColor={'rgba(0,0,0, .87)'}
labelFontSize={14}
labelHeight={14}
onChangeText={(value, index, data) => this.handleCurrentTeamChange(data[index].team)}
containerStyle={MainDrawerStyles.selectTeamContainer}
/>
1) Select 'Create a new team' options
The 'this.handleCurrentTeamChange()' is not changing the selectedTeam object but the dropdown is setting 'Create a new team' as the value in the UI.
2) Should not change since value property did not change. But it does :(
@tanmaybhatt Hey any luck on a possible workaround?
Yeah, as a workaround for my case I made some changes to
react-native-material-dropdown/src/components/dropdown/index.js
I made these changes to componentWillReceiveProps function which did the thing. Line 118
componentWillReceiveProps({ value }) {
if (value !== this.props.value) {
this.setState({ value });
}
}
changed to
componentWillReceiveProps({ value }) {
this.setState({ value });
}
I do not know why this worked, maybe something was already changing the props value. I guess I did not get the time to check the rest of the code.
This worked in my case. However, your case is different. Perhaps you should edit this function according to your needs. So, when the value is equal to "CREATE_NEW_TEAM" or whatever the value of your Create new team item, the label of the dropdown should not change.
componentWillReceiveProps({ value }) {
if (value !== "CREATE_NEW_TEAM") {
this.setState({ value });
}
}
You may also assign this value to some prop and then check here using that prop.
Hope this helps!
@tanmaybhatt Thanks for the detailed explanation. I was hoping we didn't need to modify the library. However I did find that this component is storing its value and using the value passed in props. Luckily this isn't a major bug in our app that we are able to push this back a bit.
@n4kz Hey Alexander thanks for the libraries! Any feedback regarding our issue/needs and if this will be looked into for a possible fix?
@tanmaybhatt's changed to worked for me as well.
Thanks for issue and sorry for delayed reply. Described rendering problems are related to PureComponent
usage, as Dropdown
itself and TextField
are based on them. I will implement something like extraData
prop on FlatList
component, to force re-render of TextField
(or any other component used as Dropdown
base) in next major releases of TextField
and Dropdown
.
Considering this is still an issue, @n4kz would you accept a PR with @tanmaybhatt's workaround as a temporary patch?
call forceUpdate via the ref exposed by this component
@tvelev92 i tried with forceUpdate but doest not work well. I added a ref like
<Dropdown
label="Todas las Listas"
data={this.state.lists}
baseColor="#86939e"
itemTextStyle={{ fontWeight: "bold" }}
fontSize={16}
textColor="#86939e"
labelFontSize={15}
containerStyle={{
width: "90%",
marginLeft: 20,
marginRight: 20
}}
value={this.state.selectedList}
onChangeText={this.selectHandlerList}
rippleDuration={0}
ref = {(c) => { this.form.listas = c; }}
/>
and then When I use a handlerEvent to hint to clear fields from a Button using onPress
handleClearFields = () => {
this.forms.listas.forceUpdate(); // this does not work anymore.
for (let key in this.form) {
this.form[key].clear()
}
}
Could you provide to me an working example, please?
I was facing the same problem and I try to find solution like below.Please have a look and no need to update plugin.
{this.props.getAreasSuccessFailure && !this.props.isFetching ?
I had the same problem. My solution is add ref and setState on it. Example of code for single dropdown:
constructor(props) {
super(props);
this.myDropdown = null;
}
resetMyDropdown () {
if (this.myDropdown) {
this.myDropdown.setState({ value: '' });
}
}
render() {
return (
<Dropdown
label='Label'
data={data}
value={this.getSelectedName(data)}
ref={c => (this.myDropdown = c)}
/>
);
}
Example for nested components from my app:
constructor(props) {
super(props);
numberOfComponents = 3;
this.chooseDropdowns = Array(numberOfComponents).fill(0);
}
renderChoose() {
return Array(numberOfComponents)
.fill(0)
.map((_, index) => getData(index)) // GetData return array with objects {value: 'name of select', id: 'SOMEID'}, eg. [{value: 'first option', id: 1}...]
.map((data, index) => (
<View key={index}>
<Dropdown
label={getLevelLabelByIndex(index)} //getLevelLabelByIndex return string for each dropdown
data={data}
onChangeText={value => {
// here all deeper dropdowns will be cleared
for (let i = index + 1; i < numberOfComponents; i++) {
if (this.chooseDropdowns[i]) {
this.chooseDropdowns[i].setState({ value: '' });
}
}
return this.selected(data, value, index);
}}
value={this.getSelectedName(data, index)}
ref={c => (this.chooseDropdowns[index] = c)}
/>
</View>
));
}
Hi, @tanmaybhatt Facing the same issue. Any update. "react-native": "0.59.8", "react-native-modal-dropdown": "^0.7.0",
Any suggestion is appreciable. Thanks
@akhilsanker
I made clean rn project with that function for You. :) If you have any problems here, please send your code pls :)
App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import { Dropdown } from 'react-native-material-dropdown';
/* Data for first dropdown */
const firstLevel = [
{ value: 'First option without suboptions', id: 1 },
{ value: 'Second option with single suboption', id: 2 },
{ value: 'Third option with multiple suboptions', id: 3 }
];
/* Data for second dropdown */
const secondLevel = [
{ value: 'First suboption for firstLevel with ID 2', id: 'A', parentId: 2 },
{ value: 'First suboption for firstLevel with ID 3', id: 'B', parentId: 3 },
{ value: 'Second suboption for firstLevel with ID 3', id: 'C', parentId: 3 },
];
type Props = {};
export default class App extends Component<Props> {
state = {
idOfFirstDropdownValue: null, // value of first dropdown (id prop from data object)
idOfSecondDropdownValue: null // value of second dropdown (id prop from data object)
};
constructor(props) {
super(props);
this.firstDropdown = null; // variable for referring to first dropdown
this.secondDropdown = null; // variable for referring to second dropdown
}
resetDropdown(dropdown) {
/* dropdown variable for select first dropdown or second dropdown to reset */
switch (dropdown) {
case 'second':
if (this.secondDropdown) {
this.secondDropdown.setState({ value: '' });
this.setState({ idOfSecondDropdownValue: null });
}
break;
default:
if (this.firstDropdown) {
/* line below reset dropdown */
this.firstDropdown.setState({ value: '' });
/* line below reset just my state, i reset that, because i filter list by that variable */
this.setState({ idOfFirstDropdownValue: null });
}
}
}
render() {
/* make additional array with filtered second Level */
const secondLevelData = secondLevel.filter(el => el.parentId === this.state.idOfFirstDropdownValue);
return (
<View style={styles.container}>
<Dropdown
label='First level dropdown'
data={firstLevel}
onChangeText={(value, index, data) => {
const id = data[index].id;
this.setState({idOfFirstDropdownValue: id});
/* Here is call function for reset second dropdown */
this.resetDropdown('second');
}}
containerStyle={{width: '100%'}}
ref={c => (this.firstDropdown = c)}
/>
<Dropdown
label='Second level dropdown'
data={secondLevelData}
onChangeText={(value, index, data) => {
const id = data[index].id;
this.setState({idOfSecondDropdownValue: id});
}}
containerStyle={{width: '100%'}}
ref={c => (this.secondDropdown = c)}
/>
<Text>Value of first dropdown: {this.state.idOfFirstDropdownValue}</Text>
<Text>Value of second dropdown: {this.state.idOfSecondDropdownValue}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
package.json
{
"name": "Rn59SampleApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.8.3",
"react-native": "0.59.8",
"react-native-material-dropdown": "^0.7.0"
},
"devDependencies": {
"@babel/core": "^7.8.7",
"@babel/runtime": "^7.8.7",
"babel-jest": "^25.1.0",
"jest": "^25.1.0",
"metro-react-native-babel-preset": "^0.58.0",
"react-test-renderer": "16.8.3"
},
"jest": {
"preset": "react-native"
}
}
I cleared it by using the value prop and state. Having 3 dropdowns and if the top one is used it should reset the other two, if the second one is changed it should only reset the third one. Using functional components and useState hook. Sorry about the somewhat messy code, will refactor it but should give you a grasp how to do it
const [pickedCategories, setPickedCategories] = useState('');
const selectedCategories = (name, value) => {
if (name === 'mainCategory' && pickedCategories.subCategory) {
setPickedCategories({
mainCategory: '',
subCategory: '',
subSubCategory: '',
});
}
if (
(name === 'mainCategory' || name === 'subCategory') &&
(pickedCategories.subCategory || pickedCategories.subSubCategory)
) {
setPickedCategories((prevState) => ({
...prevState,
[name]: '',
subSubCategory: '',
}));
}
setPickedCategories((prevState) => ({
...prevState,
[name]: value,
}));
};
<Dropdown
label='Huvudkategori'
value={pickedCategories.mainCategory}
data={renderCategories('main')}
onChangeText={(text) => selectedCategories('mainCategory', text)}
/>
<Dropdown
label='Kategori'
value={pickedCategories.subCategory}
data={renderCategories('sub')}
onChangeText={(text) => selectedCategories('subCategory', text)}
/>
<Dropdown
label='Underkategori'
value={pickedCategories.subSubCategory}
data={renderCategories('subSub')}
onChangeText={(text) => selectedCategories('subSubCategory', text)}
/>
I am using two dropdowns on the single screen and I have to reset the second dropdown when the value of first dropdown is changed. I tried to use the value prop and bound it to a state variable but the text of the dropdown does not change when state changes. Am I missing out something here?