当我向其中添加项目时,我有一个ListView
似乎在某些情况下崩溃的问题。这次崩溃最糟糕的是我找不到一致的复制步骤。它总是在添加新项目时发生,ListView
但每添加约 10 个项目只会发生一次。
我有一个实时客户端,它会随着时间的推移不断向其添加数据。当我有两台设备显示相同的数据同时获取一个新设备并且其中一台崩溃时,另一台也出于同样的原因崩溃。
这是堆栈跟踪和错误:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: {packagename}, PID: 28309
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4309)
at android.view.ViewGroup.addView(ViewGroup.java:4145)
at android.view.ViewGroup.addView(ViewGroup.java:4086)
at com.facebook.react.views.view.ReactViewManager.addView(ReactViewManager.java:203)
at com.facebook.react.views.view.ReactViewManager.addView(ReactViewManager.java:37)
at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:394)
我在与他自己的本机代码无关的 react-native 中找不到任何有此问题的人。我没有在这个中编写任何本机代码。
根据有关本机 Android 应用程序的此问题的帖子,它闻起来像是ListView
对它的项目进行了错误处理,并试图在将其与以前的父级分离之前添加一个。
有人对可能导致它的原因有任何想法吗?谢谢。
更新:
这是我的代码,它比示例所需的要长,但以防万一它包含我正在做的奇怪的事情导致这个问题,我附上了整个东西
class ItemsViewer extends Component {
constructor() {
super();
this._isMounted = true;
const dataSource = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: dataSource,
};
this._shouldAdjustScrollForKeyboardOnNextLayout = false;
this._scrollListToBottomThrottled = throttle(this._scrollListToBottom.bind(this), 300);
this._onListLayout = this._onListLayout.bind(this);
this._onFooterLayout = this._onFooterLayout.bind(this);
this._renderRow = this._renderRow.bind(this);
this._renderFooter = this._renderFooter.bind(this);
}
componentWillUnmount() {
this._isMounted = false;
}
componentWillMount() {
this._isMounted = true;
this.setState({dataSource: this.state.dataSource.cloneWithRows(this.props.items)});
}
componentWillReceiveProps(nextProps) {
if (this.props.items != nextProps.items) {
this.setState({dataSource: this.state.dataSource.cloneWithRows(nextProps.items)});
}
if(this.props.isKeyboardShown != nextProps.isKeyboardShown) {
this._shouldAdjustScrollForKeyboardOnNextLayout = true;
}
}
_scrollListToBottom() {
if(!this._isMounted) return;
if (this.listHeight && this.footerY && this.footerY > this.listHeight) {
var scrollTarget = this.listHeight - this.footerY;
var scrollResponder = this.refs.itemsList.getScrollResponder();
scrollResponder.scrollTo({y: -scrollTarget});
}
}
_onScroll(event) {
this.scrollY = event.nativeEvent.contentOffset.y;
}
_renderRow(rowData, sectionID, rowID) {
let prevItem = null;
if(this.props.items.length > 1 && rowID > 0) {
prevItem = this.props.items[rowID - 1];
}
return renderItem(getItemByContentType, rowData, prevItem, this.props.userColor);
}
_onListLayout(event) {
this.prevListHeight = this.listHeight === undefined ? 0 : this.listHeight;
this.listHeight = event.nativeEvent.layout.height;
this._adjustScrollForKeyboardIfNeeded();
}
_adjustScrollForKeyboardIfNeeded() {
if(!this._isMounted) return;
if(!this._shouldAdjustScrollForKeyboardOnNextLayout) return;
this._shouldAdjustScrollForKeyboardOnNextLayout = false;
const diff = this.prevListHeight - this.listHeight;
var scrollTarget = this.scrollY + diff;
this.refs.itemsList.scrollTo({y: scrollTarget});
}
_onFooterLayout(event) {
const prevY = this.footerY;
this.footerY = event.nativeEvent.layout.y + event.nativeEvent.layout.height;
// Scrolling to bottom when footer location has changed, indicating new items arrived
if(this.footerY !== prevY) {
this._scrollListToBottomThrottled();
}
}
_renderFooter() {
return <View onLayout={this._onFooterLayout}
style={style.listFooter} />;
}
render() {
const {dataSource} = this.state;
return <ListView dataSource={dataSource} enableEmptySections={true}
renderRow={this._renderRow}
ref='itemsList' initialListSize={50}
onLayout={this._onListLayout}
renderFooter={this._renderFooter}
onScroll={this._onScroll.bind(this)} />;
}
};