简体   繁体   English

ReactJs - 可重用的Redux服务

[英]ReactJs - Reuseable Redux Service

I understand the concept of Redux's actions, reducers, and mapping to stores. 我理解Redux的操作,缩减器和映射到商店的概念。 I have been able to successfully execute Redux into my app. 我已经能够成功地将Redux执行到我的应用程序中。

I was going along merrily using React's contextTypes for child components that needed data from Redux that had been called before. 我正在愉快地使用React的contextTypes来处理需要以前调用过的Redux数据的子组件。

Then I ran into a strange situation where the data was mutated by a child. 然后我遇到了一个奇怪的情况,数据被一个孩子变异了。 When I posted the problem on SO, a member told me I should be using contextTypes sparingly anyway. 当我在SO上发布问题时,一位成员告诉我,无论如何我应该谨慎使用contextTypes。

So the only way to overcome my problem was map to stores, AGAIN , in the child's parent, like a higher component of the parent had done earlier, and pass that data to the child as props. 因此,克服我的问题的唯一方法是映射到子项的父项中的商店, AGAIN ,就像之前父项的更高组件,并将该数据作为道具传递给子项。

But that seems all wrong to me. 但这似乎对我来说都是错的。 Mapping to the same store again? 再次映射到同一个商店? Why? 为什么? What am I not understanding? 我不明白的是什么? Why do I have to write this on every component that needs the same data another component mapped to? 为什么我必须在需要另一个组件映射到的相同数据的每个组件上写这个?

  export default class Foo extends Component {        

  .....

  // I DID THIS STUFF IN A HIGHER COMPONENT.
  // WHY MUST I REPEAT MYSELF AGAIN?
  // WHAT AM I NOT UNDERSTANDING?

  static propTypes = {
    children: PropTypes.node.isRequired,
    dispatch: PropTypes.func.isRequired,
    products: PropTypes.array
  };

  componentDidMount() {
    const { dispatch } = this.props;  
    dispatch(fetchProductsIfNeeded());
  }

  .....

 }  

const mapStateToProps = (state) => {
  const {productsReducer } = state;
  if (!productsReducer) {
    return {
      isFetching: false,
      didInvalidate: false,
      error: null,
      products: []
    };
  }

  return {
    error: productsReducer.error,
    isFetching: productsReducer.isFetching,
    didInvalidate: productsReducer.didInvalidate,
    products: productsReducer.products
  };
};

export default connect(mapStateToProps)(Foo);

I looked at containers, but it appears to me that containers wrap all dumb components in them at once as such ... 我看了容器,但在我看来,容器一次性包裹所有哑组件......

   <ProductsContainer>
      <ProductsComponent />
      <ProductSpecialsComponent />
      <ProductsDiscountedComponent />
   </ProductsContainer>

And that is not what I want. 这不是我想要的。 I thought, like a service I could use that container in each respective dumb component as a such .... 我想,就像一项服务,我可以在每个相应的哑组件中使用该容器作为这样的....

   <ProductsContainer>
      <ProductsDiscountedComponent />
   </ProductsContainer>

   <ProductsContainer>
      <ProductSpecialsComponent />
   </ProductsContainer>

   <ProductsContainer>
      <ProductsComponent />
   </ProductsContainer>

Right now in order to get my 3 sub components illustrated above, each one of them has to map to stores and that just seems all wrong. 现在,为了获得上面说明的3个子组件,每个组件必须映射到商店,这似乎都是错误的。

I cannot find anything that I can grasp as a solution. 我找不到任何我能把握的解决方案。

Question: 题:

Is there a way I can map to a particular store just once , and call on that "service" for those components that need that data? 有没有一种方法可以映射到特定的商店一次 ,并为那些需要该数据的组件调用“服务”?

If so, examples would be appreciated. 如果是这样,我们将不胜感激。

Post Script: 后脚本:

I though perhaps if I could perform the 'mapping service' as a pure JavaScript function o/s of react, and just import that function in the components that need it, that would solve the problem, but I have not seen any examples of Redux stores being mapped o/s React. 我或许如果我可以将'映射服务'作为纯粹的JavaScript函数执行,而只是在需要它的组件中导入该函数,这将解决问题,但我还没有看到任何Redux的例子正在映射的商店o / s React。

UPDATE: 更新:

I posted the solution here ...... 我在这里发布了解决方案......

React-Redux - Reuseable Container/Connector React-Redux - 可重复使用的容器/连接器

First, an aside about your past problems. 首先,抛开你过去的问题。 It's true that context is not appropriate for something like this. 确实,上下文不适合这样的事情。 You should also be worried about the mutation you mentioned. 您还应该担心您提到的突变。 If you're using a Redux store, the data that exits it should always be immutable. 如果您正在使用Redux存储,则退出它的数据应始终是不可变的。 Perhaps a library like Immutable.js would help there. 也许像Immutable.js这样的图书馆会有所帮助。


Now let's turn to the matter at hand. 现在让我们转向手头的问题。 Perhaps what you aren't fully grokking is what a "dumb" component is. 也许你并不完全是一个“愚蠢”的组成部分。 A dumb component should be stateless and a pure: 一个愚蠢的组件应该是无国籍和纯粹的:

const Product = ({ name, comments }) => (
  <div>
    <h1>{name}</h1>
    <CommentsList comments={comments} />
  </div>
);

The component gets everything it needs from props. 该组件从道具中获取所需的一切。 Now there are a number of ways to get data into this component, but they are all based on props. 现在有很多方法可以将数据导入到这个组件中,但它们都基于props。 For example, the following is the most straightforward: 例如,以下是最直接的:

const ProductList = ({ products }) => (
  <div>
    {products.map( p => <Product product={product} /> )}
  </div>
);

class App extends Component {
  getInitialState () {
    return { products: [] };
  }

  componentDidMount () {
    // connect to store, blah blah...
  }

  render () {
    return (
      <div>
        {/* blah blah */}
        <ProductsList products={this.state.products} />
        {/* blah blah */}
      </div>
    );
  }
}

As you can see from the example, the entire components tree will get its state from props that are simple passed down from one connection to the store. 从示例中可以看出,整个组件树将从简单的道具中获取其状态,这些道具从一个连接传递到商店。 Aside from App , all components are dumb, stateless, and predictable. App之外,所有组件都是哑巴,无状态和可预测的。

But there are also cases where connecting the entire tree through props is impractical and where we need localized connections to our stores. 但也有一些情况,通过道具连接整个树是不切实际的,我们需要本地连接到我们的商店。 That's where HOCs can be hugely helpful: 这就是HOC可以提供巨大帮助的地方:

const WithProducts = Comp => class WrappedComponent extends Component {
  getInitialState () {
    return { products: [] };
  }

  componentDidMount () {
    // connect to store, blah blah...
  }

  render () {
    return (
      <Comp products={this.state.products} {...this.props} />
    );
  }
}

const ProductListWithProducts = WithProducts( ProductList );

Now any component we so wrap will receive the list of products from the store as a prop - no code duplication required. 现在,我们所包装的任何组件都将从商店收到产品列表作为支柱 - 无需代码重复。 No repeating yourself. 不要重复自己。 Notice how I did not alter the ProductList or Product components to make this work: those components are too dumb to care. 请注意我没有改变ProductListProduct组件以使其工作:这些组件太愚蠢而无法关注。

The majority of the components in any React app you create should be so dumb. 您创建的任何React应用程序中的大多数组件都应该是如此愚蠢。


As another aside, you should not be worried about calling your store more than once. 另外,您应该担心不止一次打电话给您的商店。 If you are worried about that, there's something wrong with the store implementation because calls to stores should be idempotent. 如果您对此感到担心,那么商店实施会出现问题,因为对商店的调用应该是幂等的。 You can use actions and so forth to populate the stores, but that should be wholly independent from getting values from stores. 您可以使用操作等来填充商店,但这应该完全独立于商店获取值。 There should be no performance or network penalty form well-design store retrievals (and, again, using libraries like Immutable can help here too). 设计良好的商店检索应该没有性能或网络惩罚(同样,使用像Immutable这样的库也可以在这里帮助)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM