3

I am trying to get my head around on how recompose works. Although I understand the underlying concept of it I have a problem with using withProps function. If I try using it for decorating each child of a component with additional props it simply fails to work. What I do is:

const decorateChild = withProps({ /* some additional props */ });
// ... later on
{ React.Children.map(this.props.children, decorateChild) }

And it simply doesn't work. I had to do React.cloneElement instead. Could you please help me with that. Here's a sandbox. Please, notice WrappedComponent.js line number 9

EDIT (The code I have problem with...)
so this works fine:

const decorateChild = actions => child =>
  typeof child === "function"
    ? child(actions)
    : React.cloneElement(child, actions);

however I wanted to have it written using recompose. As I said before, if instead of cloneElement I try to do withProps(action)(child) it won't work -> nothing gets rendered. If I try forcing the component to render by withProps(action)(child)() the app crashes.
In general, I wonder how entire WrappedComponent.js could be expressed using recompose. What I dislike about current implementation is how I have to manually manage ref because the underlying component that I'm using (here Wrap) requires it and this is something I cannot change (3rd party library). I would also love to handle it using recompose

I hope this little explanation makes it a bit more clear what I'm after.

SOReader
  • 5,697
  • 5
  • 31
  • 53
  • I tried messing around a bit with your sandbox. But I am not sure what exactly you want to do. Also, just to make things simpler you should put in the relevant piece of code in the body of the question as well as specify what exactly is happening and what you would like to happen. – theJuls Jul 21 '18 at 12:56
  • @theJuls - just put more details. I also rephrased one of the sentences in my original question. Hope it helped understand what I want to achieve. – SOReader Jul 21 '18 at 17:30

1 Answers1

3

withProps returns a function whose input is another Component (note that child here is the output of a component i.e. the opaque JS object that represents it), also decorateChild(action) should return the opaque data structure which explains why you need to call it as a function again

Probably the following looks worse than what you did with React.cloneElement but it works:

const decorateChild = actions => child => {
  const Component = (props) => ({ ...child, props: { ...child.props, ...props } })
  return typeof child === "function"
    ? child(actions)
    : withProps(actions)(Component)();
    // this also works
    // : withProps(actions)((props) => React.cloneElement(child, props))();
};

https://codesandbox.io/s/mjx0626wx8

Mauricio Poppe
  • 4,817
  • 1
  • 22
  • 30
  • +1 for a code that works (the latter version doesn't actually). Thanks for that. Nevertheless I still don't understand why I have to pass to `withProps` a cloned element... and should actually `renderComponent` instead work here? (spoiler: it doesn't). Could you please share a good source of info on that? – SOReader Jul 23 '18 at 08:08
  • `withProps` actually cares about a component and that's it, you need to clone the element because `withProps` will pass additional props to `child` but the props of `child` are marked as readOnly i.e. you can't do `const Component = props => { child.props = { ...child.props, ...props }; return child; };`, the only solution would be to create a clone of `child` that has the props received from `withProps` – Mauricio Poppe Jul 23 '18 at 19:49
  • Also, to find out the types of input I read [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts) and [@types/recompose](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/dcd9338caaf79d5f0214721da19056f8c00b4f19/types/recompose/index.d.ts#L70-L72) – Mauricio Poppe Jul 23 '18 at 19:58