Hi, thanks for the docs and conventions for Higher Order Components, they are really useful. I have a question regarding a best practice that I haven't seen. As I am writing more and more HOC to handle different parts of the logic, I am wondering about the specific naming of the props while following passing the rest of the props. What should the name of the prop be?
Common sense indicates that it should follow as close as possible to the name of the library if it's a single entity:
import React from 'react';
import withAuth from './hoc/auth';
// everything concerning 'auth' is within the 'auth' prop
const Home = ({ auth }) => (
<div>
<pre>{JSON.stringify(auth.error, null, 2)}</pre>
<pre>{JSON.stringify(auth.user, null, 2)}</pre>
</div>
);
export default withAuth(...)(Home);
So my question is, is this preferred? Or would passing several props like auth and user in this example make more sense? (auth for workflow logic, user for the data):
import React from 'react';
import withAuth from './hoc/auth';
// user is very useful on its own, so we split it apart
const Home = ({ auth, user }) => (
<div>
<pre>{JSON.stringify(auth.error, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
</div>
);
export default withAuth(...)(Home);
Is it preferred to stick to a single prop, or to use them as you would normally use variables? The later is easier to use and more direct, including reduced code size and better separation of concerns.
However it has way too many side-effects and chance of collision, with an unclear separation line: what is a property of the main object and what should be separated? Would error make sense as a separated prop or only as auth? What about a token if it was there? If we prefer splitting too much then collisions are almost inevitable:
import React from 'react';
import withAuth from './hoc/auth';
// `error` is too generic and if splitting is preferred, other libs will do similar
const Home = ({ error, user }) => (
<div>
<pre>{JSON.stringify(error, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
</div>
);
export default withAuth(...)(Home);
So, is the first example preferred? Does it make sense? Since React.js is the most wanted library/framework, I expect that this will become a larger and larger issue if it's not addressed.
Note: this came initially in my thought since I have a component that I want it to pass history, but it'd conflict with react-router's .withRouter() if I wanted to publish it to a larger audience since that also passes a history prop. So this would be a counter-example, as they are doing the latter.
I just found how this was a concern from Mixins and one of the reasons to migrate to HOC, but it just seems the issue has just changed shape but remains there.
That is what the render prop pattern is intended to address.
https://reactjs.org/docs/render-props.html
(personally, I prefer using children instead of render, but I digress 🤷♀️)
@Kovensky I see, I agree with this post but it fails to acknowledge one of the issues of render props that I haven't seen solved (but it is commented here): too much nesting:
export default () => (
<Counter>
{(counterProps) => (
<Timer>
{(timerProps) => (
<YourComponent {...timerProps} {...counterProps} />
)}
</Timer>
)}
</Counter>
);
I think that the naming class of HOCs would be practically eliminated if there was a convention of only passing a single prop with the main name of the library. Compare render props to compose():
const YourComponent = ({ timer, counter }) => (
<YourComponent {...timer} {...counter} />
);
export default compose(timer, counter)(YourComponent);
Redux used to use Render Props but they migrated to HOC, though their reason is for better testability (I think), which is similar to @gaearon tweet here:
This is the original version. Removed it because makes it hard to trigger side effects in response to state changes. No lifecycle.
That’s the annoying thing. Usually the thing I want to connect is not Redux-aware so I want to colocate effects with subscription.
So it forces me to introduce an extra component layer if I want to keep the connected component unaware of Redux concerns.
It seems that HOCs are here to stay 😄
There's an idea for dealing with the nesting problem (https://gist.github.com/trueadm/35f083d32e5af93dd8fd706dae378123#file-2-future-ideas-js-L24), but I don't really see a way of getting it done without being able to turn render into an actual coroutine (generator function).
See also https://github.com/Andarist/babel-plugin-jsx-adopt/blob/master/README.md
It totally depends on your implementation, domain, and API expectations, but namespacing is probably a good idea to reduce the chance of naming collisions.
Sure, thanks @alexkrolick. From your answer it seems that it doesn't make sense in the official docs then since it's a case-by-case decision and not something to be recommended for everyone.
Most helpful comment
It totally depends on your implementation, domain, and API expectations, but namespacing is probably a good idea to reduce the chance of naming collisions.