i just integrated flow for the first time to check my javascript sources statically.
I am struggling with a error flow finds and i am not able to solve it on my own. Its about using es6 classes and inheritance. More specific i created some react Components and they should inherit some methods.
I have a Callout Component, that represents a callout message of unspecified severity. To make things a little more simple i thought about providing a ErrorMessage Component, that inherits the Callout Component. My classes Structure looks like:
React.Component
> AbstractComponent (here i add some project-wide helpers for i18n and so on
> Callout (this represents a pretty message on the screen)
> ErrorMessage (this represents an error)
Flow tells me:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/js/Components/Generic/ErrorMessage.js:14:43
statics of Callout [1] is not a polymorphic type.
11│ icon: string
12│ };
13│
[1] 14│ export default class ErrorMessage extends Callout<Props> {
15│
16│ static get defaultProps(): Props {
17│ return {
The part Callout<Props>
gets highlighted
I already define the Props Type for the Callout class, so this might be the problem but i cant solve it on my own.
A similar error is thrown a few lines below, where i try to access a parent method by addressing super.content (content is a get-method of Callout).
Thanks in advance
UPDATE: Why do i want to use class inheritance?
The inheritance Callout > ErrorMessage just exists to reduce redundant code, but its not necessary, so lets ignore this and talk about a more mon case:
I want to have a class AbstractComponent
to make mon things in my project easier.
Some examples:
Printing of translation strings: In order to make the ponent multilingual, i created a utility class to generate translation strings, inside a ponent it works like
function render() {
return (
<div>
{new Translation(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
).toString()}
</div>
)
}
In order to use this, every ponent in my stack ends up with the import statement on top
import Translation from "../Core/Translation"
or in the best case
import t from "../Core/Translation"
I use webpack to build a bundle and webpack seems to blow up the piled javascript with every import statement you use. So i figured - to reduce coding effort and bundle size - i provide a intermediate ponent class, that adds some utility methods like:
class AbstractComponent extends React.Component {
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api: ApiInterface = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
I also added some other things to show you more reason for a intermediate Component class.
So, all of this works! But flow plains about missing return types and so on, thats good with me, for that purpose i want to use flow! The problem i cant solve is the inheritance itself... But for me it does make a lot of sense.
i just integrated flow for the first time to check my javascript sources statically.
I am struggling with a error flow finds and i am not able to solve it on my own. Its about using es6 classes and inheritance. More specific i created some react Components and they should inherit some methods.
I have a Callout Component, that represents a callout message of unspecified severity. To make things a little more simple i thought about providing a ErrorMessage Component, that inherits the Callout Component. My classes Structure looks like:
React.Component
> AbstractComponent (here i add some project-wide helpers for i18n and so on
> Callout (this represents a pretty message on the screen)
> ErrorMessage (this represents an error)
Flow tells me:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/js/Components/Generic/ErrorMessage.js:14:43
statics of Callout [1] is not a polymorphic type.
11│ icon: string
12│ };
13│
[1] 14│ export default class ErrorMessage extends Callout<Props> {
15│
16│ static get defaultProps(): Props {
17│ return {
The part Callout<Props>
gets highlighted
I already define the Props Type for the Callout class, so this might be the problem but i cant solve it on my own.
A similar error is thrown a few lines below, where i try to access a parent method by addressing super.content (content is a get-method of Callout).
Thanks in advance
UPDATE: Why do i want to use class inheritance?
The inheritance Callout > ErrorMessage just exists to reduce redundant code, but its not necessary, so lets ignore this and talk about a more mon case:
I want to have a class AbstractComponent
to make mon things in my project easier.
Some examples:
Printing of translation strings: In order to make the ponent multilingual, i created a utility class to generate translation strings, inside a ponent it works like
function render() {
return (
<div>
{new Translation(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
).toString()}
</div>
)
}
In order to use this, every ponent in my stack ends up with the import statement on top
import Translation from "../Core/Translation"
or in the best case
import t from "../Core/Translation"
I use webpack to build a bundle and webpack seems to blow up the piled javascript with every import statement you use. So i figured - to reduce coding effort and bundle size - i provide a intermediate ponent class, that adds some utility methods like:
class AbstractComponent extends React.Component {
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api: ApiInterface = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
I also added some other things to show you more reason for a intermediate Component class.
So, all of this works! But flow plains about missing return types and so on, thats good with me, for that purpose i want to use flow! The problem i cant solve is the inheritance itself... But for me it does make a lot of sense.
Share Improve this question edited Mar 23, 2018 at 7:56 Philipp Wrann asked Mar 22, 2018 at 15:22 Philipp WrannPhilipp Wrann 1,8493 gold badges21 silver badges31 bronze badges 3- 3 umm is there any solid reason to use inheritance? React team strongly discourage it. – Tomasz Mularczyk Commented Mar 22, 2018 at 16:58
- The Callout > ErrorMessage inheritance not but i would like to keep the AbstractComponent. Why? One simple example: I wrote a translation utility to output translation strings, inside a react ponent it works like {new Translation(namespace, id, args, fallback).toString()} In order to use this i need to import the Translation class, so every pnent ends up with: import Translation from "../Translation"; So i decided it would be useful to have a translate method inside of every react ponent i code, so i can simply write: this.translate(args...) – Philipp Wrann Commented Mar 22, 2018 at 19:13
- So you tell me: Adopt your code because the react team thinks inheritance is not cool enough, they dont need it, i dont have to need it, know what? Facebook sucks. – Philipp Wrann Commented Mar 26, 2018 at 7:16
1 Answer
Reset to default 3If you really want to deal with inheritance (which I don't have an issue with, I just feel like you will probably run into issues later), you can do something like the following:
class AbstractComponent<Props: {}, State: ?{} = null> extends React.Component<Props, State> {
api: ApiInterface
logger: typeof Logger
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
And use it like:
class Test extends AbstractComponent<{ some: string, args: string }> {
render() {
const { some, args } = this.props
return (
<div>
{this.translate(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
)}
</div>
)
}
}
Now, I will say that to some extent I understand where Facebook is ing from. Your ponent in this case is really already an abstract construct. And if you want this to be more flexible (let's say you have a stateless ponent that could benefit from having a logger
and a translate
function), you could do one of two things:
This is the defined type and translate function I'm using in both:
type CommonProps = {
logger?: Logger,
api?: ApiInterface,
translate?: (namespace: string, key: string, args: ?string[], fallback: ?string) => string
}
// This should look familiar
function translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
Higher order ponent
function addCommonStuff({ logger = new Logger(), api = new MockApi(), translate = translate }: CommonProps) {
return <Props: {}>(
WrappedComponent: ComponentType<Props>
): ComponentType<
$Diff<Props, $NonMaybeType<CommonProps>>
> => (props: Props) => <WrappedComponent {...props} logger={logger} api={api} translate={translate} />
}
And used like:
class Test extends React.Component<{}> {}
const TestWithCommons = addCommonStuff({})(Test)
;<TestWithCommons />
Reusable ponent with a render prop
class Common extends React.Component<CommonProps & { render?: Function, children?: Function }, $NonMaybeType<CommonProps>> {
state = {
logger: this.props.logger || new Logger(),
api: this.props.api || new MockApi(),
translate: translate
}
render() {
const { children, render } = this.props
return typeof render === 'function' ? render(this.state) : (
typeof children === 'function' ? children(this.state) : null
)
}
}
And use it like this:
class TestCommon extends React.Component<{}> {
render() {
return <Common>
{({ logger, api, translate }) => translate('namespace',
'key',
null,
`Fallback message`
)}
</Common>
}
}
As an aside to this discussion, you don't need to write defaultProps
as a getter for your callout. static defaultProps = {}
should be enough. It shouldn't take passed in props into account or anything. If it does, you're better off using state
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745624732a4636739.html
评论列表(0条)