React Native ListView renderRow 方法未使用正确的数据源触发

React Native ListView renderRow method not firing with proper dataSource

我已经使用 React Native 一段时间了,以前从未遇到过这个问题。也许是因为通常我的数据对象实际上是一个对象,而目前它是一个对象数组。它仍然是一个数组是至关重要的,这样我才能保持正确的顺序。这是一个 Messenger 应用程序,所以一旦我按日期对消息进行排序,我需要它保持这种状态。

问题

对话中的新消息将不会呈现!我使用 REDUX/Firebase 与我的远程数据库同步,当我通过应用程序发送消息时,我正在侦听新消息,然后更新状态。我没有使用 child_appended 事件,而是使用 value 事件,因为我希望整个收件箱的邮件都发生变化。所有这一切都正确无误地发生。

state 中的 dataSource 对象更新得很好,我可以计算行数并查看它是否正确更新。我还可以查看传入数组中的最后一个对象,并查看刚刚添加了正确文本和日期的当前消息。但是,当逐步执行 renderMessageRow 函数时,我可以看到每一行都呈现 EXCEPT 新行。 WTF ... 当我在此行渲染函数中暂停或打印时,参数只是停在新消息之前的对象处。然而,当我从 renderMessageRow 方法内部打印出 this.state.dataSource 时,真正的乐趣才开始。当您这样做时,新消息就在那里!!我可以看到它并且行数显示它在 dataSource 对象中增加了一个。哈哈

我已经尝试通过多种方式更改此实现,添加大量切片或展开运算符以确保它与更改前的状态不同。什么都不起作用。奇怪的是,在我将来自 REDUX 的数据源从一个对象的对象(无法保持顺序)更改为一个对象数组之前,此操作运行良好。当它是对象的对象时,新消息总是出现在整个列表中的随机位置...

代码

export default class SingleConvoView extends Component {

    //rN Lifecycle ----------------------

    constructor(props) {
        super(props);

        this.dataProtocol = new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2,
        });

        this.state = {
            conversationListViewHeight: 0,
            dataSource: this.dataProtocol.cloneWithRows(this.props.messages),
        };
    }


    componentWillReceiveProps(nextProps, nextState) {
        if(deepEqual(this.props.messages, nextProps.messages, {strict: true}) === false) {
            //This fires every time the REDUX state changes without any problems at all
            //The messages property here has the new message appended to it
            //The row count shows 1 more showing than before sending the message
            this.updateDataSource(nextProps.messages);
        }
    }


    componentDidReceiveProps(nextProps, nextState) {
        //Tried this just incase, didn't work, commented out...
        //this.state.dataSource.rowShouldUpdate('s1', (nextProps.messages.length - 1));
    }


    render() {
        return (
            <View style={[
                styles.container,
                {
                    width: this.props.display.width,
                    height: this.props.display.height,
                }
            ]}>

                { this.renderConversation() }
            </View>
        );
    }


    renderConversation() {
        if(this.state.dataSource.getRowCount() > 0) {
            return (
                <View style={{ height: (this.props.display.height - headerBarHeight - 50) }}>
                    <ListView
                        onLayout={event => {
                            // console.log('on layout event: new content size is: ', event.nativeEvent.layout.height);
                            this.setState({ conversationListViewHeight: event.nativeEvent.layout.height });
                        }}
                        onContentSizeChange={(newWidth, newHeight) => {
                            let totalContentHeight = newHeight - this.state.conversationListViewHeight + headerBarHeight;

                            if(this.state.conversationListViewHeight === 0 || newHeight < this.state.conversationListViewHeight) totalContentHeight = 0;
                            this.conversationScrollViewRef.scrollTo({ x: 0, y: totalContentHeight, animated: false });
                        }}
                        scrollEnabled={true}
                        removeClippedSubviews={false}
                        dataSource={this.state.dataSource}
                        renderRow={this.renderMessageRow.bind(this)}
                        pageSize={this.state.dataSource.getRowCount()}
                        ref={ref => { this.conversationScrollViewRef = ref; }}
                        renderScrollComponent={this.renderScrollComponent.bind(this)} />
                </View>
            );

        } else {
            return null;
        }
    }


    renderScrollComponent(props) {
        return (
            <ScrollView 
                contentContainerStyle={{ paddingBottom: 20 }}
                style={[
                    styles.conversationBox,
                    { width: this.props.display.width - mainContainerSideMargins }
                ]} />
        );
    }


    renderMessageRow(message, sectionID, rowID, highlightRow) {
        let messageUser = message.userMessage ? 'You' : (this.props.senderFirstName || 'no name'),
            messageTime = '', messageTitle = '';

        if(message.hasOwnProperty('created')) {
            let currentSentDate = new Date(message.created);
            messageTime = `${currentSentDate.toLocaleDateString('en-US', DATE_DISPLAY_OPTIONS)}`;
        }

        messageTitle = message.userMessage ? `${messageTime}: ${messageUser}` : `${messageUser}: ${messageTime}`;

        return (
            <View style={styles.messageRow}>
                <Text style={[
                        bodyFontStyle, 
                        styles.messageOwnerHeader,
                        { 
                            color: message.userMessage ? brand_blue_color : primary_color,
                            alignSelf: message.userMessage ? 'flex-end' : 'flex-start',
                        }
                    ]}>

                    { messageTitle }
                </Text>

                <View 
                    shadowRadius={2}
                    shadowOpacity={1}
                    shadowColor={'rgba(0, 0, 0, 0.4)'}
                    shadowOffset={{width: -1, height: 1}}
                    style={styles.messageBodyContainer}>

                    <Text style={[
                            styles.messageBody,
                            { textAlign: message.userMessage ? 'right' : 'left' }
                        ]}>

                        { message.body }
                    </Text>
                </View>
            </View>
        );
    }


    //Functionality ---------------------

    updateDataSource(data) {
        if(typeof data != 'undefined' || data != null) {
            let tempData = data.slice();

            this.setState({
                dataSource: this.state.dataSource.cloneWithRows(tempData),
            });
        }
    }


}

我现在已经通过添加 initialListSize 道具解决了这个问题。我无法想象这是永久解决方案,但是由于没有其他人回应,我正在为 Google 搜索者回答这个问题。如果其他人给出更好的答案,我会删除它并给他们学分。

renderConversation() {
    if(this.state.dataSource.getRowCount() > 0) {
        return (
            <View style={{ height: (this.props.display.height - headerBarHeight - 50) }}>
                <ListView
                    onLayout={event => {
                        this.setState({ conversationListViewHeight: event.nativeEvent.layout.height });
                    }}
                    onContentSizeChange={(newWidth, newHeight) => {
                        let totalContentHeight = newHeight - this.state.conversationListViewHeight + headerBarHeight;

                        if(this.state.conversationListViewHeight === 0 || newHeight < this.state.conversationListViewHeight) totalContentHeight = 0;
                        this.conversationScrollViewRef.scrollTo({ x: 0, y: totalContentHeight, animated: false });
                    }}
                    initialListSize={this.state.dataSource.getRowCount()}
                    scrollEnabled={true}
                    removeClippedSubviews={false}
                    dataSource={this.state.dataSource}
                    renderRow={this.renderMessageRow.bind(this)}
                    pageSize={this.state.dataSource.getRowCount()}
                    ref={ref => { this.conversationScrollViewRef = ref; }}
                    renderScrollComponent={this.renderScrollComponent.bind(this)} />
            </View>
        );

    } else {
        return null;
    }
}