Redux状态未按预期收到

时间:2017-10-02 00:53:09

标签: javascript react-native react-redux

我第一次使用redux,而且我正在使用一些微妙的东西。

我有一个名为Dashboard的容器,它显示两个SimpleTabs。一个简单的选项卡是按下的组件,并为其按下的项目返回一个数字。我可以看到调度的操作,事件处理程序触发等,但是在mapStateToProps中接收的状态永远不会包含项值。这可能就是为什么渲染永远不会被触发,因为状态没有改变。

注意:我使用Ignite样板作为起点。它使用了reduxsauce,所以DashboardRedux.js可能看起来有点不寻常。

Dashboard.js

import React, { Component } from 'react'
import { ScrollView, Text, View, Image, TouchableOpacity, StyleSheet } from 'react-native'
import moment from 'moment'
import { Images, Colors, Metrics, ApplicationStyles } from '../Themes'
import SimpleTab from '../Components/SimpleTab'
import DashboardHeader from '../Components/DashboardHeader'
import DashboardActions from '../Redux/DashboardRedux'
import { connect } from 'react-redux'


export class Dashboard extends Component {
  //TODO make numbers into enums
  constructor(props) {
    super(props)
    this.updateTimeframe = this.updateTimeframe.bind(this)
    this.updateAnalysisView = this.updateAnalysisView.bind(this)
    const curTimeframe = 0
    const curAnalysisView = 0
    this.state = {curTimeframe, curAnalysisView}
  }

  // Event handler for timeframe tab
  updateTimeframe(newValue) {
    //newValue gets received as expected
    this.props.updateTimeframe(newValue)
  }

  // Event handler for analysisview tab
  updateAnalysisView(newValue) {
    this.props.updateAnalysisView(newValue)
  }

  getUpdateTime = () => {
    let s = moment().format("h:mm a")
    return s
  }

  // Takes us back to login
  openLoginScreen = () => {
    //TODO does  navigater have notion of <back>?
    this.props.navigation.navigate('LoginScreen')
  }


  // For info on flex: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
  render () {
    let styles = ApplicationStyles.screen
    /*
    let localStyles = StyleSheet.create({
        container: {
          paddingBottom: Metrics.baseMargin
        },
        centered: {
          alignItems: 'center'
        }
      })

    console.log(styles)
    */

    return (
      //Problem: this.props.curTimeframe is always undefined
      <View style={styles.mainContainer}>

        <DashboardHeader updateTime={this.getUpdateTime()}></DashboardHeader>

        <View style={{justifyContent: 'space-between'}} >
          <SimpleTab
            onSelect={this.updateTimeframe}
            curTab={this.props.curTimeframe}
            tabNames={["TODAY", "1W", "1M", "3M", "6M"]}
          />
        </View>

        <View style={{flex:1}} >
          <Text style={{color: Colors.snow}}>
            Analytical stuff for {this.props.curTimeframe} and {this.props.curAnalysisView}
          </Text>
        </View>

        <View style={{height:60, justifyContent: 'space-between'}} >
          <SimpleTab
            onSelect={this.updateAnalysisView}
            curTab={this.props.curAnalysisView}
            tabNames={["HOME", "DAYPART", "REC", "INGRED", "SETTINGS"]}
          />
        </View>
      </View>

    )}

}
const mapStateToProps = (state) => {
  // Problem: state passed never contains curAnalysisView or curTimeframe
  return {
    curAnalysisView: state.curAnalysisView,
    curTimeframe: state.curTimeframe
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    updateTimeframe: newValue => dispatch(DashboardActions.updateTimeframe(newValue)),
    updateAnalysisView: newValue => dispatch(DashboardActions.updateAnalysisView(newValue))
  }
}


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

DashboardRedux.js

    import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  updateTimeframe: ['newValue'],
  updateAnalysisView: ['newValue'],
})

export default Creators
export const DashboardTypes = Types

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  curTimeframe: 0,
  curAnalysisView: 0
})

/* ------------- Reducers ------------- */
export const updateTimeframe = (state, {newValue}) => {
  //newValue gets passed as expected
  return state.merge({curTimeframe: newValue});
}

export const updateAnalysisView = (state, {newValue}) => {
  return state.merge({curAnalysisView: newValue});
}

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.UPDATE_TIMEFRAME]: updateTimeframe,
  [Types.UPDATE_ANALYSIS_VIEW]: updateAnalysisView
})

SimpleTab.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { View, Text, Image, StyleSheet, TouchableHighlight } from 'react-native'
import { Colors, Metrics, Fonts, Images } from '../Themes/'
import styles from '../Themes/ApplicationStyles'

export default class SimpleTab extends Component {
  static defaultProps = {
    onSelect: null,
    curTab: 0,
    tabNames: ["Tab1", "Tab2", "Tab3"]
  }

  static propTypes = {
    onSelect: PropTypes.func,
    curTab: PropTypes.number,
    tabNames: PropTypes.array
  }


  tabSelected = (tabNum) => {
    this.props.onSelect(tabNum);
  }

  renderTabBar = () => {
    let localStyles = StyleSheet.create({
          unselectedText: {
            marginTop: Metrics.baseMargin,
            marginHorizontal: Metrics.baseMargin,
            textAlign: 'center',
            fontFamily: Fonts.type.base,
            fontSize: Fonts.size.regular,
            color: Colors.snow
          },
          selectedText: {
            marginTop: Metrics.baseMargin,
            marginHorizontal: Metrics.baseMargin,
            textAlign: 'center',
            fontFamily: Fonts.type.base,
            fontSize: Fonts.size.regular,
            fontWeight: 'bold',
            color: Colors.fire
          }
        })

    let result = []
    for (i=0; i<this.props.tabNames.length; i++) {
      let tabStyle = (i == this.props.curTab) ? localStyles.selectedText : localStyles.unselectedText
      result.push(
        <TouchableHighlight key={this.props.tabNames[i]} onPress={this.tabSelected.bind(this, i)}>
          <Text style={tabStyle}>{this.props.tabNames[i]}</Text>
        </TouchableHighlight>
      )
    }
    return result
  }

  render () {
    console.log("rendering tab")

    return (
      <View flexDirection='row' style={styles.contentContainer}>
        {this.renderTabBar()}
      </View>
    )
  }
}

3 个答案:

答案 0 :(得分:0)

MapStateToProps通过reducers接收新的状态属性。 Dashboard.js中的MapStatetoProps应如下所示,以获取新值。

const mapStateToProps = (state) => {
// Problem: state passed never contains curAnalysisView or curTimeframe
//new state values should be accessed via reducers..
return {
    curAnalysisView: state.updateAnalysisView['curAnalysisView'],
    curTimeframe: state.updateTimeframe['curTimeframe']
 }
}

答案 1 :(得分:0)

mapStateToProps应该:

const mapStateToProps = (state) => {
const stateObj = state.toJS()
return {
    curAnalysisView: stateObj.curAnalysisView,
    curTimeframe: stateObj.curTimeframe
 }
}

.toJS()函数将它从不可变对象转换为JS对象。

此外,reducer应该有一个默认情况,它只返回没有传递动作时的当前状态。

答案 2 :(得分:0)

事实证明我需要指定&#34;仪表板&#34;州树的分支是这样的:

const mapStateToProps = (state) => {
  return {
    curAnalysisView: state.dashboard.curAnalysisView,
    curTimeframe: state.dashboard.curTimeframe
  }
}