From 2becb9e23b588fc602178ba4ef14c00c4f0312b5 Mon Sep 17 00:00:00 2001 From: VincentBel Date: Sat, 20 Jan 2018 19:34:19 +0800 Subject: [PATCH 1/4] remove hoist transform --- src/index.ts | 3 - .../react-hoist-generics-transform.ts | 124 ------------- ...react-js-make-props-and-state-transform.ts | 166 ++++++++++-------- .../non-react/input.tsx | 18 -- .../non-react/output.tsx | 10 -- .../propless-stateless/input.tsx | 7 - .../propless-stateless/output.tsx | 10 -- .../props-and-state/input.tsx | 7 - .../props-and-state/output.tsx | 14 -- .../propless-stateless/output.tsx | 8 +- .../set-state-advanced/output.tsx | 6 +- .../set-state-only/output.tsx | 6 +- .../state-in-class-member/output.tsx | 6 +- .../state-in-constructor/output.tsx | 6 +- .../static-proptypes-getter-simple/output.tsx | 10 +- .../static-proptypes-many-props/output.tsx | 84 ++++----- .../static-proptypes-simple/output.tsx | 10 +- test/runner.ts | 2 - 18 files changed, 164 insertions(+), 333 deletions(-) delete mode 100644 src/transforms/react-hoist-generics-transform.ts delete mode 100644 test/react-hoist-generics-transform/non-react/input.tsx delete mode 100644 test/react-hoist-generics-transform/non-react/output.tsx delete mode 100644 test/react-hoist-generics-transform/propless-stateless/input.tsx delete mode 100644 test/react-hoist-generics-transform/propless-stateless/output.tsx delete mode 100644 test/react-hoist-generics-transform/props-and-state/input.tsx delete mode 100644 test/react-hoist-generics-transform/props-and-state/output.tsx diff --git a/src/index.ts b/src/index.ts index 7e824e0..0ba4b33 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import * as ts from 'typescript'; import { compile } from './compiler'; import { reactJSMakePropsAndStateInterfaceTransformFactoryFactory } from './transforms/react-js-make-props-and-state-transform'; -import { reactHoistGenericsTransformFactoryFactory } from './transforms/react-hoist-generics-transform'; import { reactRemovePropTypesAssignmentTransformFactoryFactory } from './transforms/react-remove-prop-types-assignment-transform'; import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/react-move-prop-types-to-class-transform'; import { collapseIntersectionInterfacesTransformFactoryFactory } from './transforms/collapse-intersection-interfaces-transform'; @@ -11,7 +10,6 @@ import { reactRemoveStaticPropTypesMemberTransformFactoryFactory } from './trans export { reactMovePropTypesToClassTransformFactoryFactory, reactJSMakePropsAndStateInterfaceTransformFactoryFactory, - reactHoistGenericsTransformFactoryFactory, collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, @@ -21,7 +19,6 @@ export { export const allTransforms = [ reactMovePropTypesToClassTransformFactoryFactory, reactJSMakePropsAndStateInterfaceTransformFactoryFactory, - reactHoistGenericsTransformFactoryFactory, collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, diff --git a/src/transforms/react-hoist-generics-transform.ts b/src/transforms/react-hoist-generics-transform.ts deleted file mode 100644 index 768517c..0000000 --- a/src/transforms/react-hoist-generics-transform.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as ts from 'typescript'; -import * as _ from 'lodash'; - -import * as helpers from '../helpers'; - -export type Factory = ts.TransformerFactory; - -/** - * Hoist generics to top of a class declarations in a React component - * - * @example - * Before: - * class SomeComponent extends React.Component<{foo: number;}, {bar: string;}> {} - * - * After - * type SomeComponentProps = {foo: number;}; - * type SomeComponentState = {bar: string;}; - * class SomeComponent extends React.Component {} - */ -export function reactHoistGenericsTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { - return function reactHoistGenericsTransformFactory(context: ts.TransformationContext) { - return function reactHoistGenericsTransform(node: ts.SourceFile) { - return visitSourceFile(node); - }; - }; - - function visitSourceFile(sourceFile: ts.SourceFile) { - - for (const statement of sourceFile.statements) { - if (ts.isClassDeclaration(statement) && helpers.isReactComponent(statement, typeChecker)) { - return hoist(statement, sourceFile); - } - } - - return sourceFile; - } -} - -/** - * Hoist props and state generic types - * @param reactClass - * @param sourceFile - */ -function hoist(reactClass: ts.ClassDeclaration, sourceFile: ts.SourceFile) { - if (!reactClass.heritageClauses) { - return sourceFile; - } - const className = reactClass && reactClass.name && reactClass.name.getText(sourceFile); - const reactHeritageClauses = _.find(reactClass.heritageClauses, helpers.isReactHeritageClause); - - if (reactHeritageClauses === undefined || !reactHeritageClauses.types == undefined) { - return sourceFile; - } - const [reactType] = reactHeritageClauses.types; - if (reactType.typeArguments === undefined || reactType.typeArguments.length < 2) { - return sourceFile; - } - - const [propType, stateType] = reactType.typeArguments; - const propTypeName = `${className}Props`; - const stateTypeName = `${className}State`; - const propTypeDeclaration = ts.createTypeAliasDeclaration([], [], propTypeName, [], propType); - const stateTypeDeclaration = ts.createTypeAliasDeclaration([], [], stateTypeName, [], stateType); - const propTypeRef = ts.createTypeReferenceNode(propTypeName, []); - const stateTypeRef = ts.createTypeReferenceNode(stateTypeName, []); - const newClassStatement = insertTypeRefs(reactClass, propTypeRef, stateTypeRef); - - let statements = helpers.insertBefore(sourceFile.statements, reactClass, propTypeDeclaration) - statements = helpers.insertAfter(statements, propTypeDeclaration, stateTypeDeclaration); - statements = helpers.replaceItem(statements, reactClass, newClassStatement); - - return ts.updateSourceFileNode(sourceFile, statements); -} - -/** - * Replace props and state types in a React component with type references - * - * @example - * input - * ``` - * class MyComp extends React.Component<{}, {}> {} - * ``` - * - * output - * ``` - * class MyComp extends React.Component {} - * ``` - * - * @param reactClassDeclaration A React class declaration - * @param propTypeRef React Props type reference - * @param stateTypeRef React State type reference - */ -function insertTypeRefs( - reactClassDeclaration: ts.ClassDeclaration, - propTypeRef: ts.TypeReferenceNode, - stateTypeRef: ts.TypeReferenceNode, -) { - if (reactClassDeclaration.heritageClauses === undefined) { - return reactClassDeclaration; - } - const reactHeritageClause = _.find(reactClassDeclaration.heritageClauses, helpers.isReactHeritageClause); - - if (reactHeritageClause === undefined) { - return reactClassDeclaration; - } - - const [reactExpression] = reactHeritageClause.types; - const newReactExpression = ts.updateExpressionWithTypeArguments( - reactExpression, - [propTypeRef, stateTypeRef], - reactExpression.expression, - ); - const newHeritageClauses = ts.updateHeritageClause(reactHeritageClause, [newReactExpression]); - - return ts.updateClassDeclaration( - reactClassDeclaration, - reactClassDeclaration.decorators, - reactClassDeclaration.modifiers, - reactClassDeclaration.name, - reactClassDeclaration.typeParameters, - helpers.replaceItem(reactClassDeclaration.heritageClauses, reactHeritageClause, newHeritageClauses), - reactClassDeclaration.members, - ); -} diff --git a/src/transforms/react-js-make-props-and-state-transform.ts b/src/transforms/react-js-make-props-and-state-transform.ts index e6dd233..0b5e968 100644 --- a/src/transforms/react-js-make-props-and-state-transform.ts +++ b/src/transforms/react-js-make-props-and-state-transform.ts @@ -12,31 +12,57 @@ export type Factory = ts.TransformerFactory; export function reactJSMakePropsAndStateInterfaceTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { return function reactJSMakePropsAndStateInterfaceTransformFactory(context: ts.TransformationContext) { return function reactJSMakePropsAndStateInterfaceTransform(sourceFile: ts.SourceFile) { - const visited = ts.visitEachChild(sourceFile, visitor, context); + const visited = visitSourceFile(sourceFile, typeChecker); ts.addEmitHelpers(visited, context.readEmitHelpers()); return visited; + }; + }; +} - function visitor(node: ts.Node) { - if (ts.isClassDeclaration(node)) { - return visitClassDeclaration(node, sourceFile, typeChecker); - } - - return node; - } +function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + for (const statement of sourceFile.statements) { + if (ts.isClassDeclaration(statement) && helpers.isReactComponent(statement, typeChecker)) { + return visitReactClassDeclaration(statement, sourceFile, typeChecker); } } + + return sourceFile; } -function visitClassDeclaration( +function visitReactClassDeclaration( classDeclaration: ts.ClassDeclaration, sourceFile: ts.SourceFile, - typeChecker: ts.TypeChecker + typeChecker: ts.TypeChecker, ) { - if (!helpers.isReactComponent(classDeclaration, typeChecker)) { - return classDeclaration; + if (!classDeclaration.heritageClauses || !classDeclaration.heritageClauses.length) { + return sourceFile; } + const className = classDeclaration && classDeclaration.name && classDeclaration.name.getText(sourceFile); + const propType = getPropsTypeOfReactComponentClass(classDeclaration, sourceFile); + const stateType = getStateTypeOfReactComponentClass(classDeclaration, typeChecker); + const propTypeName = `${className}Props`; + const stateTypeName = `${className}State`; + const propTypeDeclaration = ts.createTypeAliasDeclaration([], [], propTypeName, [], propType); + const stateTypeDeclaration = ts.createTypeAliasDeclaration([], [], stateTypeName, [], stateType); + const propTypeRef = ts.createTypeReferenceNode(propTypeName, []); + const stateTypeRef = ts.createTypeReferenceNode(stateTypeName, []); + + const newClassDeclaration = getNewReactClassDeclaration(classDeclaration, propTypeRef, stateTypeRef); + + let statements = helpers.insertBefore(sourceFile.statements, classDeclaration, [ + propTypeDeclaration, + stateTypeDeclaration, + ]); + statements = helpers.replaceItem(statements, classDeclaration, newClassDeclaration); + return ts.updateSourceFileNode(sourceFile, statements); +} +function getNewReactClassDeclaration( + classDeclaration: ts.ClassDeclaration, + propTypeRef: ts.TypeNode, + stateTypeRef: ts.TypeNode, +) { if (!classDeclaration.heritageClauses || !classDeclaration.heritageClauses.length) { return classDeclaration; } @@ -48,10 +74,7 @@ function visitClassDeclaration( firstHeritageClause.types[0], ts.updateExpressionWithTypeArguments( firstHeritageClause.types[0], - [ - getPropsTypeOfReactComponentClass(classDeclaration, sourceFile), - getStateTypeOfReactComponentClass(classDeclaration, typeChecker), - ], + [propTypeRef, stateTypeRef], firstHeritageClause.types[0].expression, ), ); @@ -77,44 +100,45 @@ function getPropsTypeOfReactComponentClass( classDeclaration: ts.ClassDeclaration, sourceFile: ts.SourceFile, ): ts.TypeNode { - const staticPropTypesMember = _.find(classDeclaration.members, (member) => { - return ts.isPropertyDeclaration(member) && + const staticPropTypesMember = _.find(classDeclaration.members, member => { + return ( + ts.isPropertyDeclaration(member) && helpers.hasStaticModifier(member) && - helpers.isPropTypesMember(member, sourceFile); + helpers.isPropTypesMember(member, sourceFile) + ); }); if ( - staticPropTypesMember !== undefined - && ts.isPropertyDeclaration(staticPropTypesMember) // check to satisfy type checker - && staticPropTypesMember.initializer - && ts.isObjectLiteralExpression(staticPropTypesMember.initializer) + staticPropTypesMember !== undefined && + ts.isPropertyDeclaration(staticPropTypesMember) && // check to satisfy type checker + staticPropTypesMember.initializer && + ts.isObjectLiteralExpression(staticPropTypesMember.initializer) ) { - return buildInterfaceFromPropTypeObjectLiteral(staticPropTypesMember.initializer) + return buildInterfaceFromPropTypeObjectLiteral(staticPropTypesMember.initializer); } - const staticPropTypesGetterMember = _.find(classDeclaration.members, (member) => { - return ts.isGetAccessorDeclaration(member) && + const staticPropTypesGetterMember = _.find(classDeclaration.members, member => { + return ( + ts.isGetAccessorDeclaration(member) && helpers.hasStaticModifier(member) && - helpers.isPropTypesMember(member, sourceFile); + helpers.isPropTypesMember(member, sourceFile) + ); }); if ( - staticPropTypesGetterMember !== undefined - && ts.isGetAccessorDeclaration(staticPropTypesGetterMember) // check to satisfy typechecker + staticPropTypesGetterMember !== undefined && + ts.isGetAccessorDeclaration(staticPropTypesGetterMember) // check to satisfy typechecker ) { - const returnStatement = _.find( - staticPropTypesGetterMember.body.statements, - (statement) => ts.isReturnStatement(statement), + const returnStatement = _.find(staticPropTypesGetterMember.body.statements, statement => + ts.isReturnStatement(statement), ); if ( - returnStatement !== undefined - && ts.isReturnStatement(returnStatement) // check to satisfy typechecker - && returnStatement.expression - && ts.isObjectLiteralExpression(returnStatement.expression) + returnStatement !== undefined && + ts.isReturnStatement(returnStatement) && // check to satisfy typechecker + returnStatement.expression && + ts.isObjectLiteralExpression(returnStatement.expression) ) { - return buildInterfaceFromPropTypeObjectLiteral( - returnStatement.expression - ) + return buildInterfaceFromPropTypeObjectLiteral(returnStatement.expression); } } @@ -132,7 +156,7 @@ function getStateTypeOfReactComponentClass( return ts.createTypeLiteralNode([]); } if (!initialStateIsVoid) { - collectedStateTypes.push(initialState) + collectedStateTypes.push(initialState); } return ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.IntersectionType, collectedStateTypes); @@ -149,30 +173,24 @@ function getInitialStateFromClassDeclaration( ): ts.TypeNode { // initial state class member - const initialStateMember = _.find(classDeclaration.members, (member) => { + const initialStateMember = _.find(classDeclaration.members, member => { try { - return ts.isPropertyDeclaration(member) && - member.name && - member.name.getText() === 'state'; - } catch(e) { + return ts.isPropertyDeclaration(member) && member.name && member.name.getText() === 'state'; + } catch (e) { return false; } }); - if (initialStateMember - && ts.isPropertyDeclaration(initialStateMember) - && initialStateMember.initializer - ) { - const type = typeChecker.getTypeAtLocation(initialStateMember.initializer)! + if (initialStateMember && ts.isPropertyDeclaration(initialStateMember) && initialStateMember.initializer) { + const type = typeChecker.getTypeAtLocation(initialStateMember.initializer)!; return typeChecker.typeToTypeNode(type); } // Initial state in constructor - const constructor = _.find( - classDeclaration.members, - (member) => member.kind === ts.SyntaxKind.Constructor, - ) as ts.ConstructorDeclaration | undefined; + const constructor = _.find(classDeclaration.members, member => member.kind === ts.SyntaxKind.Constructor) as + | ts.ConstructorDeclaration + | undefined; if (constructor && constructor.body) { for (const statement of constructor.body.statements) { @@ -181,9 +199,7 @@ function getInitialStateFromClassDeclaration( ts.isBinaryExpression(statement.expression) && statement.expression.left.getText() === 'this.state' ) { - return typeChecker.typeToTypeNode( - typeChecker.getTypeAtLocation(statement.expression.right) - ); + return typeChecker.typeToTypeNode(typeChecker.getTypeAtLocation(statement.expression.right)); } } } @@ -204,20 +220,20 @@ function getStateLookingForSetStateCalls( const typeNodes: ts.TypeNode[] = []; for (const member of classDeclaration.members) { if (member && ts.isMethodDeclaration(member) && member.body) { - lookForSetState(member.body) + lookForSetState(member.body); } } return typeNodes; function lookForSetState(node: ts.Node) { - ts.forEachChild(node, lookForSetState) + ts.forEachChild(node, lookForSetState); if ( ts.isExpressionStatement(node) && ts.isCallExpression(node.expression) && node.expression.expression.getText().match(/setState/) ) { - const type = typeChecker.getTypeAtLocation(node.expression.arguments[0]) + const type = typeChecker.getTypeAtLocation(node.expression.arguments[0]); typeNodes.push(typeChecker.typeToTypeNode(type)); } } @@ -258,8 +274,8 @@ function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteral const initializer = propertyAssignment.initializer; const isRequired = isPropTypeRequired(initializer); const typeExpression = isRequired - // We have guaranteed the type in `isPropTypeRequired()` - ? (initializer as ts.PropertyAccessExpression).expression + ? // We have guaranteed the type in `isPropTypeRequired()` + (initializer as ts.PropertyAccessExpression).expression : initializer; const typeValue = getTypeFromReactPropTypeExpression(typeExpression); @@ -272,7 +288,7 @@ function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteral ); }); - return ts.createTypeLiteralNode(members) + return ts.createTypeLiteralNode(members); } /** @@ -323,11 +339,7 @@ function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), undefined, ); - result = ts.createFunctionTypeNode( - [], - [arrayOfAny], - ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), - ); + result = ts.createFunctionTypeNode([], [arrayOfAny], ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); } } else if (ts.isCallExpression(node)) { /** @@ -345,9 +357,9 @@ function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { if (argument.elements.every(elm => ts.isStringLiteral(elm) || ts.isNumericLiteral(elm))) { result = ts.createUnionTypeNode( (argument.elements as ts.NodeArray).map(elm => - ts.createLiteralTypeNode(elm) + ts.createLiteralTypeNode(elm), ), - ) + ); } } } else if (/oneOfType$/.test(text)) { @@ -361,9 +373,7 @@ function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { } else if (/arrayOf$/.test(text)) { const argument = node.arguments[0]; if (argument) { - result = ts.createArrayTypeNode( - getTypeFromReactPropTypeExpression(argument) - ) + result = ts.createArrayTypeNode(getTypeFromReactPropTypeExpression(argument)); } } else if (/objectOf$/.test(text)) { const argument = node.arguments[0]; @@ -380,16 +390,16 @@ function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { 'key', undefined, ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ) + ), ], getTypeFromReactPropTypeExpression(argument), - ) - ]) + ), + ]); } } else if (/shape$/.test(text)) { const argument = node.arguments[0]; if (ts.isObjectLiteralExpression(argument)) { - return buildInterfaceFromPropTypeObjectLiteral(argument) + return buildInterfaceFromPropTypeObjectLiteral(argument); } } } @@ -402,7 +412,7 @@ function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); } - return result + return result; } /** diff --git a/test/react-hoist-generics-transform/non-react/input.tsx b/test/react-hoist-generics-transform/non-react/input.tsx deleted file mode 100644 index c0e4a28..0000000 --- a/test/react-hoist-generics-transform/non-react/input.tsx +++ /dev/null @@ -1,18 +0,0 @@ -interface IFoo { - -} -class Foo { - -} - -class Bar extends Foo { - -} - -class Foo2 implements IFoo { - -} - -class Bar2 extends Foo implements IFoo { - -} diff --git a/test/react-hoist-generics-transform/non-react/output.tsx b/test/react-hoist-generics-transform/non-react/output.tsx deleted file mode 100644 index ea73175..0000000 --- a/test/react-hoist-generics-transform/non-react/output.tsx +++ /dev/null @@ -1,10 +0,0 @@ -interface IFoo { -} -class Foo { -} -class Bar extends Foo { -} -class Foo2 implements IFoo { -} -class Bar2 extends Foo implements IFoo { -} diff --git a/test/react-hoist-generics-transform/propless-stateless/input.tsx b/test/react-hoist-generics-transform/propless-stateless/input.tsx deleted file mode 100644 index ab0d702..0000000 --- a/test/react-hoist-generics-transform/propless-stateless/input.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; - -export default class MyComponent extends React.Component<{}, {}> { - render() { - return
; - } -} diff --git a/test/react-hoist-generics-transform/propless-stateless/output.tsx b/test/react-hoist-generics-transform/propless-stateless/output.tsx deleted file mode 100644 index 5dae9ae..0000000 --- a/test/react-hoist-generics-transform/propless-stateless/output.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from 'react'; -type MyComponentProps = { -}; -type MyComponentState = { -}; -export default class MyComponent extends React.Component { - render() { - return
; - } -} diff --git a/test/react-hoist-generics-transform/props-and-state/input.tsx b/test/react-hoist-generics-transform/props-and-state/input.tsx deleted file mode 100644 index c1e13db..0000000 --- a/test/react-hoist-generics-transform/props-and-state/input.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; - -export default class MyComponent extends React.Component<{foo: string; bar: object;}, {baz: string; [k: string]: string}> { - render() { - return
; - } -} diff --git a/test/react-hoist-generics-transform/props-and-state/output.tsx b/test/react-hoist-generics-transform/props-and-state/output.tsx deleted file mode 100644 index a2321a1..0000000 --- a/test/react-hoist-generics-transform/props-and-state/output.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -type MyComponentProps = { - foo: string; - bar: object; -}; -type MyComponentState = { - baz: string; - [k: string]: string; -}; -export default class MyComponent extends React.Component { - render() { - return
; - } -} diff --git a/test/react-js-make-props-and-state-transform/propless-stateless/output.tsx b/test/react-js-make-props-and-state-transform/propless-stateless/output.tsx index b2d81de..5dae9ae 100644 --- a/test/react-js-make-props-and-state-transform/propless-stateless/output.tsx +++ b/test/react-js-make-props-and-state-transform/propless-stateless/output.tsx @@ -1,7 +1,9 @@ import * as React from 'react'; -export default class MyComponent extends React.Component<{ - }, { - }> { +type MyComponentProps = { +}; +type MyComponentState = { +}; +export default class MyComponent extends React.Component { render() { return
; } diff --git a/test/react-js-make-props-and-state-transform/set-state-advanced/output.tsx b/test/react-js-make-props-and-state-transform/set-state-advanced/output.tsx index ec13c77..d95b659 100644 --- a/test/react-js-make-props-and-state-transform/set-state-advanced/output.tsx +++ b/test/react-js-make-props-and-state-transform/set-state-advanced/output.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; -export default class MyComponent extends React.Component<{ - }, { foo: number; bar: number; } & { baz: number; } & { something: { big: number; here: string; of: { a: number; }[]; }; }> { +type MyComponentProps = { +}; +type MyComponentState = { foo: number; bar: number; } & { baz: number; } & { something: { big: number; here: string; of: { a: number; }[]; }; }; +export default class MyComponent extends React.Component { render() { return