eromoe
V2EX  ›  React

react-redux 初始化 获取 数据,并生成元素加入到页面上

  •  
  •   eromoe · Jun 20, 2016 · 14135 views
  •   You need to sign in to view this topic
    This topic created in 3640 days ago, the information mentioned may be changed or developed.

    先吐槽一下 V2EX 没有保存草稿的功能,因为挂代理上的,发布的时候代理抽风了,结果写了半天全没了。。。。

    言归正传,我这 2,3 周零零碎碎的在看 react,redux,route 想从大项目里一下子把这一揽子全部搞懂(周边还有什么 webpack es6 只弄懂基本, js 的很多原生方法都没搞清楚,比如在 ajax 后面加 bind 之类的的) 看了很多例子,官方教程也看了,发现好多例子写法都不同,在 ajax 上就更不同了。。。虽然有些都是用什么 thunk 但是写的东西感觉不一样。。。小项目初始化都是直接 给 state/props 一个值,然后在 render 里直接传进去,大项目写的我根本看不懂在哪里拿的数据。。。。

    现在想自己个项目来弄懂这些问题,准备弄个 bypy 的 file manager webui , 结合了 2 个我觉得比较能看懂的项目(一个 redux demo 一个 react route 的 demo )

    搞了半天,route怎么都显示不出来。。。所以先去掉了。。。先搞初始化 因为 file manager 所有点击的节点都是一样的,我想做一个自包含的 componnent ,但是由于初始化传参的问题,感觉怎么写都不对。。。所以想先把这个搞懂,就做了个最简单: App 显示根目录下的所有节点(TreeNode)

    但是碰到了个错误 Uncaught TypeError: Cannot read property 'nodes' of null 在 App.render 的地方报错的

    主要代码如下

    containers/App.js:

    import React, { Component, PropTypes } from 'react';
    import { bindActionCreators } from 'redux';
    import { connect } from 'react-redux';
    import {getFileList} from '../actions/NodeActions'
    import Footer from '../components/Footer';
    import TreeNode from '../containers/TreeNode';
    import Home from '../containers/Home';
    
    
    export default class App extends Component {
    
      componentDidMount() {
        let nodes = getFileList();
        this.setState({
          nodes: nodes
        });
      }
    
      render() {
        const { actions } = this.props;
        const { nodes } = this.state;
        return (
          <div className="main-app-container">
            <Home />
            <div className="main-app-nav">Simple Redux Boilerplate</div>
            {nodes.map(node =>
              <TreeNode key={node.name} node={node} {...actions}  />
            )}
            <Footer />
          </div>
        );
      }
    }
    
    function mapStateToProps(state) {
      return {
        test: state.test
      };
    }
    
    
    function mapDispatchToProps(dispatch) {
      return {
        actions: bindActionCreators(getFileList, dispatch)
      };
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(App);
    

    actions/NodeActions.js:

    import { OPEN_NODE, CLOSE_NODE } from '../constants/ActionTypes';
    
    export function openNode() {
      return {
        type: OPEN_NODE
      };
    }
    
    export function closeNode() {
      return {
        type: CLOSE_NODE
      };
    }
    
    class NodeModel {
        constructor(name, path, type, right) {
            this.name = name;
            this.path = path;
            this.type = type;
            this.right = right;
        }
    }
    
    const testNodes = [
      new NodeModel('t1','t1', 'd', '777'),
      new NodeModel('t2','t2', 'd', '447'),
      new NodeModel('t3','t3', 'd', '667'),
    ]
    
    export function getFileList() {
      return {
        nodes: testNodes
      }
    }
    
    export function ansyncGetFileList() {
      return dispatch => {
        setTimeout(() => {
          dispatch(getFileList());
        }, 1000);
      };
    }
    

    1.我觉得可能是 testNodes 不能那么初始化,就直接改成了

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

    没效果

    2.可能是 scope 的原因,就把 testNodes 移到 getFileList 里,还是同样的错。

    真的 have no idea.

    13 replies    2016-06-20 20:26:42 +08:00
    spritevan
        1
    spritevan  
       Jun 20, 2016
    let nodes = getFileList();
    // getFileList = () => { nodes: [...] }
    // this.state.nodes = { nodes: [] }

    App.render() 里面应该要用 `nodes.nodes.map()` 吧?
    yesmeck
        2
    yesmeck  
       Jun 20, 2016
    ```javascript
    // state 一个初始值
    // 因为 componentDidMount 在 render 后执行,所以第一次 rander 的时候, state 还没 nodes
    constructor(props) {
    super(props)

    this.state = {
    nodes: []
    }
    }
    ```

    ```javascript
    function mapDispatchToProps(dispatch) {
    return {
    actions: bindActionCreators(getFileList, dispatch) // getFileList 并不是个 action creator ,不需要 bind
    };
    }
    ```
    eromoe
        3
    eromoe  
    OP
       Jun 20, 2016
    @spritevan 噢!谢谢指出, 这里是不是用 var { xx } = xxx; 或者 {...nodes} 更好?

    @yesmeck
    加了 constructor ,我在想是不是能在 constructor 里直接赋初始值,但是好像又在哪里看到 react 不建议这么做,又好像没有。。。
    代码改成下面这样
    1.
    ```
    render() {
    const { actions } = this.props
    const { nodes } = this.state
    console.log(nodes)
    return (
    <div className="main-app-container">
    <Home />
    <div className="main-app-nav">Simple Redux Boilerplate</div>
    {nodes.nodes.map(node =>
    <TreeNode key={node.name} node={node} {...actions} />
    )}
    <Footer />
    </div>
    );
    }
    ```
    log 出来如下,而且页面不显示

    []

    错误:App.js?4495:35 Uncaught TypeError: Cannot read property 'map' of undefined

    2.

    ```
    render() {
    const { actions } = this.props
    const { nodes } = this.state
    console.log(nodes)
    return (
    <div className="main-app-container">
    <Home />
    <div className="main-app-nav">Simple Redux Boilerplate</div>
    {nodes.map(node =>
    <TreeNode key={node.name} node={node} {...actions} />
    )}
    <Footer />
    </div>
    );
    }
    ```
    不用 nodes.nodes.map ,会 log 2 遍,然后显示出页面 ,并报错

    []
    Object {nodes: Array[3]}

    错误 : App.js?4495:35 Uncaught TypeError: nodes.map is not a function

    3. 修改赋值部分
    ```
    componentDidMount() {
    let {nodes} = getFileList()
    this.setState({
    nodes: nodes
    })
    }
    ```
    使用 2. 原始的 render 就不会出错,按理来说
    var { nodes} = getFileList();
    nodes.map

    应该等于
    var nodes = getFileList();
    nodes.nodes.map

    好奇怪。。。
    yesmeck
        4
    yesmeck  
       Jun 20, 2016   ❤️ 1
    评论代码不能高亮太难看了,可以的话你把代码放 github 上我可以帮你看看。
    eromoe
        5
    eromoe  
    OP
       Jun 20, 2016
    eromoe
        6
    eromoe  
    OP
       Jun 20, 2016
    一开始没想到,只要下面这么简单就行了,因为总是觉得 reducer 只是用来更新的,不能初始化,又戳破了一个盲点~

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

    render() {
    // will be re-rendered once store updated
    const {nodes} = this.props;
    // use nodes
    }

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

    export default connect(
    mapStateToProps,
    { getFileList: ansyncGetFileList }
    )(App);
    yesmeck
        7
    yesmeck  
       Jun 20, 2016   ❤️ 1
    @eromoe github 上回复你了
    eromoe
        8
    eromoe  
    OP
       Jun 20, 2016
    @yesmeck 非常感谢! 我刚刚也是发现能直接用这个 this.props.getFileList(); 然后豁然开朗, 现在感觉都串起来了~谢谢~
    eromoe
        9
    eromoe  
    OP
       Jun 20, 2016
    @yesmeck
    又发现个问题。。。 我想把 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>
    );
    ```

    貌似 info 没有传进去。。。 log 显示是 undefined


    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


    google 了一圈 没看到问类似问题的,都是什么 parent 传数据到 child 的问题,他们的 child 都是已经存在的。。。
    如果这些信息没办法判断问题的话, 我也更新了 gitbub
    yesmeck
        10
    yesmeck  
       Jun 20, 2016
    没看到 github 有更新
    eromoe
        11
    eromoe  
    OP
       Jun 20, 2016
    @yesmeck Oh! 不好意思。。。换了台机子, ssh 有 passphrase , git push 之后就没注意。。。
    eromoe
        12
    eromoe  
    OP
       Jun 20, 2016
    @yesmeck 找到问题了~ 我在 TreeNode 下面也写了 connect , 原来所有子元素都是需要继承父元素的啊,除非是不同的根元素,否组不能加 connect 。
    ericls
        13
    ericls  
       Jun 20, 2016 via iPhone
    建议用 thunk
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5453 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 08:55 · PVG 16:55 · LAX 01:55 · JFK 04:55
    ♥ Do have faith in what you're doing.