React-Redux - 可重用 Container/Connector

React-Redux - Reuseable Container/Connector

我完全迷失了 react-redux 容器(即连接器)的概念,因为它没有按照我的预期进行。我的问题很简单,对我来说很合理,但我找不到一个很好的例子来说明如何完成它。

假设我们有一个连接到具有产品上下文的商店的 React 组件,我们将调用此组件 ProductContext

此外,假设我们想在整个应用程序中自由地重用 ProductContext,以避免在可能需要产品的每个其他组件上分派操作的样板代码。

这就是我的意思:

来自 DiscountuedProducts:

<ProductContext >
   // do something with props from container
</ProductContext >

来自季节性产品:

<ProductContext >
   // do something with props from container
</ProductContext >

从我在 react-redux 上看到的示例来看,在我看来,他们的容器将季节性产品和停产产品都集中在容器本身中。那是如何重复使用的?

来自 ProductContextComponent:

<section >
  <DiscontinuedProducts />
  <SeasonalProducts />
</section >

复杂的事情,虽然试图对这件最令人沮丧的事情保持冷静,"nebulous tersity" 似乎是我收到的唯一回复。

这是我的 ProductContext:

@connect(state => ({
   products: state.productsReducer.products
}))
export default class ProductContext extends Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const { dispatch } = this.props;
    const clientId = this.context.clientInfo._id;
    dispatch(fetchProductsIfNeeded(clientId));
  }

 // get from parent
 static  contextTypes = {
    clientInfo: PropTypes.object.isRequired
  };

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


  render() {
    if (!this.props.products) return <Plugins.Loading />;
    .....
    return (
         // I want to put the products array here
      );
    }
 };

那么我的想法是如果我这样做:

来自 DiscountuedProducts:

<ProductContext >
   // do something with props from container
</ProductContext >

DiscontinuedProducts 应该了解这些产品,它可以简单地筛选出已停产的产品。

我是不是完全错了?这不合情理吗?

如果有人知道网络上的 详尽 示例演示如何实现这一点,我将不胜感激向我指出。我在这个问题上花了一个多星期,准备放弃 react-redux。

更新: 下面是一个使用 HOC 的非常巧妙的解决方案。

If anyone knows of a exhaustive example on the Net demonstrating how this can be achieved, I would much appreciate it being pointed out to me.

看看Shopping Cart example

示例代码来自:shopping-cart/containers/CartContainer.js

Let us say we have a react component that connects to a store that has product context,

产品没有商店,用户没有商店,等等。有一个商店 一切。您应该使用 reducers 来获取完整的存储并减少到您的组件所需的内容。

来自示例:

import { getTotal, getCartProducts } from '../reducers'

const mapStateToProps = (state) => {
  return {
    products: getCartProducts(state),
    total: getTotal(state)
  }
}

这里的状态是整个商店。

let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions

组件不分派操作,它们调用传递给它们的函数。这些函数存在于一个动作模块中,您可以将其导入并作为道具传递给容器。反过来,容器将这些功能传递给组件道具。

来自示例:

import { checkout } from '../actions'

CartContainer.propTypes = {
  checkout: PropTypes.func.isRequired
}

connect(mapStateToProps,
  { checkout }
)(CartContainer)

connect 做了什么,它订阅了 store 的变化,调用了 map 函数,与常量 props(这里是 action 函数)合并,并将新的 props 分配给容器。

DiscontinuedProducts should have knowledge of those products and it can simply filter for what is discontinued

这实际上是正确的。你说的knowledgeone store,绝对应该在reducer中过滤。

希望这能解决问题。

我发现了一种比文档更实用的方法来利用 redux 中的重复数据。当它已经在 更高级别 上完成一次时,在每个受祝福的组件上重复 mapToProps 和分派实例化对我来说是没有意义的,这就是解决方案。确保您的应用符合 Babel 6,因为我使用了装饰器。

1.我为上下文创建了一个高阶组件....

product-context.js:

   import React, { Component, PropTypes } from 'react';
   // redux
   import { connect } from 'react-redux';
   import { fetchProductsIfNeeded }  from '../../redux/actions/products-actions';

    // productsReducer is already in state from index.js w/configureStore
    @connect(state => ({
      products: state.productsReducer.products
    }))
   export default function ProductContext(Comp) {
    return (
      class extends Component {
        static propTypes = {
          children: PropTypes.node,
          dispatch: PropTypes.func.isRequired,
          products: PropTypes.array
        };

        static contextTypes = {
         clientInfo: PropTypes.object.isRequired;
        };

        componentDidMount() {
          const { dispatch } = this.props;
          const clientId = this.context.clientInfo._id;
          dispatch(fetchProductsIfNeeded(clientId));
        }

        render() {
          if (!this.props.products) return (<div>Loading products ..</div>);
          return (
            <Comp products={ this.props.products }>
              { this.props.children }
            </Comp>
          )
        }
      }
    )
  }

2。组件利用 product-context.js

carousel-slider.js:

import React, { Component, PropTypes } from 'react';
......
import ProductContext from '../../../context/product-context';

@Radium
@ProductContext 
export default class CarouselSlider extends Component {

  constructor(props) {
    super(props);   }

  ......

  static showSlideShow(carouselSlides) {
    carouselSlides.map((slide, index) => {
       ......          
     results.push (
       ......
      )
    });
    return results;   
  }

  render() {
    const carouselSlides = this.props.products;
    const results = CarouselSlider.showSlideShow(carouselSlides);
    return (
      <div id="Carousel" className="animation" ref="Carousel">
        { results }
      </div>
    );   
  } 
}

好了。我所需要的只是对 product-context 的装饰器引用,并且作为 HOC,它 returns 轮播组件返回 products 属性。

我为自己节省了至少 10 行重复代码,并且我从较低的组件中删除了所有相关的 contextType,因为使用装饰器不再需要它。

希望这个现实世界的例子对我有帮助,因为我讨厌 todo 例子。