React-Redux:如何在加载时使用来自异步调用的数据填充组件的prop?

时间:2016-11-16 08:33:18

标签: javascript reactjs asynchronous redux react-redux

我有这个自动完成组件,它将一组术语作为dataSource prop。我想要提供的数据位于公共API中,我已按照教程here获取以下代码。但是本教程(以及其他许多其他内容)解释了如何将这些操作绑定到事件,而我想用页面加载的数据填充此prop。我该怎么做呢?

actions.js

import fetch from 'isomorphic-fetch';

export function loadSchools(termId) {
  return {
    type: 'LOAD_SCHOOLS',
    termId
  };
}

export function receiveSchools(termId, json) {
  return {
    type: 'RECEIVE_SCHOOLS',
    termId,
    schools: json.data.children.map(child => child.data), // ???
    receivedAt: Date.now()
  };
}

export function getSchools(termId) {
  return function (dispatch) {
    dispatch(loadSchools(termId));
    return fetch('http://www.northwestern.edu/class-descriptions/4650/index-v2.json')
      .then(response => {
        if (response.status >= 400) {
          throw new Error('Bad response from server');
        }
        return response.json();
      })
      .then(data => dispatch(receiveSchools(termId, data)));
  };
}

reducers.js

const initialState = {
  schoolsData: {
    isFetching: false,
    lastUpdated: 0,
    schools: []
  }
};

function schools(state = initialState, action) {
  switch (action.type) {
    case 'LOAD_SCHOOLS':
      return {
        ...state,
        isFetching: true
      };
    case 'RECEIVE_SCHOOLS':
      return {
        ...state,
        isFetching: false,
        schools: action.schools,
        lastUpdated: receivedAt
      }
    default:
      return state;
  }
}

export default schools;

Search.jsx

import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';

export default class Search extends React.Component {
  render() {
    return (
        <AutoComplete
          hintText="Search for something."
          dataSource={this.props.searchdata}
          maxSearchResults={15}
          filter={AutoComplete.caseInsensitiveFilter}
          onNewRequest={}
        />
    );
  }
}

Search.propTypes = {
  searchdata: React.PropTypes.array.isRequired,
  onSelect: React.PropTypes.func
};

index.jsx

import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { grey500, white, fullBlack } from 'material-ui/styles/colors';
import { fade } from 'material-ui/utils/colorManipulator';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import schools from './reducers/reducers';
import colors from './colors';
import NavBar from './components/NavBar.jsx';
import Serif from './components/Serif.jsx';

const store = createStore(schools, applyMiddleware(thunkMiddleware));

const muiTheme = getMuiTheme({
  palette: {
    primary1Color: colors.northwesternPurple,
    primary2Color: colors.northwesternPurple120,
    primary3Color: grey500,
    accent1Color: colors.northwesternPurple30,
    accent2Color: colors.richBlack10,
    accent3Color: colors.richBlack50,
    textColor: colors.richBlack80,
    alternateTextColor: white,
    canvasColor: white,
    borderColor: colors.richBlack20,
    disabledColor: fade(colors.richBlack80, 0.3),
    pickerHeaderColor: colors.northwesternPurple,
    clockCircleColor: fade(colors.richBlack80, 0.07),
    shadowColor: fullBlack
  }
});

class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <MuiThemeProvider muiTheme={muiTheme}>
          <div> {/* MuiThemeProvider requires stricly one child element */}
            <NavBar />
            <Serif /> {/* This component contains SearchContainer, which in turn contains Search */}
          </div>
        </MuiThemeProvider>
      </Provider>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));

2 个答案:

答案 0 :(得分:1)

您可以从其他组件呈现搜索组件,让我们将其称为 SearchContainer SearchContainer 由来自connectreact-redux函数修饰,该函数仅作为调度操作以获取学校的角色。 SearchContainer 在呈取学校之前不会呈现搜索组件。

这里是代码看起来像的例子。在这里,我假设您不使用react-redux

首先,您在 reducers.js 中的初始状态存在一个小问题。它应该是:

const initialState = {
  isFetching: false,
  lastUpdated: 0,
  schools: []
};

function schools(state = initialState, action) {
  switch (action.type) {
    case 'LOAD_SCHOOLS':
      return {
        ...state,
        isFetching: true
      };
    case 'RECEIVE_SCHOOLS':
      return {
        ...state,
        isFetching: false,
        schools: action.schools,
        lastUpdated: receivedAt
      }
    default:
      return state;
  }
}

<强> SearchContainer.js

// ./containers/SearchContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadSchools } from '../actions/actions'
import Search from '../components/Search';

class SearchContainer extends Component { 
  componentDidMount() {
    this.props.loadSchools(this.props.termId);
  },
  render() {
    const { 
      schools, 
      isFetching
    } = this.props;
    if (isFetching) {
      return null;
    }
    return <Search schools={schools} />;
  }
}

const mapStateToProps = (state) => ({
  isFetching: state.isFetching,
  schools: state.schools
});

const mapActionsToProps = (dispatch) => ({
  loadSchools: (termId) => dispatch(loadSchools(termId)),
});

export default connect(mapStateToProps, mapActionsToProps)(SearchContainer);

这样,在第一次渲染时,不会渲染搜索组件。只有在学校装满后才能呈现。

答案 1 :(得分:0)

您可以从 componentDidMount 生命周期方法调度LOAD_SCHOOLS操作(可能在您的 Serif 组件中,但我无法查看代码)。

来自文档:

装载组件后立即调用

componentDidMount()。需要DOM节点的初始化应该放在这里。如果需要从远程端点加载数据,这是实例化网络请求的好地方。在此方法中设置状态将触发重新渲染。

https://facebook.github.io/react/docs/react-component.html#componentdidmount