Wout Mertens
2 min readOct 3, 2017

--

I really dislike renderProps.

  1. children are “not meant” to be a function
  2. If you have to re-run the renderProp due to some new incoming data, there is no clean way to tell the not-HOC to re-render.
  3. They encourage creating arrow functions in render(), which uses needless resources
  4. They do not show up in the React Devtools, so you have no idea what code created a certain component.
  5. They encourage mixing “DOM-level” code with “app-level” code.

Instead, I vastly prefer “Component + componentProps”, which I unfortunately don’t have a snazzy name for.

To whit:

const MyMug = ({beverage}) => <div>Drinking an {beverage}.</div>class Caffeinate extends Component {
propTypes = { Mug: PropTypes.func.isRequired };
state = { coffee: "Americano" }; render() {
const {Mug} = this.props
return <Mug beverage={this.state.coffee)} />;
}
}
render(
<Caffeinate Mug={MyMug} />,
document.querySelector("#root")
);
//=> Drinking an Americano.

There, nice and clean. MyMug is just a React component that you can find in the tree, no weird things are happening, no arrow functions created in render(), no problems.

Now, what if you need to customize your verb? That’s where the “componentProps” part comes in:

const MyMug = ({beverage, verb = "Drinking"}) => <div>{verb} an {beverage}.</div>class Caffeinate extends Component {
propTypes = {
Mug: PropTypes.func.isRequired,
mugProps: PropTypes.func.isRequired,
};
state = { coffee: "Americano" }; render() {
const {Mug, mugProps} = this.props
return <Mug {...mugProps} beverage={this.state.coffee)} />;
}
}
render(
<Caffeinate Mug={MyMug} mugProps={{verb: "Gulping"}}/>,
document.querySelector("#root")
);
//=> Gulping an Americano.

This is exactly as flexible as renderProps, but it plays nice with React. You can get MyMug to re-render by changing mugProps.

The render() for ScrollWatch would look like this:

render() {
const { Cmp, cmpProps } = this.props;
const { x, y } = this.state;
return <Cmp {...cmpProps} x={x} y={y} />
}

--

--