我有两个屏幕,一个列表(Flatlist)和一个过滤器屏幕,我希望能够为列表设置一些过滤器。列表屏幕的状态为“数据”和“ usedFilters”。当我切换到过滤器屏幕时,将状态设置为用于响应导航的导航参数,然后通过navigation.navigate以及onChange函数作为过滤器屏幕的道具。在那里读取它们,并设置过滤器屏幕类的状态(通常使用列表屏幕中已通过过滤器的过滤器,如果未传递有效过滤器,则将初始化某些过滤器)。 之后,可以更改过滤器。如果发生这种情况,过滤器屏幕的状态将被更新。 如果然后单击应用按钮,则过滤器屏幕的状态将传递到onChange函数,并通过该状态返回到列表屏幕,onChange函数将更新列表屏幕的状态“ usedFilters”。如果按下取消按钮,则将null传递给onChange函数,并且不会调用setState。
为列表屏幕设置新状态非常正常。问题是,当我按下“取消”按钮(或由React导航自动显示的“后退”按钮)时,更改仍然保留。仅在状态已更改之前,这种情况才会发生。因此,如果从未应用过更改,因此列表屏幕的“ usedFitlers”状态为空,则不会发生此行为。仅当我已经进行了一些更改并且因此列表屏幕的“ usedFitlers”状态具有有效值并传递到过滤器屏幕时,取消或返回按钮才能按预期工作。
我正在使用expo-cli 3,并在我的android智能手机和iOS模拟器上进行了尝试。行为相同。我也使用chrome开发工具进行了调查,但我根本不知道“ usedFitlers”状态的更新位置。
我正在使用React Native 0.60和React Navigation 3.11.0
我最好的猜测是,由于某种原因,这两个状态共享相同的内存,或者一个是指向另一个或类似对象的指针。 (前一段时间在python中出现了类似的问题,不知道在分配变量时它使用了指针)。 有人知道吗?
列表屏幕:
export default class ListScreen extends React.Component {
state = { data: [], usedFilters: null };
static navigationOptions = ({ navigation }) => {
let data = navigation.getParam('data')
let changefilter = navigation.getParam('changeFilter')
let currfilter = navigation.getParam('currFilter')
return {
headerTitle:
<Text style={Styles.headerTitle}>{strings('List')}</Text>,
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('FilterScreen', {
dataset: data, onChange: changefilter, activeFilters:
currfilter })} >
<View paddingRight={16}>
<Icon name="settings" size={24} color=
{Colors.headerTintColor} />
</View>
</TouchableOpacity>
),
};
};
_onChangeFilter = (newFilter) => {
if (newFilter) {
this.setState({ usedFilters: newFilter })
this.props.navigation.setParams({ currFilter: newFilter });
} // added for debugging reasons
else {
this.forceUpdate();
let a = this.state.usedFilters;
}
}
_fetchData() {
this.setState({ data: fakedata.results },
() => this.props.navigation.setParams({ data: fakedata.results,
changeFilter: this._onChangeFilter }));
}
componentDidMount() {
this._fetchData();
}
render() {
return (
<ScrollView>
<FlatList/>
// Just data rendering, no problems here
</ScrollView>
);
}
}
过滤器屏幕:
export default class FilterScreen extends React.Component {
static navigationOptions = () => {
return {
headerTitle: <Text style={Styles.headerTitle}> {strings('filter')}
</Text>
};
};
state = { currentFilters: null }
_onChange = (filter, idx) => {
let tmp = this.state.currentFilters;
tmp[idx] = filter;
this.setState({ currentFilters: tmp })
}
_initFilterElems() {
const filters = this.props.navigation.getParam('activeFilters');
const dataset = this.props.navigation.getParam('dataset');
let filterA = [];
let filterB = [];
let filterC = [];
if (filters) {
// so some checks
} else {
// init filters
}
const filterElements = [filterA, filterB, filterC];
this.setState({ currentFilters: filterElements })
}
componentDidMount() {
this._initFilterElems()
}
render() {
const onChange = this.props.navigation.getParam('onChange');
return (
<ScrollView style={Styles.screenView}>
<FlatList
data={this.state.currentFilters} // Listeneinträge
keyExtractor={(item, index) => 'key' + index}
renderItem={({ item, index }) => (
<FilterCategory filter={item} name={filterNames[index]}
idx={index} onChange={this._onChange} />
)}
ItemSeparatorComponent={() => <View style=
{Styles.listSeperator} />}
/>
<View style={Layout.twoHorizontalButtons}>
<TouchableOpacity onPress={() => {
onChange(this.state.currentFilters);
this.setState({ currentFilters: null });
this.props.navigation.goBack();
}}>
<View style={Styles.smallButton}>
<Text style={Styles.buttonText}>{strings('apply')} </Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => {
onChange(null);
this.setState({ currentFilters: null });
this.props.navigation.goBack();
}}>
<View style={Styles.smallButton}>
<Text style={Styles.buttonText}>{strings('cancel')}
</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView >
);
}
}
因此,当我按下“取消”按钮时,列表屏幕的_onChangeFilter函数将返回null。此部分有效,根据console.log和调试器,不会调用setState。但是,如果我在else部分中设置断点,则可以看到this.state.usedFilters已更改。
答案 0 :(得分:0)
好一会儿我就知道了。问题在于整个过滤器列表总是被引用,因为即使更改列表的子部分,react native(js)似乎总是使用引用。
使用lodash cloneDeep解决了该问题。