react redux将数据分配给组件

时间:2016-06-20 11:46:54

标签: javascript reactjs redux

我搜索过,所有问题都与How to pass props to {this.props.children}

有关

但我的情况不同, 我将App填入初始数据nodes,并将nodes映射到TreeNode列表,我希望每个TreeNode都具有传入的属性node

伪代码:

App.render:

{nodes.map(node => 
<TreeNode key={node.name} info={node} /> 
)} 

TreeNode.render:

const { actions, nodes, info } = this.props 
return ( 
<a>{info.name}</a> 
); 

似乎节点不能作为信息传入,日志显示信息未定义。

warning.js?8a56:45 Warning: Failed propType: Required prop `info` was not specified in `TreeNode`. Check the render method of `Connect(TreeNode)`. 

TreeNode.js?10ab:57 Uncaught TypeError: Cannot read property 'name' of undefined 

下面只是一个更完整的代码与这个问题有关(我认为商店和行动关系不大):

容器/ App.js:

import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Footer from '../components/Footer';
import TreeNode from '../containers/TreeNode';
import Home from '../containers/Home';
import * as NodeActions from '../actions/NodeActions'

export default class App extends Component {

  componentWillMount() {
    // this will update the nodes on state
    this.props.actions.getNodes();
  }

  render() {
    const { nodes } = this.props
    console.log(nodes)
    return (
      <div className="main-app-container">
        <Home />
        <div className="main-app-nav">Simple Redux Boilerplate</div>
        <div>
          {nodes.map(node =>
            <TreeNode key={node.name} info={node} />
          )}
        </div>

        <Footer />
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    nodes: state.opener.nodes
  };
}


function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(NodeActions, dispatch)
  };
}

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

容器/ TreeNode.js

import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import classNames from 'classnames/bind'
import * as NodeActions from '../actions/NodeActions'

class TreeNode extends Component {


  handleClick() {
    this.setState({ open: !this.state.open })
    if (this.state.open){
      this.actions.getNodes()
    }
  }

  render() {
    const { actions, nodes, info } = this.props

    if (nodes) {
      const children =<div>{nodes.map(node => <TreeNode info={node} />)}</div>
    } else {
      const children = <div>no open</div>
    }

    return (
      <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
        <a>{info.name}</a>
        {children}
      </div>
    );
  }
}

TreeNode.propTypes = {
  info:PropTypes.object.isRequired,
  actions: PropTypes.object.isRequired
}


function mapStateToProps(state) {
  return {
    open: state.open,
    info: state.info,
    nodes: state.nodes
  };
}


function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(NodeActions, dispatch)
  };
}


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

减速器/ TreeNodeReducer.js

import { OPEN_NODE, CLOSE_NODE, GET_NODES } from '../constants/NodeActionTypes';

const initialState = {
  open: false,
  nodes: [],
  info: {}
}


const testNodes = [
  {name:'t1',type:'t1'},
  {name:'t2',type:'t2'},
  {name:'t3',type:'t3'},
]


function getFileList() {
  return {
    nodes: testNodes
  }
}


export default function opener(state = initialState, action) {
  switch (action.type) {
  case OPEN_NODE:
    var {nodes} = getFileList()  
    return {
      ...state,
      open:true,
      nodes:nodes
    };
  case CLOSE_NODE:
    return {
      ...state,
      open:false
    };
  case GET_NODES:
    var {nodes} = getFileList()
    return {
      ...state,
      nodes:nodes
    };
  default:
    return state;
  }
}

有关完整代码,请参阅我的github https://github.com/eromoe/simple-redux-boilerplate

这个错误让我很困惑。我看到的是我父母已经有了一些孩子,然后使用react.Children向他们提供道具,他们不使用redux。

2 个答案:

答案 0 :(得分:1)

这是因为您正在从父Redux连接组件中渲染Redux连接组件,并尝试将props作为状态传递给它。

为什么TreeNode.js需要连接到Redux?道具/动作应该单向传递,只有顶层组件连接到状态,所有子组件基本上都是哑组件。

TreeNode应该与此类似:

class TreeNode extends Component {


  handleClick() {
    this.setState({ open: !this.state.open })
    if (this.state.open){
      this.props.actions.getNodes();
    }
  }

  render() {
    const { nodes, info } = this.props

    if (nodes) {
      const children =<div>{nodes.map(node => <TreeNode info={node} />)}</div>
    } else {
      const children = <div>no open</div>
    }

    return (
      <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
        <a>{info.name}</a>
        {children}
        <div>{nodes.map(node => <TreeNode info={node} />)}</div>
      </div>
    );
  }
}

TreeNode.propTypes = {
  info: PropTypes.object.isRequired,
  actions: PropTypes.object.isRequired
}

export default class TreeNode;

并且父组件将像这样呈现TreeNode,将props传递给组件:

<div>
   {nodes.map(node =>
      <TreeNode key={node.name} info={node} actions={this.props.actions} />
   )}
</div>

答案 1 :(得分:1)

在循环nodes值时,您拨打TreeNode并提供属性info:这很好!

但是当渲染组件时,会调用此函数:

function mapStateToProps(state) {
  return {
    open: state.open,
    info: state.info,
    nodes: state.nodes
  };
}

如您所见,道具info将使用state.info中的值覆盖。我认为state.info值为undefined。所以React警告你TreeNode需要这个值。此警告来自组件配置:

TreeNode.propTypes = {
  info:PropTypes.object.isRequired
}

为什么state.info未定义?我认为你并没有像现在这样称呼它。你应该致电state['reducerNameSavedWhenCreatingReduxStore]。info to retreive {}`。

你不应该通过ThreeNodeprops填写connect()sprintf