【问题标题】:React Native Flatlist Not Rerendering ReduxReact Native Flatlist 不重新渲染 Redux
【发布时间】:2019-10-24 20:37:13
【问题描述】:

当我从 redux 传递的道具发生变化时,我的 FlatList 不会更新。每次我发送消息时,我都会在 firebase 和我的 redux 商店中增加每个人的未读消息数。我确保包含密钥提取器和额外数据,但都没有帮助。唯一改变未读消息计数的是设备的重新加载。如何确保 flatList 使用 MapStateToProps 进行更新。我确保使用 Object.Assign 创建一个新对象:

行动:

export const sendMessage = (
  message,
  currentChannel,
  channelType,
  messageType
) => {
  return dispatch => {
    dispatch(chatMessageLoading());
    const currentUserID = firebaseService.auth().currentUser.uid;
    let createdAt = firebase.database.ServerValue.TIMESTAMP;
    let chatMessage = {
      text: message,
      createdAt: createdAt,
      userId: currentUserID,
      messageType: messageType
    };


    FIREBASE_REF_MESSAGES.child(channelType)
      .child(currentChannel)
      .push(chatMessage, error => {
        if (error) {
          dispatch(chatMessageError(error.message));
        } else {
          dispatch(chatMessageSuccess());
        }
      });

    const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child(channelType)
      .child(currentChannel).child('users')

    UNREAD_MESSAGES.once("value")
      .then(snapshot => {
        snapshot.forEach(user => {
          let userKey = user.key;
          // update unread messages count
          if (userKey !== currentUserID) {
            UNREAD_MESSAGES.child(userKey).transaction(function (unreadMessages) {
              if (unreadMessages === null) {
                dispatch(unreadMessageCount(currentChannel, 1))
                return 1;
              } else {
                alert(unreadMessages)
                dispatch(unreadMessageCount(currentChannel, unreadMessages + 1))
                return unreadMessages + 1;
              }
            });
          } else {
            UNREAD_MESSAGES.child(userKey).transaction(function () {
              dispatch(unreadMessageCount(currentChannel, 0))
              return 0;
            });
          }
        }
        )
      })
  };
};

export const getUserPublicChannels = () => {
  return (dispatch, state) => {
    dispatch(loadPublicChannels());
    let currentUserID = firebaseService.auth().currentUser.uid;
    // get all mountains within distance specified
    let mountainsInRange = state().session.mountainsInRange;
    // get the user selected mountain
    let selectedMountain = state().session.selectedMountain;

    // see if the selected mountain is in range to add on additional channels
    let currentMountain;
    mountainsInRange
      ? (currentMountain =
        mountainsInRange.filter(mountain => mountain.id === selectedMountain)
          .length === 1
          ? true
          : false)
      : (currentMountain = false);

    // mountain public channels (don't need to be within distance)
    let currentMountainPublicChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
      "Public"
    )
      .child(`${selectedMountain}`)
      .child("Public");

    // mountain private channels- only can see if within range
    let currentMountainPrivateChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
      "Public"
    )
      .child(`${selectedMountain}`)
      .child("Private");

    // get public channels
    return currentMountainPublicChannelsRef
      .orderByChild("key")
      .once("value")
      .then(snapshot => {
        let publicChannelsToDownload = [];

        snapshot.forEach(channelSnapshot => {
          let channelId = channelSnapshot.key;
          let channelInfo = channelSnapshot.val();
          // add the channel ID to the download list

          const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child("Public")
            .child(channelId).child('users').child(currentUserID)
          UNREAD_MESSAGES.on("value",snapshot => {
              if (snapshot.val() === null) {
                // get number of messages in thread if haven't opened
                dispatch(unreadMessageCount(channelId, 0));
              } else {
                dispatch(unreadMessageCount(channelId, snapshot.val()));
              }
            }
            )
          publicChannelsToDownload.push({ id: channelId, info: channelInfo });
        });

        // flag whether you can check in or not
        if (currentMountain) {
          dispatch(checkInAvailable());
        } else {
          dispatch(checkInNotAvailable());
        }

        // if mountain exists then get private channels/ if in range
        if (currentMountain) {
          currentMountainPrivateChannelsRef
            .orderByChild("key")
            .on("value", snapshot => {
              snapshot.forEach(channelSnapshot => {
                let channelId = channelSnapshot.key;
                let channelInfo = channelSnapshot.val();


                const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child("Public")
                  .child(channelId).child('users').child(currentUserID)
                UNREAD_MESSAGES.on("value",
                  snapshot => {
                    if (snapshot.val() === null) {
                      // get number of messages in thread if haven't opened
                      dispatch(unreadMessageCount(channelId, 0));
                    } else {
                     dispatch(unreadMessageCount(channelId, snapshot.val()));
                    }
                  }
                  )
                publicChannelsToDownload.push({ id: channelId, info: channelInfo });
              });
            });
        }
        return publicChannelsToDownload;
      })

      .then(data => {
        setTimeout(function () {
          dispatch(loadPublicChannelsSuccess(data));
        }, 150);
      });
  };
};

减速机:

case types.UNREAD_MESSAGE_SUCCESS:
        const um = Object.assign(state.unreadMessages, {[action.info]: action.unreadMessages});
    return {
      ...state,
      unreadMessages: um
    };

Container- 在里面我用未读消息将地图状态连接到道具,并作为道具传递给我的组件:

const mapStateToProps = state => {
return {
  publicChannels: state.chat.publicChannels,
  unreadMessages: state.chat.unreadMessages,
};
}

组件:

render() {
    // rendering all public channels
    const renderPublicChannels = ({ item, unreadMessages }) => {
      return (
        <ListItem
          title={item.info.Name}
          titleStyle={styles.title}
          rightTitle={(this.props.unreadMessages || {} )[item.id] > 0 && `${(this.props.unreadMessages || {} )[item.id]}`}
          rightTitleStyle={styles.rightTitle}
          rightSubtitleStyle={styles.rightSubtitle}
          rightSubtitle={(this.props.unreadMessages || {} )[item.id] > 0 && "unread"}
          chevron={true}
          bottomDivider={true}
          id={item.Name}
          containerStyle={styles.listItemStyle}
        />
      );
    };


    return (
        <View style={styles.channelList}>
          <FlatList
            data={this.props.publicChannels}
            renderItem={renderPublicChannels}
            keyExtractor={(item, index) => index.toString()}
            extraData={[this.props.publicChannels, this.props.unreadMessages]}
            removeClippedSubviews={false}
          />
        </View>
    );
  }
}

【问题讨论】:

    标签: javascript react-native chat react-native-flatlist listitem


    【解决方案1】:

    Object.assign 会将所有内容合并到作为参数提供的第一个对象中,并返回相同的对象。在redux中,你需要创建一个新的对象引用,否则不保证更改被拾取。使用这个

    const um = Object.assign({}, state.unreadMessages, {[action.info]: action.unreadMessages});
    // or
    const um = {...state.unreadMessages, [action.info]: action.unreadMessages }
    

    【讨论】:

    • 不幸的是,这仍然没有更新 FlatList,所以这是 Object.assign 之外的问题。不知道是什么
    • 从您提供的代码中无法判断出什么问题。您将不得不自己进行一些调试 - 首先将您的 Firebase 操作替换为更简单的操作,该操作只会发送 types.UNREAD_MESSAGE_SUCCESS 操作并查看它是否以这种方式工作。如果是这样,那么您知道错误出现在原始操作逻辑中,因此您可以查看那里。或者,在你的渲染中简单的 console.log 将帮助你确定你的道具是否真的改变了正确的方式,如果他们这样做了,你就会知道问题出在你的 FlatList 组件中
    • 我认为这与我在上面代码中添加的 getUserPublicChannels 中使用 ref.on 的方式有关。不幸的是,我在 firebase 上打开了另一个问题,其中 >.60 我无法在调试中 console.log() 所以我现在依赖警报,这使得调试非常困难
    • 我能够通过使用 on 和您上面的代码更改并切换到 on 来使其工作。愚蠢地我以前使用过一次。标记为正确,因为它是问题的一半
    【解决方案2】:

    Object.assign() 不返回新对象。因此,reducer 中的 unreadMessages 指向同一个对象,并且组件没有被重新渲染。

    在你的减速器中使用它

    const um = Object.assign({}, state.unreadMessages, {[action.info]: action.unreadMessages});
    

    【讨论】:

    • 不幸的是,这仍然没有更新 FlatList,所以这是 Object.assign 之外的问题。不知道是什么