react-redux 初始 ajax 数据并生成子数据
react-redux initial ajax data and generate childrens
我是react的新手-redux.I不得不说我看了很多示例项目,很多使用webpack并且将很多包结合在一起,没有详细介绍。官方的example我也看了好几遍,还是没看懂,特别是how to get initial data, and show it in the dom
和communicate with ajax
(不像jquery.ajax,在redux里用ajax好像很复杂,每个人的代码都有不同的方法和不同的风格,很难理解)
我决定建立一个文件管理器webui来学习react-redux。
首先,我只是想让它工作,所以没有 ajax:
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);
};
}
reducers/index.js
import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'
const rootReducer = combineReducers({
opener
});
export default rootReducer;
reducers/TreeNodeReducer.js
import { OPEN_NODE, CLOSE_NODE } from '../constants/ActionTypes';
const initialState = [
{
open: false
}
]
export default function opener(state = initialState, action) {
switch (action.type) {
case OPEN_NODE:
return true;
case CLOSE_NODE:
return false;
default:
return state;
}
}
reducers/index.js
import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'
const rootReducer = combineReducers({
opener
});
export default rootReducer;
store/store.js(来自 redux 演示的副本):
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import DevTools from '../containers/DevTools';
const logger = createLogger();
const finalCreateStore = compose(
// Middleware you want to use in development:
applyMiddleware(logger, thunk),
// Required! Enable Redux DevTools with the monitors you chose
DevTools.instrument()
)(createStore);
module.exports = function configureStore(initialState) {
const store = finalCreateStore(rootReducer, initialState);
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers'))
);
}
return store;
};
chrome 控制台说:Uncaught TypeError: Cannot read property 'nodes' of null
在 App render() {
我不太了解 es6,由于 react-redux 奇怪的语法让我阅读了 es6 文档,但我不确定我的代码是否正确。
特林:
我认为可能无法在列表中使用 new
实例创建 testNodes
,因此我将 testNodes
更改为普通 json:
const testNodes = [
{name:'t1',type:'t1'},
{name:'t2',type:'t2'},
{name:'t3',type:'t3'},
]
还是一样的错误
也许action不能获取全局testNodes
?我把 testNodes
移到 getFileList
,也不行。
我不知道。
解决这个问题后,我会尝试将 getFileList
内容替换为 ajax 调用。
PS:我的react-route也有奇怪的问题,chrome显示空白页面,用route包裹App
时没有错误,只是觉得react-redux太难了对于newbee...这只是一些抱怨...
简单
- 您不需要自己绑定 ActionCreators
- 你需要使用
this.props.getFileList
- 你不需要用组件的状态来管理它
例如。
import {ansyncGetFileList} from '../actions/NodeActions'
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);
根据问题更新和评论更新
- 由于您的州树没有节点映射,您需要将其放在州的根或
opener
子树中。
对于 async
操作,您必须修改您的 thunk 动作创建器
例如。
export function ansyncGetFileList() {
return dispatch => {
setTimeout(() => {
dispatch({ type: 'NODES_SUCCESS', nodes: getFileList()}); // might need to export the type as constant
}, 1000);
};
}
处理reducer中的NODES_SUCCESS
动作类型
const initialState = {
nodes: []
};
export default function nodes(state = initialState, action) {
switch (action.type) {
// ...
case 'NODES_SUCCESS':
let nodes = state.nodes.slice();
return nodes.concat(action.nodes);
// ...
}
}
使用nodes reducer来管理nodes子树
例如
import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'
import nodes from './nodes'
const rootReducer = combineReducers({
opener, nodes
});
export default rootReducer;
如上使用mapStateToProps
获取节点
关于mapDispatchToProps
The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn’t aware of Redux, and you don’t want to pass dispatch or the Redux store to it.
因为您已经可以访问 dispatch
,所以您可以直接调用它。传递地图是它的 shorthand 版本。 video
我是react的新手-redux.I不得不说我看了很多示例项目,很多使用webpack并且将很多包结合在一起,没有详细介绍。官方的example我也看了好几遍,还是没看懂,特别是how to get initial data, and show it in the dom
和communicate with ajax
(不像jquery.ajax,在redux里用ajax好像很复杂,每个人的代码都有不同的方法和不同的风格,很难理解)
我决定建立一个文件管理器webui来学习react-redux。 首先,我只是想让它工作,所以没有 ajax:
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);
};
}
reducers/index.js
import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'
const rootReducer = combineReducers({
opener
});
export default rootReducer;
reducers/TreeNodeReducer.js
import { OPEN_NODE, CLOSE_NODE } from '../constants/ActionTypes';
const initialState = [
{
open: false
}
]
export default function opener(state = initialState, action) {
switch (action.type) {
case OPEN_NODE:
return true;
case CLOSE_NODE:
return false;
default:
return state;
}
}
reducers/index.js
import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'
const rootReducer = combineReducers({
opener
});
export default rootReducer;
store/store.js(来自 redux 演示的副本):
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import DevTools from '../containers/DevTools';
const logger = createLogger();
const finalCreateStore = compose(
// Middleware you want to use in development:
applyMiddleware(logger, thunk),
// Required! Enable Redux DevTools with the monitors you chose
DevTools.instrument()
)(createStore);
module.exports = function configureStore(initialState) {
const store = finalCreateStore(rootReducer, initialState);
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers'))
);
}
return store;
};
chrome 控制台说:Uncaught TypeError: Cannot read property 'nodes' of null
在 App render() {
我不太了解 es6,由于 react-redux 奇怪的语法让我阅读了 es6 文档,但我不确定我的代码是否正确。
特林:
我认为可能无法在列表中使用
new
实例创建testNodes
,因此我将testNodes
更改为普通 json:const testNodes = [ {name:'t1',type:'t1'}, {name:'t2',type:'t2'}, {name:'t3',type:'t3'}, ]
还是一样的错误也许action不能获取全局
testNodes
?我把testNodes
移到getFileList
,也不行。
我不知道。
解决这个问题后,我会尝试将 getFileList
内容替换为 ajax 调用。
PS:我的react-route也有奇怪的问题,chrome显示空白页面,用route包裹App
时没有错误,只是觉得react-redux太难了对于newbee...这只是一些抱怨...
简单
- 您不需要自己绑定 ActionCreators
- 你需要使用
this.props.getFileList
- 你不需要用组件的状态来管理它
例如。
import {ansyncGetFileList} from '../actions/NodeActions'
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);
根据问题更新和评论更新
- 由于您的州树没有节点映射,您需要将其放在州的根或
opener
子树中。 对于
async
操作,您必须修改您的 thunk 动作创建器 例如。export function ansyncGetFileList() { return dispatch => { setTimeout(() => { dispatch({ type: 'NODES_SUCCESS', nodes: getFileList()}); // might need to export the type as constant }, 1000); }; }
处理reducer中的
NODES_SUCCESS
动作类型const initialState = { nodes: [] }; export default function nodes(state = initialState, action) { switch (action.type) { // ... case 'NODES_SUCCESS': let nodes = state.nodes.slice(); return nodes.concat(action.nodes); // ... } }
使用nodes reducer来管理nodes子树 例如
import { combineReducers } from 'redux'; import opener from './TreeNodeReducer' import nodes from './nodes' const rootReducer = combineReducers({ opener, nodes }); export default rootReducer;
如上使用
mapStateToProps
获取节点关于
mapDispatchToProps
The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn’t aware of Redux, and you don’t want to pass dispatch or the Redux store to it.
因为您已经可以访问
dispatch
,所以您可以直接调用它。传递地图是它的 shorthand 版本。 video