Skip to content
This repository has been archived by the owner on Sep 10, 2022. It is now read-only.

Is there a more efficient way to namespace the result of a series of HoCs than collecting parent props then spreading them back in? #358

Closed
cheapsteak opened this issue Apr 12, 2017 · 8 comments

Comments

@cheapsteak
Copy link

cheapsteak commented Apr 12, 2017

Is there a more efficient way to namespace a series of HoCs than this? (credits to my friend @Jephuff for the below solution)

const namespace = (ns, ...hocs) => compose(
  Wrapped => (props) => Wrapped({ parentProps: props }),
  ...hocs,
  Wrapped => ({ parentProps, ...props }) => Wrapped({ ...parentProps, [ns]: props })
);

// example usage:
compose(
  withProps({a: 10, b: 11, c: 12}),
  namespace(
    'namespace',
    withProps({a: 1, b: 2, c: 3}),
  ),
)((props) => <pre>{JSON.stringify(props, null, '  ')}</pre>)

renders

{
  "a": 10,
  "b": 11,
  "c": 12,
  "namespace": {
    "a": 1,
    "b": 2,
    "c": 3
  }
}

Here's a pen: http://codepen.io/cheapsteak/pen/OmJWKZ?editors=0010

@cheapsteak cheapsteak changed the title Is there a more efficient way to namespace the result of a series of HoCs than collecting parent props then spreading them back? Is there a more efficient way to namespace the result of a series of HoCs than collecting parent props then spreading them back in? Apr 12, 2017
@istarkov
Copy link
Contributor

Not sure it's more efficient but IMO more readable:

const namespace =  (ns, ...hocs) => compose(
  withProps(props => ({ $parentProps: props })), // TODO $parentProps as symbol
  ...hocs,
  mapProps(props => ({ [ns]: props, ...props.$parentProps }))
)

@istarkov
Copy link
Contributor

Also nice idea, will try in my projects. cc @wuct

@wuct
Copy link
Contributor

wuct commented Apr 13, 2017

I can't come up with other solution. Using Symbol looks good to me.

@Isaddo
Copy link

Isaddo commented Apr 24, 2017

Not sure if a module I wrote was helpful. Just put it to a repo compose-with-scope(feedback wanted!!)

Use it to rewrite the @cheapsteak's example will like below

compose(
  withProps({a: 10, b: 11, c: 12}),
  composeWithScope(
    withProps({a: 1, b: 2, c: 3}),
    passProps(props => ({ namespace: props })),
  ),
)((props) => <pre>{JSON.stringify(props, null, '  ')}</pre>)

Using composeWtihScope with consumeProps, injectProps, passProps and passHandlers can manage the inner props and outer props of a compose scope and avoid prop name collision.

  • Only specified outer props will pass into the scope.
    (Specify by consumProps or injectProps)
  • Only specified inner props will send to the base component.
    (Specify by passProps or passHandlers)
  • All outer, not consumed props will skip the scope and pass through to the base component.
  • Injected outer props will still send to the base component.
const consumeRoute = composeWithScope(
 consumeProps({
   location: propTypes.object,
   match: propTypes.object,
   history: propTypes.object,
 }),
 passProps(({ location, match }) => ({
   path: location.pathname,
   view: match.params.view,
   id: match.params.id,
 })),
 passHandlers({
   redirectToMenu: ({ history }) => () => history.push('/menu'),
 }),
)
  • consumeProps receives the same paramater as recompose/setPropTypes
  • injectProps receives the same paramater as recompose/setPropTypes
  • passProps receives the same paramater as recompose/withProps
  • passHandlers receives the same paramater as recompose/withHandlers

@cdomigan
Copy link

Problem with these solutions is that nesting namespace hocs breaks it, as they all depend on the same parentProps key. Using a symbol would work, except some hocs will not copy over symbols depending on how they pass props, so there is no guarantee your symbol will be accessible out the other side :(

@cdomigan
Copy link

A way to make nesting work is to also pass a $parentPropsNestLevel prop that you increment and decrement at either end, and then your $parentProps becomes: $parentProps2, where 2 is the nesting level

@cdomigan
Copy link

My final solution has a __scopes array that I simply push and pop to and from 👍

@Jephuff
Copy link

Jephuff commented Aug 30, 2017

The same parentProps key shouldn't be an issue unless you need to access it
example: https://codesandbox.io/s/6xjxqwowoz

you will end up with

{
  parentProps: {
      parentProps: {
        // the parent props.
      }
  }
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants