先吐槽一下 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 1
评论代码不能高亮太难看了,可以的话你把代码放 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
|