React Native + Redux:如何在单击按钮时呈现<listview>?

时间:2016-09-24 09:17:25

标签: javascript listview reactjs react-native redux

我有两个组件:AddingAnimalPanel.jsAnimalList.js。我试图尝试执行以下操作:

AddingAnimalPanel.js中,我有一个按钮,一旦点击它,它就会调用一个函数_addAnimal()并通过redux更新状态,更新后的状态会传递到AnimalList.js并通过<ListView/>列出州。

但是我收到错误:Cannot read property 'rowIdentities' of undefined并且我没有为animalData定义初始状态。所以我尝试将初始状态属性animalData定义为数组,但我仍然遇到错误:Cannot read property 'length' of undefined

我似乎无法弄清楚出了什么问题。我做错了什么?

这是我的代码:

AddingAnimalPanel.js

  _addAnimal() {
    var animals = [{animal: 'bird'}, {animal: 'cat'}, {animal: 'dog'}]

    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    var animalData = ds.cloneWithRows(animals)

    this.props.uploadAnimalData(animalData) //Calling action creator
  }

在行动创作者中:

export const uploadAnimalData = (animalData) => {
  return {
    type: actionTypes.UPLOAD_ANIMAL_DATA,
    animalData: animalData,
  }
}

在减速器中:

const DEFAULT_STATE = {
 //Tried also both animalData:[] and animalData:{} but still got the length error
}

export default function(state = DEFAULT_STATE, action) {
  switch(action.type) {

    case actionTypes.UPLOAD_ANIMAL_DATA:
      return {
        ...state,
        animalData: action.animalData
      }
    default:
      return state
  }
}

redux正常工作,道具传递到AnimalList.js

  _renderRow(rowData){
    return (
      <AnimalListRow rowData={rowData} {...this.props}/>
    )
  }

  render() {

    return (
      <View style={styles.container}>
        <ListView
          dataSource={this.props.animalData} //ListView is causing the error
          renderRow={this._renderRow}
        />
      </View>
    )
  }
}

编辑 - 附加代码

这是我目前为redux设置的。

减速器:

const DEFAULT_STATE = {

}

export default function(state = DEFAULT_STATE, action) {
  switch(action.type) {

    case actionTypes.UPLOAD_ANIMAL_DATA:
      return {
        ...state,
        animalData: action.animalData
      }
    default:
      return state
  }
}

export const getAnimalData = (state) => ({
  animalData: state.animalData,
})

在名为connect的单独文件中使用来自react-redux的index.js,这就是animalData传递给AnimalList.js的方式:

// @flow
import AnimalList from './AnimalList'
import { connect } from 'react-redux'
import * as actions from './actions'
import {
  getAnimalData,
} from '../../reducers/rootReducer'

const mapStateToProps = (state) => ({
  ...getAnimalData(state),
})

export default connect(mapStateToProps, actions)(AnimalList)

2 个答案:

答案 0 :(得分:10)

我认为你走在正确的轨道上,这很可能是初始数据的问题。如果没有可执行的例子,很难确定确切的问题。

ListView提供任何对象或数组都不起作用。您必须向ds.cloneWithRows()组件提供ds.cloneWithRowsAndSections()ListView的结果。

可以采取多种行动方案。我建议的是将Listview.DataSource - 功能更接近消耗的位置,即AnimalList组件。您最初存储在Redux存储中的内容可能是一个空数组。来自react-reduxconnectAnimalList.js的要点可能是:

import { connect } from 'react-redux';


    ...


    render() {
      return (
        <View style={styles.container}>
          <ListView
            dataSource={this.props.animalData} //ListView isn't causing an error
            renderRow={this._renderRow}
          />
        </View>
      );
   }
}

const ds = new ListView.DataSource({
  rowHasChanged: (r1, r2) => r1 !== r2
});

const mapStateToProps = (state) => {
  return {
    animalData: ds.cloneWithRows(state.animalReducer.animalData)
  };
};

export default connect(mapStateToProps)(AnimalList);

实现相同结果的另一种方法是延迟ListView组件的渲染并暂停DataSource创建,直到animalData可用。例如AnimalList.js

  static propTypes = {
    animalData: React.PropTypes.array.isRequired
  };      

  ...

  render() {
    let animalListView = this.props.animalData.length >= 1
      <ListView
        dataSource={this.state.ds.cloneWithRows(this.props.animalData)}
        renderRow={this._renderRow}
      /> :
      null;

    return (
      <View style={styles.container}>
        {animalListView}
      </View>
    );
  }
}

编辑:我发现您的代码存在更多潜在问题。

要将状态更改传播到reducer,您需要从Redux调用dispatch。你可能想要在AddingAnimalPanel.js中做什么:

_addAnimal() {
  const { store } = this.context; // Assuming you're passing down a reference to store in context
  var animals = [{animal: 'bird'}, {animal: 'cat'}, {animal: 'dog'}]

  store.dispatch(this.props.uploadAnimalData(animals))
}

检查缩减器中的case actionTypes.UPLOAD_ANIMAL_DATA是否因此而被触发。

使用您发布的其他代码,如果我设法正确地为您的代码做好了准备,AnimalList.js

  render() {
    return (
      <View style={styles.container}>
        <ListView
          dataSource={this.props.animalData} //ListView isn't causing an error
          renderRow={this._renderRow}
        />
      </View>
    );
  }
}

const ds = new ListView.DataSource({
  rowHasChanged: (r1, r2) => r1 !== r2
});

const mapStateToProps = (state) => ({
  animalData: ds.cloneWithRows(...getAnimalData(state)),
})

export default connect(mapStateToProps, actions)(AnimalList)

我将做的是设置

const DEFAULT_STATE = {
  animalData: []
}

在您的减速机中。这种方式state.animalData始终是常规数组,AnimalList组件处理数据转换为格式ListView期望接收数据。

如果设置仍然不清楚,请尝试查找例如GitHub并浏览该代码以了解它是如何连接在一起的。

答案 1 :(得分:0)

尝试以下内容并告诉我们。首先,像你一样把减速器中的初始状态放回原处:

const DEFAULT_STATE = {
 animalData:[] 
}

然后,在reducer中尝试以下代码:

case actionTypes.UPLOAD_ANIMAL_DATA:
  return Object.assign({}, state, {
    animalData: action.animalData,
  })

最后,在mapStateToProps中使用state.animalData访问列表。最后一部分可能不需要,我只是不确定你的getAnimalData是如何使用的。

希望它有所帮助。