V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
eromoe
V2EX  ›  React

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

  •  
  •   eromoe · 2016-06-20 12:32:02 +08:00 · 13199 次点击
    这是一个创建于 2861 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先吐槽一下 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 条回复    2016-06-20 20:26:42 +08:00
    spritevan
        1
    spritevan  
       2016-06-20 13:11:16 +08:00
    let nodes = getFileList();
    // getFileList = () => { nodes: [...] }
    // this.state.nodes = { nodes: [] }

    App.render() 里面应该要用 `nodes.nodes.map()` 吧?
    yesmeck
        2
    yesmeck  
       2016-06-20 13:19:07 +08:00
    ```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
       2016-06-20 14:00:54 +08:00
    @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  
       2016-06-20 14:07:06 +08:00   ❤️ 1
    评论代码不能高亮太难看了,可以的话你把代码放 github 上我可以帮你看看。
    eromoe
        5
    eromoe  
    OP
       2016-06-20 14:23:18 +08:00
    eromoe
        6
    eromoe  
    OP
       2016-06-20 14:36:01 +08:00
    一开始没想到,只要下面这么简单就行了,因为总是觉得 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  
       2016-06-20 14:54:55 +08:00   ❤️ 1
    @eromoe github 上回复你了
    eromoe
        8
    eromoe  
    OP
       2016-06-20 15:11:55 +08:00
    @yesmeck 非常感谢! 我刚刚也是发现能直接用这个 this.props.getFileList(); 然后豁然开朗, 现在感觉都串起来了~谢谢~
    eromoe
        9
    eromoe  
    OP
       2016-06-20 17:24:16 +08:00
    @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  
       2016-06-20 17:29:18 +08:00
    没看到 github 有更新
    eromoe
        11
    eromoe  
    OP
       2016-06-20 17:33:38 +08:00
    @yesmeck Oh! 不好意思。。。换了台机子, ssh 有 passphrase , git push 之后就没注意。。。
    eromoe
        12
    eromoe  
    OP
       2016-06-20 20:25:15 +08:00
    @yesmeck 找到问题了~ 我在 TreeNode 下面也写了 connect , 原来所有子元素都是需要继承父元素的啊,除非是不同的根元素,否组不能加 connect 。
    ericls
        13
    ericls  
       2016-06-20 20:26:42 +08:00 via iPhone
    建议用 thunk
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2884 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:26 · PVG 15:26 · LAX 00:26 · JFK 03:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.