-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React migration #1914
React migration #1914
Conversation
this.childContainer = this.node; | ||
} | ||
} | ||
ReactDOM.render(child, this.childContainer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://reactjs.org/docs/react-dom.html#unmountcomponentatnode - it should be called on dispose at least, otherwise react components will leak
@@ -0,0 +1,192 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you remove status-bar.old.ts
that we are capable to see real changes in this file?
this.onRender.dispose(); | ||
} | ||
|
||
protected render(): ReactElement<any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about making ReactWidget
abstract and replace it with abstract protected render(): MaybeArray<ReactElement<any>>;
} | ||
} | ||
ReactDOM.render(child, this.childContainer); | ||
this.onRender.dispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should go to the callback:
ReactDOM.render(child, this.childContainer, () => this.onRender.dispose());
import { ReactElement } from "react"; | ||
|
||
@injectable() | ||
export class ReactWidget extends BaseWidget { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a deprecation note to VirtualWidget
and put that ReactWidget
should be used instead.
import * as React from "react"; | ||
import { StatusBarElements } from './status-bar-elements'; | ||
|
||
export interface StatusBarLayoutData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please rebase, it seems to contain code which was removed on master
return <StatusBarView leftElements={leftElements} rightElements={rightElements} />; | ||
} | ||
|
||
protected createAttributes(entry: StatusBarEntry): StatusBarEntryAttributes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it will produce a new function each time. It would be interesting to try to add https://github.com/palantir/tslint-react, it has useful rules as jsx-no-bind
, jsx-no-lamba
to catch such issues in jsx.
It seems that StatusBarEntryAttributes
is superflous and createAttributes
can be inlined to renderElement
as jsx:
return <div
className={entry.command ? 'element hasCommand' : 'element'}
title={entry.tooltip}
onClick={() => {...}}
/>
after that rules should be able to catch bogus onClick={() => {...}}
const leftElements: React.ReactElement<StatusBarElements> = <StatusBarElements key="statusbar-left-elements" alignment="left" entries={leftEntries} />; | ||
const rightElements: React.ReactElement<StatusBarElements> = <StatusBarElements key="statusbar-right-elements" alignment="right" entries={rightEntries} />; | ||
|
||
return <StatusBarView leftElements={leftElements} rightElements={rightElements} />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should allow an array, as well, a return type should be MaybePromise<ReactElement<any>>
in the superclass, after that StatusBarView
is superflous and can be inlined to [leftElements, rightElements]
rightEntries.push(this.renderElement(entry)); | ||
} | ||
}); | ||
const leftElements: React.ReactElement<StatusBarElements> = <StatusBarElements key="statusbar-left-elements" alignment="left" entries={leftEntries} />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StatusBarElements
and StatusBarView
are superfluous:
return [
<div key="statusbar-left-elements" className="area left">{leftEntries}</div>,
<div key="statusbar-right-elements" className="area right">{rightEntries}</div>
];
|
||
export class StatusBarElements extends React.Component<StatusBarElements.Props> { | ||
render() { | ||
return <div key={"statusbar-elements-" + this.props.alignment + "-area"} className={"area " + this.props.alignment}>{this.props.entries}</div>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi: keys are only making sense into an array, here you have the following structure:
<StatusBarElements><div key="..." /></StatusBarElements>
- key is redundant as you can see
Generally when you use jsx keys are not necessary, they are needed only then jsx elements are collected into an array and only for direct members of the array
} | ||
|
||
protected createAttributes(entry: StatusBarEntry): StatusBarEntryAttributes { | ||
const attrs: StatusBarEntryAttributes = {}; | ||
|
||
if (entry.command) { | ||
attrs.onclick = () => { | ||
attrs.onClick = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should get rid of it: https://stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind
const leftElements = h.div({ className: 'area left' }, VirtualRenderer.flatten(leftEntries)); | ||
const rightElements = h.div({ className: 'area right' }, VirtualRenderer.flatten(rightEntries)); | ||
return VirtualRenderer.flatten([leftElements, rightElements]); | ||
const leftElements = <div className="area left"><React.Fragment>{leftEntries}</React.Fragment></div>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React.Fragment
is redundant here, leftEntries
already wrapped into div
, the same for rightEntries
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without wrapping React.Fragment I get the usual warning:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the top-level render call using <div>. See https://fb.me/react-warning-keys for more information.
in div
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw. React.Fragment
does not replace div
. Actually it does not render a html tag at all. As far as I understood one can use it to group a list of elements without setting key attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am not sure React.Fragment
is proper fix. https://fb.me/react-warning-keys
does not tell anything about it. They say there that each child of leftEntries
and rightEntries
should have a key. Are you sure that React.Fragment
does the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understood the following blog post, yes:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, thanks for checking!
f4d82e9
to
fbf205e
Compare
super.onUpdateRequest(msg); | ||
const child = this.render(); | ||
if (!this.childContainer) { | ||
// if we are adding scrolling, we need to wrap the contents in its own div, to not conflict with the virtual dom algo. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still true for React?
The statusbar is explicitly calling That said, I don't know if there are any bad implications with the current approach has, given that it seems to be a smoother path for our migration (we already explicitly call update everywhere). |
} | ||
|
||
protected onclick(entry: StatusBarEntry): () => void { | ||
return () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It creates a new function each time. We should use exactly the same function for all entries. It can be accomplished by retrieving an entry id from an event target.
b0e9a53
to
3f4bbf8
Compare
if (this.ready) { | ||
return [this.renderSearchField(), this.renderExtensionList()]; | ||
} else { | ||
const spinner = h.div({ className: 'fa fa-spinner fa-pulse fa-3x fa-fw' }, ''); | ||
return h.div({ className: 'spinnerContainer' }, spinner); | ||
const spinner = this.creator.div({ className: 'fa fa-spinner fa-pulse fa-3x fa-fw' }, ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how you managed to make it a minimal change.
But given that people will take our code as an example, we should at least use TSX.
protected render(): h.Child { | ||
const leftEntries: h.Child[] = []; | ||
const rightEntries: h.Child[] = []; | ||
getLayoutData(): StatusBarLayoutData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you reintroduced these methods by accident?
4c0eb95
to
c509f5d
Compare
import { ReactWidget } from '../widgets/react-widget'; | ||
import * as React from "react"; | ||
|
||
export interface StatusBarLayoutData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used, please remove
backgroundColor?: string | ||
} | ||
|
||
export interface StatusBarEntryData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
protected onUpdateRequest(msg: Message): void { | ||
super.onUpdateRequest(msg); | ||
if (!this.childContainer) { | ||
// if we are adding scrolling, we need to wrap the contents in its own div, to not conflict with the virtual dom algo. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still the case with react?
Signed-off-by: Jan Bicker <jan.bicker@typefox.io>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Proof of concept with StatusBar and ExtensionManager