Ad

React-Redux - Reuseable Container/Connector

- 1 answer

I am completely lost on the react-redux container (ie connector) concept as it is not doing what I anticipated. My issue is straight forward, and to me reasonable, yet I cannot find a well written example of how to accomplish it.

Let us say we have a react component that connects to a store that has product context, and we will call this component ProductContext.

Furthermore, let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions on every other component that may need products.

Illustratively this is what I mean:

from DiscountuedProducts:

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

from SeasonalProducts:

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

From the examples I see at react-redux, it appears to me that their containers lump both seasonal and discontinued products in the container itself. How is that reusable?

from the ProductContextComponent:

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

Complicating matters, while trying to keep a cool head about this most frustrating matter, "nebulous tersity" seems to be the only responses I receive.

So here is my 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
      );
    }
 };

Then my thinking is if I do this:

from DiscountuedProducts:

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

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

Am I completely wrong on this? Is this not reasonable to desire?

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. I have spent over a week on this issue and am about ready to give up on react-redux.

UPDATE: A very slick solution below with use of a HOC.

Ad

Answer

I have found a more practical way of utilizing repetitive data from redux than the docs. It makes no sense to me to repeat mapToProps and dispatch instantiation on every blessed component when it was already done once at a higher level, and there inlies the solution. Make sure your app is Babel 6 compliant as I used decorators.

1. I created a higher order component for the context ....

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. component utilizing 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>
    );   
  } 
}

So there you go. All I needed was a decorator reference to product-context, and as a HOC, it returns the carousel component back with the products prop.

I saved myself at least 10 lines of repetitive code and I removed all related contextType from lower components as it is no longer needed with use of the decorator.

Hope this real world example helps as I detest todo examples.

Ad
source: stackoverflow.com
Ad