React-redux исходные данные ajax и сгенерировать дочерние элементы

Я новичок в react-redux. Должен сказать, что я читал много примеров проекта, многие используют webpack и объединяют множество пакетов без подробного описания. Я также несколько раз читал официальный пример, но все еще не могу его хорошо понять, особенно в how to get initial data, and show it in the dom и communicate with ajax (в отличие от jquery.ajax, использование ajax в redux кажется очень сложным, у каждого кода свой подход и другой стиль, что сильно затрудняет понимать)

Решаю создать файловый менеджер webui, чтобы изучить react-redux. Для начала я просто хочу, чтобы он работал, поэтому без ajax:

контейнеры / 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);

действия / 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);
  };
}

редукторы / index.js

import { combineReducers } from 'redux';
import opener from './TreeNodeReducer'

const rootReducer = combineReducers({
  opener
});

export default rootReducer;

редукторы / 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;
  }
}

редукторы / 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;
};

хромированная консоль говорит: Uncaught TypeError: Cannot read property 'nodes' of null в App render() {

Я плохо знаю es6 из-за странного синтаксиса response-redux, заставляющего меня читать документ es6, но я не уверен, что мой код правильный.

Тринг:

  1. Я думаю, что, возможно, не удастся создать testNodes с экземпляром new в списке, поэтому я меняю testNodes на простой json: const testNodes = [ {name:'t1',type:'t1'}, {name:'t2',type:'t2'}, {name:'t3',type:'t3'}, ] Все еще та же ошибка

  2. может экшен не может получить testNodes глобальный? Я перехожу testNodes в getFileList, тоже не работаю.

Я понятия не имею. Решив эту проблему, я бы попытался заменить содержимое getFileList на вызов ajax.

PS: у моего реактивного маршрута также есть странная проблема, хром показывает пустую страницу и нет ошибок, когда я оборачиваю App маршрутом, просто чувствую, что react-redux так сложно для newbee ... это просто некоторые жалобы ...


person Mithril    schedule 20.06.2016    source источник
comment
Можете ли вы показать нам, как вы настраивали хранилище с корневым редуктором?   -  person code-jaff    schedule 20.06.2016
comment
@ code-jaff Я думаю, что store и reducer не имеют большого отношения к этому вопросу, поэтому не помещайте их, чтобы сделать свежий взгляд. Теперь добавляю код в вопрос.   -  person Mithril    schedule 20.06.2016


Ответы (1)


Просто

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

Отличный пример

Обновление на основе обновления вопроса и комментария

  1. поскольку в вашем дереве состояний нет карты для узлов, вам нужно, чтобы она находилась в корне состояния или opener поддереве.
  2. для работы async вам нужно будет изменить создателя действия преобразователя, например,

    export function ansyncGetFileList() {
      return dispatch => {
        setTimeout(() => {
          dispatch({ type: 'NODES_SUCCESS', nodes: getFileList()}); // might need to export the type as constant
        }, 1000);
      };
    }
    
  3. обрабатывать тип действия 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. используйте редуктор узлов для управления поддеревом узлов, например.

    import { combineReducers } from 'redux';
    import opener from './TreeNodeReducer'
    import nodes from './nodes'
    
    const rootReducer = combineReducers({
      opener, nodes
    });
    
    export default rootReducer;
    
  5. используйте mapStateToProps, как указано выше, чтобы получить узлы

  6. # P9 #
    # P10 #
    # P11 #
person code-jaff    schedule 20.06.2016
comment
Большое спасибо, я никогда не думал, что просто позвонить this.props.getFileList(); подойдет для моей цели! Но почему мне не нужно bindActionCreators? Я вижу много примеров его использования, и некоторые из них передают действия дочернему узлу. - person Mithril; 20.06.2016
comment
О, я понимаю, getFileList в моем коде не является ActionCreater, поэтому связывать не нужно. - person Mithril; 20.06.2016