I have been trying to get Flowtype to work with a couple React HoC's I have without success. Below is the most basic attempt at getting types to work for the wrapped component.
I have looked at several issues/sites without understanding how to get this to work. Maybe it isn't possible since it is a generic HoC that is not aware of what Props will be passed into it. Most of the examples I have found include passing specific props down the HoC.
Few of the issues I have found:
https://flowtype.org/docs/react.html#higher-order-components
https://github.com/facebook/flow/pull/1821
https://github.com/facebook/flow/issues/1523
/* @flow */
import React from 'react';
import shallowCompare from 'react/lib/shallowCompare';
// https://facebook.github.io/react/docs/shallow-compare.html
const pure = (Component: ReactClass<*>) =>
class extends React.Component<*, *, *> {
shouldComponentUpdate(nextProps: *, nextState: *) {
return shallowCompare(this, nextProps, nextState);
}
render() {
return <Component {...this.props} />;
}
};
export default pure;
https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAtgBzgJwBcwAlAUwEMBjYqfOLMAcn0pqYG5NcDiBnABYUY8BAGEGOCqzB0GzVtULAYGAEbBBw0RNzSyndMGBgBhQjj4AuY1Gpk1cOAGsAdAHMMhAQFc1rjDhgRRpgABM4Kj5NIRFEAFoqSX1XMywYVCSAOz5iHB8ZAF4wAApdPCyyLMIrUjZCMRgKPj4AHhAAPgBKMEKO1DAwKiaWsDIAD0IqsL46pVdyuErq9oAaZHXOsABvAcGwQTgfGDDF5cIAVRwwiimSysmABXpLWpB1h8IAZUJbsjeert9vtWIQClkDrEdMlWCVvBg+B8JoRnnBLEjJj8-l0uMCAL7oYGsLJhMj4EqAvZEshg-AQ1pnKrEbauVnwviuHAvPh4sDADq4-YEwZ4rioQgATxwZDAXwYZEZ1VRlgAKlKZcUgQd5crarl8Bgsu4uKL0NlcrL5YriMUSts+DqXnjanKsArJEsmcq+GrpT0+qU9q0whgAG79YH2x1o4VgVrhMP9HFmpYWgDq+AoOGlpw9516YHysNd7p452TmVTxEI+AlAEkbaV-R1A4Ng4nUABIVol63at3KwrbGs+Mi8-ld1oZrM5vsOgcvIcjsd8iNxhPh1A4oA
Do not use ReactClass, it happens to throw away type checking https://github.com/facebook/flow/issues/2159
type FunctionComponent<P> = (props: P) => ?React$Element<any>;
type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
function pure<P, S>(Component: ClassComponent<void, P, S> | FunctionComponent<P>): ClassComponent<void, P, void> {
return class extends React.Component {
shouldComponentUpdate(nextProps: P) {
return shallowCompare(this, nextProps)
}
render() {
return <Component {...this.props} />
}
}
}
Another example here https://gist.github.com/gcanti/ec95432dc76afc1b457c1b4a44396c09
@gcanti Thank you for the (continued) help! I have slightly modified what you put above to allow for context. I am unsure if context will work for a ClassComponent
/* @flow */
import React from 'react';
import shallowCompare from 'react/lib/shallowCompare';
type FunctionComponent<P, S> = (props: P, context: S) => ?React$Element<any>;
type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
function pure<P, S>(Component: ClassComponent<void, P, S>|FunctionComponent<P, S>): ClassComponent<void, P, void> {
return class extends React.Component {
shouldComponentUpdate(nextProps: P) {
return shallowCompare(this, nextProps)
}
render() {
return <Component {...this.props} />;
}
};
}
export default pure;
FYI for anyone in the future, this doesn't work for me 50% of the time. If I find a solution I will expand further.
@chrisblossom any luck with this?
I've seen this problem pop up in a few places (SO questions, other libraries).
hoping there's a straightforward way to use flow-typed components with arbitrary HOCs
I have not had any luck. Please share if you find an answer.
This seems to be working for me, if it's helpful: https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAtgBzgJwBcwAlAUwEMBjYqfOLMAcn0pqYG50yAPPIsFACuAOxoY4IsDiGtyIgCZl8AHgAKALjABvAL4AaMAGUtegHwAKVGDAB1fBRw4yCgMIM8IsiMJbXMCgBnQJVyakIAOndcSW9CFQoRAE9DNUMjMzN9VABKPwDg0LZI6M84lQA3OAwFVPTMnWswVkJZKSoCwLA1WTJ5JVYFMF5CbwUusJooj1ifSura7vrGmxscehxArTUuVbBAwgpRrSNd1cCACzghGDcZrx8AVRwFI7ILLx5CNQ2tpbBPoQjIdjsY8mAAEZwOAwShSbRNACQNmAwEEBDAGBgMCEBwchAwFTI0lkeECZC6khgSSaeygGIsVEkBzAAGsyElMVJAT84Jscis9qsMFAwB8RrzNgBtdlJAC6YAAhABeZVgQgXDCBCLrPmBGUcuUChFCoUtNrq-BCMhnU26Wmre2m+n4MVMkQs2VcgEjYFvY0OvYisWAv2jA3ypWq9Wa7UHN4Ro2C002c34KSEK02wOOnNOs1kVrpwQUGDk21gfOpsbKCwB01pqQqeyOZx3GIPYjaCI9jVanW-XRgYBmCv53Rce2oQhJZxgACySUlXTVJvRcC0eIwIgA5tkbBCKPgtCIhFgIcpshPp7PiYuw8TV00AI5CHhaKEwuFXrioDpBLpF1KWZiBGMYJmKaYO3KKoakMRdl3gpIHzMQVdU2LQEN+M541Be8QWzatFFrcFJkiABRWEsHKRIklQtdqyLKQrFNFQFEJMwcxsbQ+21dDtXpOAqyFHjYwHPUIkPfBhL2UT+34ySKAALxRcBlHoV0UErLidF4iJcLICJXx4GTVjkuMCKMoQ32HNT8A05A0VMlRgHYipOL2HIzntKd3RZBDemAzswDVGQ5BrfALCA+44i81AVAC1ggriddlSYAAxaEmEhI9lW0AAWAAmIcRy4BKl0CmKfFSjKspy-A0qK7LSuHNF1IIeLErIZLqsEtLMrgZrRxTVZUWGeyOvKnokqq4gKDygBGErhpGoUxva-B0CAA
@jamesgpearce I haven't looked too closely, but it would need to handle stateless functional components as well, and often that's where the trouble lies--handling both SFC and ES6 components.
How would one properly add types to a HOC dealing with components that have defaultProps, state, and props. Where the HOC may be having its own state, adding additional props too. For example, how would fancyComponent in the link below be properly typed
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAtgBzgJwBcwAlAUwEMBjQgGjAG8wBhOXOAOzI+IF8wo+NmADk+SjREBudKigBXDjQycBFJQE9W7LjwAUAdXwUcOMgBNteXYQCUjVGDDjC8-BzBUYFAM4+wZAAehNzm-lac3MQMjk6enD6E+PI0BHo4Qjg+9jFxcT7yZvjpmdkyAJBOhAAWGD4AdIkUIWAAvIxgHPJYAFxgAKxgvBW8sU7iHOZkxTljcS5uHgA8RiZmlmzWUYz1uzV19RlwWfxdWK0M+w1NIfVn-MAAfDJ5o07DqKPohBpmYAAKpTaDiciXwfTBGA4AHNaLEzn0zgAjaafGSoH5-ADKhGaZGBuTASLgcBgfWJpMoHDR31++IAImQoBR5DBCIDjv52oSEZ1uij8DTUF5fP4ALJaTaRHgBYKhcJSmxLRnM1ns0r0DlZeg4vGPEFgI5ZPpanwvMA3Mh9XUhc03DBUMBTVVs00E1DlXkARliHycVASSRShDSRuyBtBhWmJU5tnNVVq11xLW5RJJZMNpTu3TarXaXqGvtiEymMwjzjIrncYD0cycS3MGAAbo863FLonDlmwW88u2rl3OdmsL2+4wB5b6hSYKO4ktgI2W3M40WvkA
The initial solution presented by @gcanti didn't work for us in https://flow.org/try anymore. Because it still works when manually using React.createElement(…) we were somewhat confused so we investigated the reason for this.
It seems that there is just “no reasonable signature for [the JSX transformation]”, so it can't really work. This is from the compiler's source: https://github.com/facebook/flow/blob/40aadb48fb4809caa12401693aa7c1c838c97919/src/typing/type_normalizer.ml#L252
TypeScript types this with a lot of overloading: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L139
In the end, we went for a more simple approach using a thunk because it fit our use-case: const Hoc = makeHoc((propsFromHoc) => <MyComponent {...propsFromHoc} />). This felt better because it defines a clear interface (a function taking props, basically a subset of a stateless component) instead of delegating to createElement, which we don't really control and which has to deal with the many different kinds of React components (stateful, stateless, with/without context, …).
Most helpful comment
Do not use
ReactClass, it happens to throw away type checking https://github.com/facebook/flow/issues/2159Another example here https://gist.github.com/gcanti/ec95432dc76afc1b457c1b4a44396c09