为啥我的组件基于 redux 的 props 没有更新?

Posted

技术标签:

【中文标题】为啥我的组件基于 redux 的 props 没有更新?【英文标题】: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() 的非工作性质 【参考方案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 );

【讨论】:

以上是关于为啥我的组件基于 redux 的 props 没有更新?的主要内容,如果未能解决你的问题,请参考以下文章

在 Redux 将 Redux State 映射到 Props 后设置组件状态

Vue.js 父子组件,为啥我的 prop 在子组件中还没有?

React 类组件和 React 功能组件更改 redux store 后访问 props 的区别

This.props.dispatch 不是函数 - React-Redux

将 props 从 redux 映射到组件状态

reducer.state.props 在嵌套动作 react/redux 中未定义