使用一个事件在redux

时间:2016-09-23 14:35:43

标签: reactjs redux

我正在使用React-Redux编写一个指标页面,我之前没有使用它,并且在构建它时遇到了麻烦。

基本结构是这样的:

<input id=start_date />
<input id=end_date />
<button id=submit onClick={ this.props.fetchChartData() }/>
<Chart1 />
<Chart2 />

商店结构如下:

dates
    start_date: "2016-09-16"
    end_date: "2016-09-16"
charts
    Chart1
        api_func: "get_supported_events"
        fetching: false
        fetched: false
        data: null
        error: null
    Chart2
        api_func: "get_events_closed"
        fetching: false
        fetched: false
        data: null
        error: null

使用thunk,我现在的行动包括以下功能:

function getStateURL(state){
    return state.charts.Chart1['api_func'];
}

export function fetchChartData(){
    return (dispatch, getState) => {
        dispatch(fetchChartDataStart());
        return fetch(getStateURL(getState()))
            .then((response) => response.json())
            .then((json) => dispatch(receiveChartData(json)))
            .catch((err) => dispatch(fetchChartDataError(err)));
    }
}

问题是,我不想对图表名称进行硬编码,因为我觉得我应该能够编写一个动作,因为所有图表都需要做同样的事情。

我能想到的最佳解决方案是让按钮触发图表组件可以监听的事件,以便在请求状态时它仅限于图表的部分,而不是整个状态。有没有办法让反应组件触发可被其他组件捕获的事件?

1 个答案:

答案 0 :(得分:0)

当商店只是EventEmitter的一个实例时,您提出的解决方案似乎更像是旧的Flux模型。

使用Flux,您可以<Chart />喜欢

class Chart extends Component {
  componentDidMount() {
    store.addEventListener('fetchData', this.fetchData);
  }

  componentWillUnmount() {
    store.removeEventListener('fetchData', this.fetchData);
  }

  this.fetchData() {
    api.fetchChartData(store.get('chart1.url');
  }

  render() {
    ...
  }
}

然而,Redux并不是显而易见的。但是有可能做到这一点:

class Chart1 extends Component {
  componentWillReceiveProps(nextProps) {
    if (!nextProps.fetching && !nextProps.fetched) {
      const { fetchData, url } = this.props;
      fetchData(url);
    }
  }

  render() {
    ...
  }
}

export default connect(state => ({
  fetching: state.Chart1.fetching
  fetched: state.Chart1.fetched
  url: state.Chart1.url
}), {
  fetchData
})(Chart1)

并在/action.js

export function fetchChartData(url){
    return (dispatch) => {
        dispatch(fetchChartDataStart());
        return fetch(url)
            .then((response) => response.json())
            .then((json) => dispatch(receiveChartData(json)))
            .catch((err) => dispatch(fetchChartDataError(err)));
    }
}

考虑到所有<Chart />组件中的类似功能,值得为此实现高阶组件,并将url保留为常量而不是存储。

export const fetchData = (url) => (Wrapped) => {
  class Wrapper extends Component {
      componentWillReceiveProps(nextProps) {
        if (!nextProps.fetching && !nextProps.fetched) {
          const { fetchData, url } = this.props;
          fetchData(url);
        }
      }

      render() {
        return <Wrapped {...this.props} />
      }
  }

  return connect(null, { fetchData })(Wrapper);
}

Chart.jsx中使用它:

import { chart1Url } from '.../someconstants';
import { fetchData } from '/hocs/fetchData'

const Chart1 = () => {
    return <div>...</div>;
}

export default fetchData(chartUrl)(Chart1);

尽管有可能,我仍然认为最好的解决方案是将URL存储在常量文件中,并将api函数放在另一个模块中。你可以这样做: ./api/fetchData.js

export function fetchData(url) {
  return new Promise((resolve, reject) =>
    fetch(url)
      .then((response) => response.json())
      .then((json) => resolve(json))
      .catch((err) => reject(err));
}

./ actions.js

import { fetchData } from '../api/fetchData';
import { urls } from '.../constants';

export function fetchChartData(){
    return (dispatch) => {
        dispatch(fetchChartDataStart());
        return Promise.all(urls.map((url) =>
          fetchData(url)
            .then((json) => dispatch(receiveChartData(json)))
            .catch((err) => dispatch(fetchChartDataError(err))));
    }
}