【问题标题】:Why are my component's redux-based props not updating?为什么我的组件基于 redux 的 props 没有更新?
【发布时间】:2021-01-21 15:24:06
【问题描述】:

在将数据存储在 redux 存储和 firebase firestore 中的 react-native 应用程序中,我有一个 onSnapshot 监听器,它监听一个集合。在这个集合中的每个文档中,都有一个具有几个属性的对象,这些属性会随着时间的推移而改变。理想情况下,当这些对象属性值之一发生变化时,它应该反映在 redux 存储中,从而反映在 UI 中。问题是,当这些值之一发生变化时,redux 存储不会更新。

听众:

export const onAgendaChange = (uid, role, dispatch) => {
    let query;
    if (role == 'A') {
        query = 'a.uid';
    } else {
        query = 'b.uid';
    }
    console.log('(actions/agenda) Firestore Read: getAgenda()');
    return firebase
        .firestore()
        .collection('events')
        .where(query, '==', uid)
        .orderBy('date')
        .onSnapshot((querySnapshot) => {
            const agenda = []
            querySnapshot.docChanges().forEach(change => {
              console.log(`change: ${change.type}`) // does not log anything
            })
            querySnapshot.forEach((doc) => {
                let event = doc.data();
                event.id = doc.id;
                agenda.push(event);
                dispatch({ type: types.LOAD_AGENDA_SUCCESS, payload: agenda });
            });
        });
};

减速器:

const INITIAL_STATE = {
    loading: false,
    events: []
};
export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case types.LOAD_AGENDA:
            return {
                ...state,
                loading: true
            };
        case types.LOAD_AGENDA_SUCCESS:
            return {
                loading: false,
                events: action.payload
            };
        default:
            return {
                ...state
            };
    }
};

使用此数据的屏幕组件:

<View style={styles.container}>
  <FlatList
    data={this.props.events}
    extraData={this.props}
    keyExtractor={(item) => {
        return (
            item.toString() +
            new Date().getTime().toString() +
            Math.floor(Math.random() * Math.floor(new Date().getTime())).toString()
        );
    }}
    renderItem={({ item, index }) => {
        const title = this.props.role == 'A' ? item.b.legalName : item.a.legalName;

        const participantCheckInStatus =
            this.props.role == 'A' ? item.checkedIn.b : item.checkedIn.a;

        const currentUserCheckInStatus =
            this.props.role == 'A' ? item.checkedIn.a : item.checkedIn.b;

        console.log("agenda type B: " + item.checkedIn.b + " agenda type A: " + item.checkedIn.a)
        return (
            <Components.AgendaItem
                title={title}
                participant={participantCheckInStatus}
                checkedIn={currentUserCheckInStatus}
                navigation={this.props.navigation}
                evnt={item}
            />
        );
    }}
/>
            </View>

此集合中的文档结构类似于:

{
  exampleMap: { a: false, b: false },
  string: '',
  string: '',
  map: { ... },
  map: { ... }
}

其他说明:

  • 无论是通过操作还是从 Firebase 控制台更新文档,在 Firestore 中都成功更新了文档
  • 即使刷新了 React Native 应用程序,使用相关数据的组件也不会更新
  • 我有另一个包含地图的文档集。当更改此其他集合的映射中的值时,使用此数据的组件会正确更新。它们有相似的 reducer,两个 reducer 都是持久化的,而且监听器是相同的。
  • 我还在我提到的这些相关减速器上使用 redux-persist,包括工作和非工作减速器
  • 我正在使用 redux-thunk

特殊观察:

  • 组件正确接收道具更新的唯一时间是应用程序安装是否是新的,这意味着该应用程序已被删除并新安装在模拟器上。任何后续运行,道具数据都不会更新。

我已经在使用这个 redux 存储的父组件和子组件中确认,当我在 firebase 控制台中更改值时,文档的 exampleMap 子属性值在 redux 中不会改变。所以我玩弄了 FlatList 的 extraData 道具,但无济于事。

无论如何,对于如何将这些更改带到 redux 商店有什么想法吗?

【问题讨论】:

  • 请编辑问题以显示未按您期望的方式运行的代码,包括对 docChanges() 的调用,以便我们可以看到您正确使用它。
  • @DougStevenson 当然,但请记住,重点是使其成为 redux 的更新,而不是 docChanges() 的非工作性质

标签: reactjs firebase redux google-cloud-firestore


【解决方案1】:

这是给你带来问题的部分:


        .onSnapshot((querySnapshot) => {
            const agenda = []
            querySnapshot.docChanges().forEach(change => {
              console.log(`change: ${change.type}`) // does not log anything
            })
            querySnapshot.forEach((doc) => {
                let event = doc.data();
                event.id = doc.id;
                agenda.push(event);
                dispatch({ type: types.LOAD_AGENDA_SUCCESS, payload: agenda });
            });
        });

问题是您正在为后续调度重复使用相同的 agenda 数组 - 在中间修改它。

在你的减速器中


        case types.LOAD_AGENDA_SUCCESS:
            return {
                loading: false,
                events: action.payload
            }

你只是在为这个对象设置事件——这意味着当 react-redux 将旧的 events 与新的 events 进行比较时,它是对同一个对象的引用。 RR 看不出有什么不同。

解决此问题的方法是在将该数组保存到您的商店之前对其进行复制:


        case types.LOAD_AGENDA_SUCCESS:
            return {
                loading: false,
                events: [...action.payload]
            }

【讨论】:

    【解决方案2】:

    需要在reducer中做如下改动:

    case types.LOAD_AGENDA_SUCCESS:
      return { ...state, loading: false, events: [...action.payload] };
    

    因为您的屏幕组件会检查 events 道具的引用以确定是否重新渲染,因此您可以通过使用扩展运算符更改引用来解决此问题,如上所示。

    此外,您应该将dispatch 调用转移到forEach 循环之外,以便agenda 数组仅与其中的所有项目一起发送到reducer,而不是为每个项目一次又一次地发送:

      querySnapshot.forEach((doc) => {
        let event = doc.data();
        event.id = doc.id;
        agenda.push(event);
      });
      dispatch({ type: types.LOAD_AGENDA_SUCCESS, payload: agenda });
    

    【讨论】:

      猜你喜欢
      • 2018-12-24
      • 2017-01-25
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      相关资源
      最近更新 更多