
先吐槽一下 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.
1 spritevan 2016-06-20 13:11:16 +08:00 let nodes = getFileList(); // getFileList = () => { nodes: [...] } // this.state.nodes = { nodes: [] } App.render() 里面应该要用 `nodes.nodes.map()` 吧? |
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 }; } ``` |
3 eromoe OP @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 好奇怪。。。 |
4 yesmeck 2016-06-20 14:07:06 +08:00 评论代码不能高亮太难看了,可以的话你把代码放 github 上我可以帮你看看。 |
5 eromoe OP |
6 eromoe OP 一开始没想到,只要下面这么简单就行了,因为总是觉得 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); |
8 eromoe OP @yesmeck 非常感谢! 我刚刚也是发现能直接用这个 this.props.getFileList(); 然后豁然开朗, 现在感觉都串起来了~谢谢~ |
9 eromoe OP @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 |
10 yesmeck 2016-06-20 17:29:18 +08:00 没看到 github 有更新 |
11 eromoe OP @yesmeck Oh! 不好意思。。。换了台机子, ssh 有 passphrase , git push 之后就没注意。。。 |
12 eromoe OP @yesmeck 找到问题了~ 我在 TreeNode 下面也写了 connect , 原来所有子元素都是需要继承父元素的啊,除非是不同的根元素,否组不能加 connect 。 |
13 ericls 2016-06-20 20:26:42 +08:00 via iPhone 建议用 thunk |