Recompose: Suggestion: nest as a HOC (wrap?)

Created on 8 Aug 2017  路  13Comments  路  Source: acdlite/recompose

I find this HOC to be more useful than nest alone:

const wrap = (...outerComponents) => wrappedComponent => nest(...outerComponents, wrappedComponent)

const BlueOutline = ({ children }) => <div style={{ border: '3px solid blue' }}>{ children }</div>
const YellowOutline = ({ children }) => <div style={{ border: '3px solid yellow' }}>{ children }</div>
const RedOutline = ({ children }) => <div style={{ border: '3px solid red' }}>{ children }</div>

const withBlueOrRedOutline = branch(
  () => process.browser,
  wrap(BlueOutline, YellowOutline),
  wrap(RedOutline)
)(InnerComponent)

When rendered on the server, the component will be wrapped with a red border. On the client, the component will be wrapped with both a yellow and a blue border.

This is more flexible than nest alone because it can be used to wrap components within a compose pipeline, or in branch, etc.

Are there any plans of including a HOC like this?

Most helpful comment

@mattishii Oh, I see. A PR is still welcome :)

All 13 comments

How it differs from renderComponent hoc?

@istarkov renderComponent doesn't actually render the children. For example:

const Outer = props => <div>{ props.children }</div>
const Inner = () => <p>foo</p>

renderComponent(Outer)(Inner) // => results in <div></div>
wrap(Outer)(Inner) // => results in <div><p>foo</p></div>

Got you, looks cool, @wuct ?

The idea looks good to me, but I think the name should be wrapBy or wrapWith because it's the base component being wrapped, not outer components. I am not a native speaker so I am not sure which name is better.

@sbking will you provide a PR?

This is very easy to implement

export default const wrapWith = WrapperComponent => 
  BaseComponent => 
    (children, ...restProps) => (
      <WrapperComponent>
        <BaseComponent {...restProps }>
          {children}
        </BaseComponent>
      </WrapperComponent>
    );

Fiddle:
https://jsfiddle.net/69z2wepo/85362/

It will works unless you also want to offer the possibility to use a component instance as a wrapper
But that's a bit awkward because you have to override children or extract the constructor

Strongly agree with this - I was hoping to use nest to turn providers into HOCs. I'd like to do something like this:

export default compose(
  injectJss("insertion-point-jss"), 
  wrap(  
    ({children}) => <MuiThemeProvider theme={styles.theme}>{children}</MuiThemeProvider>,  
    ({children}) => <Provider store={store} >{children}</Provider>,   
    ({children}) => <ConnectedRouter history={history} >{children}</ConnectedRouter>,  
  )  
)(App);

which seems like something recompose was made for. Unless thats actually a terrible antipattern, hahaha. I'll use the existing 'wrap' function as written in my own code, but I'd like it if it was part of the recompose library

@mattishii I'm not sure what does your wrap do. It seems like it is different from the original proposal.

Nope it does the exact same thing, I used sbking's code. I actually don't see much use for nest if it can't be used as an hoc though.

@mattishii Oh, I see. A PR is still welcome :)

I had a similar idea/reaction when trying to think of ways to simplify/clean up the "nested providers around <App />" in some application code. I saw recompose's nest mentioned in a blog post, but I felt that surely the more idiomatic approach (at least for a library as recompose) would be to provide a function to transform a "provider component" into a HOC.

This is a different function than the wrap defined above, but I think it is similar in spirit, also trying to solve the same problem. What I came up with is something like this (untested, also lacking displayName etc):

const asHOC = (ProviderComp, providerProps) => Comp => props =>
  <ProviderComp {...providerProps}><Comp {...props} /></ProviderComp>;

which would (hopefully, unless I messed up) allow for the following for @mattishii's example above:

export default compose(
  injectJss("insertion-point-jss"),
  asHOC(MuiThemeProvider, { theme: styles.theme }),
  asHOC(Provider, { store }),
  asHOC(ConnectedRouter, { history }),
)(App);

I intentionally only map one component to one HOC, and rely on function composition to compose them, since I feel that's cleaner/more flexible. Also not a fan of having to specify functional components and explicitly render children, which is why I opted for "provide component + object of props" as the HOC's parameters instead.

Not saying this is better--just a different way to approach the same underlying problem. Discussion welcome! 馃槂

export const wrapWith =  WrapperComponent  =>
   BaseComponent  =>
     props => (
      <WrapperComponent {...props }>
        <BaseComponent {...props }/>
      </WrapperComponent> 

I'm going to close this ticket since it's stale. Please feel free to reopen it if you have any further thought.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

franklinkim picture franklinkim  路  3Comments

adrianmcli picture adrianmcli  路  3Comments

xialvjun picture xialvjun  路  4Comments

istarkov picture istarkov  路  3Comments

gajus picture gajus  路  4Comments