更新redux状态不会调用componentWillReceiveProps

时间:2018-03-07 10:33:00

标签: javascript reactjs react-native redux

我目前正在处理某款应用,虽然我遇到的问题与此处相同,Updating Redux state does not trigger componentWillReceiveProps

我已经阅读了这个答案并且没有改变状态,当我登录mapStateToProps时我可以看到不同的状态,但是我的componentWillReceiveProps没有被触发。

我的代码如下:



const mapReducer = (state = {}, action) => {
  switch (action.type) {
    case 'SET_MARKER':
      return action.selectedMarker;
    default:
      return state
  }
}

export default mapReducer

    //mapActions.js

export const setMarker = (selectedMarker) => {
  //Used to set the one that the user has selected.
  return {
    type: 'SET_MARKER',
    selectedMarker
  }
}

    //InformationScreen.js

const mapStateToProps = (state) => {
  console.log('returning in mapStateToProps');
  console.log(state.mapReducer);
  //Here I see that state.mapReducer is different everytime.
  return {
    marker: state.mapReducer,
    user: state.userReducer,
    completed: state.completedReducer
  }
}

class InformationScreen extends React.Component {

  componentWillReceiveProps(nextProps) {
      //No logs in here.
      console.log('Receiving props and the marker is');
      console.log(nextProps.marker);
  }

  render() {
    const { marker } = this.props;
    console.log(marker);
    // Here the marker does update.
    return(<Text> Hello world </Text>);
  }

}

export default connect(mapStateToProps)(InformationScreen);

import app from './reducers';
let store = createStore(app);

export default class App extends React.Component {

      state = {
        isLoadingComplete: false,
      };
    
      render() {
        if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
          return (
            <AppLoading
              startAsync={this._loadResourcesAsync}
              onError={this._handleLoadingError}
              onFinish={this._handleFinishLoading}
            />
          );
        } else {
          return (
            <ActionSheetProvider>
            <Provider store={store}>
              <View style={styles.container}>
                {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
                {Platform.OS === 'android' &&
                  <View style={styles.statusBarUnderlay} />}
                  <RootNavigation />
              </View>
            </Provider>
            </ActionSheetProvider>
          );
        }
      }

}

//index.js

    import { combineReducers } from 'redux'
    import mapReducer from './mapReducer'
    
    const app = combineReducers({
      mapReducer,
      //other reducers
    })
    
    export default app


//Dispatching the action from


    import { connect } from 'react-redux';
    import { setMarker } from '../actions/mapActions';
    import { Container, Header, Tab, Tabs, TabHeading, List, ListItem, Left, Thumbnail, Body, Separator, Badge, Right} from 'native-base';
    import _ from 'lodash';
    
    import GLOBALS from '../constants/Globals';
    
    const mapStateToProps = (state) => {
      return {
        user: state.userReducer,
        challenges: state.allChallengesReducer
      }
    }
    
    class MyChallengesScreen extends React.Component {
      static navigationOptions = {
        title: 'My Challenges',
        headerTintColor: 'rgb(255, 255, 255)',
        headerStyle: { backgroundColor: 'rgba(77, 90, 139, 1)'}
      };
    
      componentDidMount() {
        this.handleRefresh();
      }
    
      componentWillReceiveProps(nextProps) {
        if (nextProps.user.facebookId) {
          this.handleRefresh(nextProps);
        }
      }
    
      constructor (props) {
        super(props);
        this.state = {
          refreshing: false,
        }
      }
    
      markerPressed = (marker) => {
        //setChallenge.
        marker.coordinate = {latitude : +marker.latitude, longitude: +marker.longitude};
        console.log('Setting the marker');
        this.props.dispatch(setMarker(marker));
        this.props.navigation.navigate('InformationScreen');
      }
    
    
      render() {
        return (
          <Button onPress={() => this.markerPressed()}></Button>
        )
      }
    }
    export default connect(mapStateToProps)(MarkersScreen);
&#13;
&#13;
&#13;

我希望其他人之前见过类似的东西。感谢您提供任何帮助。

编辑:很遗憾,我还没有能够解决这个问题。但是在使用Redux调试器时我发现了一些非常有趣的东西。在调度动作后调用componentWillReceiveProps然后跳过&#39;跳过&#39;那个动作。看起来很奇怪,但至少它是一些东西。是时候继续挖掘了。

4 个答案:

答案 0 :(得分:3)

connect将浅显比较mapStateToProps的输出与mapStateToProps的上一输出。如果没有更改,则不会重新呈现连接的组件,即InformationScreen。正如你所说的那样“肯定会改变状态”,浅层比较会发现mapStateToProps的输出没有区别。

您可以通过传递正确的选项来覆盖避免重新渲染的这种行为。 connect接受options作为第4个参数,这是您需要设置pure: false的对象。

参考https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options

  

[pure](Boolean):如果为true,则connect()将避免重新渲染并调用mapStateToProps,mapDispatchToProps和mergeProps,如果相关的state / props对象基于各自的相等性检查保持相等。假设包装组件是一个“纯”组件,并且不依赖于其道具和所选Redux商店状态之外的任何输入或状态。默认值:true

答案 1 :(得分:1)

你的reducer应该只返回一个代表状态的新对象(不改变当前状态)。 你的减速机应该是这样的

const mapReducer = (state = {selectedMarker: null}, action) => {
  switch (action.type) {
    case 'SET_MARKER':
      return Object.assign({}, state, {
        selectedMarker: action.selectedMarker
      });
    default:
      return state
  }
}

Object.assign通过添加后面所有参数中存在的所有属性来改变第一个参数。这意味着它将通过首先添加{}中的属性然后添加属性state来改变selectedMarker: action.selectedMarker。如果state已经有selectedMarker,则会在新对象中覆盖。

并在mapStateToProps

const mapStateToProps = (state) => {
  return {
    marker: state.mapReducer.selectedMarker,
    ...
  }
}

对象突变mapStateToProps问题之后,控制台日志显示不同的值,您无法直观地判断它是否是一个变异对象。 connect中发生的事情是,新的marker道具针对之前的===道具进行了严格平等(marker)的测试。事情是,对象看起来如何,它具有什么属性等无关紧要。只有被检查的东西是对象引用是否相同

https://redux.js.org/basics/reducers#handling-actions

https://redux.js.org/basics/reducers#note-on-object.assign

答案 2 :(得分:0)

真正的问题在于:

 markerPressed = (marker) => {
    //setChallenge.
    marker.coordinate = {latitude : +marker.latitude, longitude: +marker.longitude};
    console.log('Setting the marker');
    this.props.dispatch(setMarker(marker));
    this.props.navigation.navigate('InformationScreen');
  }

您正在修改现有标记对象,并调度操作以将其放入商店。 reducer只返回给定的标记对象。

我们始终强调 reducer 需要是可以不可更新地更新数据的纯函数,但实际上您需要创建新数据无论您在何处创建它。如果你的减速器只有return action.someValue,那么创建的逻辑 someValue需要不可改变地创建它。

因此,对于您的情况,您的markerPressed()类方法需要对标记进行复制并为其指定新的坐标值。

答案 3 :(得分:0)

哇!我很高兴地说这个问题已得到解决。似乎原因在于,由于InformationScreen位于StackNavigator中,因此每次都会创建一个新实例。从第一次创建对象时,不会调用componentWillReceiveProps,这就解释了它。另外T 感谢@markerikson,指出我应该为每个动作调度重新创建一个标记对象。再次感谢,很高兴能够重新开始工作!这可能是一个菜鸟错误。

参考:https://shripadk.github.io/react/tips/componentWillReceiveProps-not-triggered-after-mounting.html