如何在 Redux 中为每个实例创建一个商店? [英] How to create one store per instance in Redux?

查看:43
本文介绍了如何在 Redux 中为每个实例创建一个商店?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时在 Redux 应用程序中为每个实例创建一个存储会很有用.Redux 的创建者自己创建了一个 Gist,描述了如何实现这一点:https://gist.github.com/gaearon/eeee2f619620ab7b55673a4ee2bf8400

Sometimes it would be useful to create one store per instance in Redux applications. The creator of Redux itself created a Gist which describes how to achieve this: https://gist.github.com/gaearon/eeee2f619620ab7b55673a4ee2bf8400

我已经在 Gist 中问过这个问题,但我认为 StackOverflow 更适合回答这个问题:

I asked this question already in the Gist but I think StackOverflow would be a better place for this question:

我想知道如何将动作dispatch 到组件自己的特殊存储?有没有办法访问 <Provider/>store-prop 的每个 <SubApp/>(以及它们的子组件)?

I wonder how to dispatch actions to the component's own special store? Is there a way to access the store-prop of the <Provider /> for each <SubApp /> (and their child-components)?

例如:我有一些 API 类在从远程服务器获取数据后调用 dispatch.但是由于我无法导入普通"存储,那么处理自定义存储以使它们可用于其他类/文件/服务的最佳方法是什么?

For example: I have some API classes that call dispatch after fetching data from a remote server. But since I can't import the "normal" store, what would be the best way to deal with custom stores to make them available for other classes/files/services?

所以我让它工作了,但我认为这是一种非常肮脏的方式(请注意代码中的 UGLY? 注释):

So I got it working, but I think it's a really dirty way (mind the UGLY? comments inside the code):

提供商:

通过在构造函数中创建商店来为每个实例创建一个商店:

Create a store per instance by creating the store inside the constructor:

export default class OffersGridProvider extends React.Component {
  constructor(props) {
    super(props)
    this.store = createStore(reducers);
  }

  render() {
    return (
      <Provider store={this.store}>
        <OffersGridContainer offers={this.props.offers} />
      </Provider>
    );
  }
}

容器:

提供者为 this 存储注入了一个 dispatch 方法到我的 OffersGridContainer,我可以使用它来将操作分派到这个实例的存储:

The Provider injects a dispatch method for this store to my OffersGridContainer which I can use to dispatch actions just to the store of this instance:

class OffersGridContainer extends React.Component {    
  componentDidMount() {

    // UGLY???
    const { dispatch } = this.props;

    let destinationIds = [];
    this.props.offers.forEach((offer) => {
      offer.to.forEach((destination) => {
        destinationIds.push(destination.destination);
      });
    });

    // MORE UGLY???
    destinationsApi.getDestinations(dispatch, destinationIds);
  }

  render() {
    return (
      <OffersGridLayout destinations={this.props.destinations} />
    );
  }
}

const mapStateToProps = function(store) {
  return {
    destinations: store.offersGridState.destinations
  };
}

export default connect(mapStateToProps)(OffersGridContainer);

API 方法:

只需使用我作为参数传递给 API 方法的 dispatch 方法:

Just use the dispatch-method that I've passed as argument to my API method:

export function getDestinations(dispatch, ids) {
  const url = $('meta[name="site-url"]').attr('content');

  const filter = ids.map((id) => {
    return `filter[post__in][]=${id}`;
  }).join('&');

  return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
    .then(response => {
      dispatch(getOffersGridSuccess(response.data));
      return response;
    });
}

更新 2

刚刚在评论中收到一个关于 mapDispatchToProps 的提示,所以我的 Container 现在看起来像这样:

Update 2

Just received a hint about mapDispatchToProps in the comments, so my Container now looks like this:

class OffersGridContainer extends React.Component {    
  componentDidMount() {
    let destinationIds = [];

    this.props.offers.forEach((offer) => {
      offer.to.forEach((destination) => {
        destinationIds.push(destination.destination);
      });
    });

    this.props.getDestinations(destinationIds);
  }

  render() {
    return (
      <OffersGridLayout destinations={this.props.destinations} />
    );
  }
}

const mapStateToProps = function(store) {
  return {
    destinations: store.offersGridState.destinations
  };
}

const mapDispatchToProps = function(dispatch) {
  return {
    getDestinations: function(ids) {
      return destinationsApi.getDestinations(dispatch, ids);
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);

更新 3(最终答案)

现在一切正常!代码如下:

Update 3 (the final answer)

Now everything works! Here's to code:

提供商:

export default class OffersGridProvider extends React.Component {    
  constructor(props) {
    super(props)
    this.store = createStore(reducers, applyMiddleware(thunk));
  }

  render() {
    return (
      <Provider store={this.store}>
        <OffersGridContainer offers={this.props.offers} />
      </Provider>
    );
  }
}

容器:

class OffersGridContainer extends React.Component {
  componentDidMount() {
    const destinationIds = this.props.offers.reduce((acc, offer) => {
      return [...acc, ...offer.to.map(d => d.destination)];
    }, []);

    this.props.getDestinations(destinationIds);
  }

  render() {
    return (
      <OffersGridLayout destinations={this.props.destinations} />
    );
  }
}

const mapStateToProps = function(store) {
  return {
    destinations: store.offersGridState.destinations
  };
}

const mapDispatchToProps = function(dispatch) {
  return {
    getDestinations: function(ids) {
      return dispatch(destinationsApi.getDestinations(ids));
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);

API 方法:

export function getDestinations(ids) {
  return function(dispatch) {
    const url = $('meta[name="site-url"]').attr('content');

    const filter = ids.map((id) => {
      return `filter[post__in][]=${id}`;
    }).join('&');

    return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
      .then(response => {
        return dispatch(getOffersGridSuccess(response.data));
      });
  }
}

推荐答案

建议不要直接在组件中调用 api,建议使用 redux-thunk 包.

Instead of making api calls in components directly, I suggest you to use redux-thunk package.

接下来,您应该将 mapDispatchToProps 函数作为第二个参数传递给 connect 函数,以将动作创建器函数注入组件:

Next, you should pass mapDispatchToProps function as second argument to connect function, to inject action creator functions to component:

import { getDestinations } from '../actions';

class OffersGridContainer extends React.Component {    
  componentDidMount() {
    // Tip.
    const destinationIds = this.props.offers.reduce((acc, offer) => {
      return [...acc, ...offer.to.map(d => d.destination)];
    }, []);

    // instead `destinationsApi.getDestinations(dispatch, destinationIds)`
    // call action creator function
    this.props.getDestinations(destinationIds);
  }

  render() {
    return (
      <OffersGridLayout destinations={this.props.destinations} />
    );
  }
}

const mapStateToProps = function(store) {
  return {
    destinations: store.offersGridState.destinations
  };
}

const mapDispatchToProps = function(dispatch) {
  return {
    // this function will be available in component as   
    // `this.props.getDestinations`.
    getDestinations: function(destinationIds) {
      dispatch(getDestinations(destinationIds));
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);

这篇关于如何在 Redux 中为每个实例创建一个商店?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆