反应原生 redux 不更新组件

Posted

技术标签:

【中文标题】反应原生 redux 不更新组件【英文标题】:React native redux not updating component 【发布时间】:2019-06-05 05:29:01 【问题描述】:

我正在尝试使用 react native 设置 redux,但它不会在商店更新时更新我的​​组件。

class Dinner extends React.Component 

    componentDidUpdate() 
        console.log('does not get called when store update');
    

    setSelected = (meal) => 
        var index = this.props.selectedDinner.indexOf(meal);

        if (index === -1) 
            console.log('Adding meal to selected: '+meal.name);
            if (this.props.selectedDinner[0] === null) 
                var tempArr = this.props.selectedDinner;
                tempArr[0] = meal;
                this.props.setSelectedDinner(tempArr);

             else if(this.props.selectedDinner[1] === null)  
                var tempArr = this.props.selectedDinner;
                tempArr[1] = meal;
                this.props.setSelectedDinner(tempArr);
             else if(this.props.selectedDinner[2] === null)  
                var tempArr = this.props.selectedDinner;
                tempArr[2] = meal;
                this.props.setSelectedDinner(tempArr);
            
         else 
            console.log("removing meal from selected: "+meal.name)
            var tempArr = this.props.selectedDinner;
            tempArr[index] = null;
            this.props.setSelectedDinner(tempArr);
        
        LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
        this.forceUpdate()
    ;

    render() 
        return (
          <View style=flex: 1, width: 360, justifyContent: 'center', alignItems: 'center', paddingBottom: 20>
            <View style=width: 340, backgroundColor: 'white', justifyContent: 'flex-start', alignItems: 'center'>
              <Text style=height: 50, fontSize: 20, fontWeight: 'bold', flex: 1, justifyContent: 'center', alignItems: 'center'>Dinner</Text>
              
                this.props.dinnerFeed.map((prop, key) => 
                  prop === null ?
                    <TouchableOpacity style=width: 320, height: 120, backgroundColor: 'lightgrey', flex: 1, justifyContent: 'center', alignItems: 'center', zIndex: 1, marginBottom: 10, flexShrink: 0 key=key><LoadingMealTile /></TouchableOpacity>
                    :
                    (prop === 'none' ? 
                      <TouchableOpacity style=width: 320, height: 120, backgroundColor: 'lightgrey', flex: 1, justifyContent: 'center', alignItems: 'center', zIndex: 1, marginBottom: 10, flexShrink: 0 key=key><BlankMealTile /></TouchableOpacity>
                      :
                      this.props.selectedDinner === null || this.props.selectedDinner.indexOf(prop) === null ?
                        <TouchableOpacity onPress=this.setSelected.bind(this, prop) style=width: 320, height: 120, backgroundColor: 'lightgrey', flex: 1, justifyContent: 'center', alignItems: 'center', zIndex: 1, marginBottom: 10, flexShrink: 0 key=key><MealTile selected=-1 name=prop.name id=prop.id url=prop.url key=key/></TouchableOpacity>
                        :
                        <TouchableOpacity onPress=this.setSelected.bind(this, prop) style=width: 320, height: 120, backgroundColor: 'lightgrey', flex: 1, justifyContent: 'center', alignItems: 'center', zIndex: 1, marginBottom: 10, flexShrink: 0 key=key><MealTile selected=this.props.selectedDinner.indexOf(prop) name=prop.name id=prop.id url=prop.url key=key/></TouchableOpacity>
                    )
                )  
              

              <TouchableOpacity onPress=this.props.loadFeedMeals style=width: 320, height: 50, backgroundColor: 'lightgrey', flex: 1, justifyContent: 'center', alignItems: 'center', zIndex: 1, marginBottom: 10><Text style=fontSize: 15, >Load More Meals</Text></TouchableOpacity>
            </View>
          </View>
        );
      
    


    function mapStateToProps(state)  
      return 
           dinnerFeed: state.dinnerFeed,
           selectedDinner: state.selectedDinner,
      
    ;

    function mapDispatchToProps(dispatch) 
      return 
        setDinnerMeals: (dinnerMeals) => dispatch(setDinnerMeals(dinnerMeals)),
        setSelectedDinner: (selectedDinner) => dispatch(setSelectedDinner(selectedDinner)),

      
    ;

    export default connect(mapStateToProps, mapDispatchToProps)(Dinner);

setSelectedDinner 函数正确更改了 redux 存储,但组件没有调用其 componentDidUpdate 函数

edit:这里是reducer代码


    export default (state, action) => 
        console.log(action);
        switch (action.type) 
            case "SET-SELECTED-DINNER":
                return 
                        ...state,
                        selectedDinner: action.selectedDinner
                  ;
            default:
                return state;
        
    ;

我相信这段代码不应该直接改变状态,因为我在 reactjs 项目中使用了这个 reducer 来进行 redux

【问题讨论】:

您能否添加处理 setSelectedDinner 操作的 reducer 代码。此类问题通常是由不正确的 reducer 引起的,它们会改变状态。 顺便说一句,我建议您在 mapStateToProps 中使用选择器,而不是直接访问状态属性。 抱歉延迟回复此问题。问题与您的减速器以及您如何传递 selectedDinner 对象有关。在下面查看我的编辑。 【参考方案1】:

Redux 状态被更新但连接的组件没有更新的最常见原因是 reducer 中的状态变化。一个非常相似的常见问题是没有认识到 Redux connect HoC 执行的是浅层比较。

这是因为 Redux 通过使用对象相等来检查更改。 (如果 === 比较返回 true,则认为对象没有改变。)

考虑以下不正确的减速器:

function todoApp(state = initialState, action) 
  switch (action.type) 
    case SET_VISIBILITY_FILTER:
      return state.visibilityFilter = action.filter;
    default:
      return state
  
 

由于上面的reducer改变了状态,所以没有触发更新。

一个正确的例子(取自Redux documentation)如下所示:

function todoApp(state = initialState, action) 
  switch (action.type) 
    case SET_VISIBILITY_FILTER:
      return Object.assign(, state, 
        visibilityFilter: action.filter
      )
    default:
      return state
  
 

浅比较

除了上述不改变状态但总是为任何变化返回一个新状态的最佳实践之外,重要的是要记住 Redux connect 对mapStateToProps 返回的所有对象使用这种浅层比较。

考虑您的mapStateToProps 函数的简化版本:

function mapStateToProps(state)  
    return 
        selectedDinner: state.selectedDinner,
    
;

现在考虑如何将选定的晚餐传递给setSelectedDinner 操作(再次简化):

setSelected = (meal) => 
    var index = this.props.selectedDinner.indexOf(meal);

    // when index === -1 we need to add this meal
    if (index === -1)      
        // there is no meal at index 0 so we add it                    
        if (this.props.selectedDinner[0] === null) 
            // NOTICE - the line below still references selected dinner in state!
            var tempArr = this.props.selectedDinner;
            tempArr[0] = meal;

            // NOTICE - we are calling setSelectedDinner with the same object
            // that comes from state! 
            this.props.setSelectedDinner(tempArr);
        
    

所以问题是,在你的 reducer 函数中,你只是简单地将 selectedDinner 对象替换为自身,因此 Redux 看不到任何更新。

将解决您的问题的最快更改是修改 reducer 函数以读取(切片调用克隆您的数组):

export default (state, action) => 
    switch (action.type) 
        case "SET-SELECTED-DINNER":
            return 
                    ...state,
                    selectedDinner: action.selectedDinner.slice()
              ;
        default:
            return state;
    
;

还有许多其他小的更改可以使此代码更易于使用且不易出现此类错误。两个简单的是:

    将修改 selectedDinner 数组的逻辑移出组件,并将其放在 reducer 中。

    介绍选择器

【讨论】:

我已经对其进行了编辑,因此它具有我的减速器。它应该工作相同,但我也会尝试这个解决方案 @ryan 我似乎找不到从组件中调用 setSelectedDinner 操作的位置。您是否添加了完整的组件代码?你的 setSelected 组件功能是否缺失? 我认为没有必要,但我已经添加它以防万一:D 我传递给 setSelectedDinner 的临时数组是否应该略有不同,因为索引 0 处的数组值与索引 0 处的状态值不同? 问题在于,由于redux(或react)正在执行浅比较,它所看到的只是数组是同一个对象。更改数组的内容不会更改对象。这就是为什么使用 slice 克隆阵列可以解决问题的原因。克隆是一个新对象。【参考方案2】:

我认为当您在 redux 中更新默认状态时,您应该尝试传递当前时间戳。这将使当前状态发生一些变化,并且您在课堂上的反应也会发生变化。

【讨论】:

以上是关于反应原生 redux 不更新组件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用从子组件传递给组件的反应变量更新 redux 状态

反应+还原。 Connect 不更新组件的状态

使用 redux 反应原生路由:错误:期望减速器是一个函数

在消费者之外更新反应上下文?

从 redux 更改状态后如何以及何时调用反应组件方法

使用 redux 反应原生导航 v3