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 domcommunicate 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 nullApp render() {

我不太了解 es6,由于 react-redux 奇怪的语法让我阅读了 es6 文档,但我不确定我的代码是否正确。

特林:

  1. 我认为可能无法在列表中使用 new 实例创建 testNodes,因此我将 testNodes 更改为普通 json: const testNodes = [ {name:'t1',type:'t1'}, {name:'t2',type:'t2'}, {name:'t3',type:'t3'}, ] 还是一样的错误

  2. 也许action不能获取全局testNodes?我把 testNodes 移到 getFileList,也不行。

我不知道。 解决这个问题后,我会尝试将 getFileList 内容替换为 ajax 调用。

PS:我的react-route也有奇怪的问题,chrome显示空白页面,用route包裹App时没有错误,只是觉得react-redux太难了对于newbee...这只是一些抱怨...

简单

  1. 您不需要自己绑定 ActionCreators
  2. 你需要使用this.props.getFileList
  3. 你不需要用组件的状态来管理它

例如。

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);

Great Example

根据问题更新和评论更新

  1. 由于您的州树没有节点映射,您需要将其放在州的根或 opener 子树中。
  2. 对于 async 操作,您必须修改您的 thunk 动作创建器 例如。

    export function ansyncGetFileList() {
      return dispatch => {
        setTimeout(() => {
          dispatch({ type: 'NODES_SUCCESS', nodes: getFileList()}); // might need to export the type as constant
        }, 1000);
      };
    }
    
  3. 处理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);
      // ...
      }
    }
    
  4. 使用nodes reducer来管理nodes子树 例如

    import { combineReducers } from 'redux';
    import opener from './TreeNodeReducer'
    import nodes from './nodes'
    
    const rootReducer = combineReducers({
      opener, nodes
    });
    
    export default rootReducer;
    
  5. 如上使用mapStateToProps获取节点

  6. 关于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