redux-connect icon indicating copy to clipboard operation
redux-connect copied to clipboard

HOC with wrapped component with asyncConnect

Open ronar opened this issue 7 years ago • 11 comments

Hi! Is it possible to make HOC which wraps asyncConnect'ed component. I can't do this. Because all asyncItems, mapStateToProps and mapDispatchToProps of the Component seem to be dropped.

Say, I've got the Component:

...
const mapStateToProps = state => ({
    ...
});

const asyncItems = [{
    key: 'myItems',
    promise: ({router, store: { dispatch, getState }}) => {}
}];
const enhancer = createHOC();

module.exports = enhancer(asyncConnect(
    asyncItems,
    mapStateToProps,
    mapDispatchToProps,
)(Component));

And HOC itself has the asyncConnect enhancer :) Is it possible or do I have to abandon this idea at all?

ronar avatar Jun 12 '17 18:06 ronar

move static over to the enhancer in HOC, that's really the only thing you can do here to get this to work. Generally asyncConnect must be the top HOC

AVVS avatar Jun 16 '17 20:06 AVVS

Okay, it make sense. And how to correctly implement HOC, make a factory which receives asyncItems, mapStateToProps and dispatchProps and returns parametrised enhancer with asyncConnect?

const enhancerFactory = (asyncItems = [], mapStateToProps, dispatchProps) => {
    ...

    function wrapComponent(wrappedComponent) {
        ...
        const enhancer = compose(
            asyncConnect(asyncItems, finalMapStateToProps, dispatchProps)
        );

        return enhancer(hoistNonReactStatic(HOCComponent, WrappedComponent));
    }

    return wrapComponent;
}

ronar avatar Jun 17 '17 13:06 ronar

Had a issue with loadable-components which also needed to be a direct decender of the Route, but then I could use the getComponent in react-router v3 to do the loading, and then send the redux-connect HOC to react-router

https://github.com/smooth-code/loadable-components/issues/15

gerhardsletten avatar Jan 05 '18 12:01 gerhardsletten

@gerhardsletten Any chance you've also been able to get redux-connect and loadable-components to work with react-router v4? Right now I can't see how since async-connect and loadable-components both seem to need to be the top level HOC.

I wouldn't mind trying my hand a pull request to add features to make this possible in redux-connect, but I'd feel better in talking about it with somebody first to plan out what needs to be done. Maybe @AVVS could help provide some direction for me (thanks for this component by the way - it's been very handy)?

I think #118 is related to this, too.

internetErik avatar Feb 05 '18 01:02 internetErik

@internetErik No, have not had the time to upgrade for RR4 after redux-connect supports it. But did you see my updated comment here about the need for a static componentId property on the component to ensure that loadable-components can register it?

Also take a look at this article by the airbnb about adding a function for the component in the config. You should then be able to itterate matched routes, and prepare them before you give them to redux-connects loadOnServer function.

Maybe there should be made an example for this. Async loading both data and components is what you expect of an application today

gerhardsletten avatar Feb 05 '18 09:02 gerhardsletten

@gerhardsletten Thanks for the info! I think I may be able to figure something out from that. (The link to the airbnb article seems to be broken btw.)

internetErik avatar Feb 07 '18 01:02 internetErik

@internetErik updated the link now in the comment below!

gerhardsletten avatar Feb 07 '18 08:02 gerhardsletten

After upgrading to RR v4 and the latest version of redux-connect, I think that the solution for both data-fetching and code-splitting is about organizing you code. The route-level component can't both be HOC'ed with redex-connect and be lazy-loaded, so you should do data-fetching in the route-level component and move lazy-loading into a child-component. Can't think of any other way to do this, since you don't have a getComponent function with react-router-config, without writing you own helper that both lazy-load and do data-fetching. Maybe this should be mentions in the README.md to save people from spending time looking into this?

gerhardsletten avatar Jul 18 '18 22:07 gerhardsletten

@gerhardsletten Do you have any example of this you could share by any chance? It would be helpful to me to see some of what you're doing in particular.

internetErik avatar Aug 16 '18 12:08 internetErik

Hi, this is an example of a controller. You just move the visuals and heavy dependencies into its own component, and just hot-load it from the controller, but keep the asyncConnect hoc on the controller so redux-connect will discover it through react-router-config

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { asyncConnect } from 'redux-connect'
import loadable from 'loadable-components'

import { isLoaded as isPageLoaded, load as loadPage } from 'redux/modules/page'

const Page = loadable(() =>
  import(/* webpackChunkName: "page" */'./Page')
)

@asyncConnect([
  {
    promise: ({ store: { dispatch, getState }, location: { pathname } }) => {
      const promises = []
      if (!isPageLoaded(getState(), pathname)) {
        promises.push(dispatch(loadPage(pathname)))
      }
      return Promise.all(promises)
    }
  }
])
@connect(state => ({
  page: state.page.data,
  error: state.page.error
}))
export default class PageContainer extends Component {
  render() {
    return <Page {...this.props} />
  }
}

gerhardsletten avatar Aug 16 '18 13:08 gerhardsletten

@gerhardsletten Thanks, this is really helpful to see!

internetErik avatar Aug 16 '18 14:08 internetErik