├── .gitignore ├── .storybook ├── main.js ├── preview-body.html └── preview-head.html ├── ISSUES.md ├── README.md ├── docs ├── Issues.md ├── awesome-editor.md ├── basic.gif ├── code.gif ├── content.md ├── drag-block.gif ├── drag.md ├── funcs.md ├── hooks.md ├── image.gif ├── mouseup-drop.md ├── plugin.md ├── selection.md └── tips.md ├── examples ├── basic │ ├── index.js │ └── public │ │ └── index.html └── next │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pages │ ├── _app.js │ ├── _document.js │ ├── api │ │ └── hello.js │ └── index.js │ ├── public │ ├── favicon.ico │ └── vercel.svg │ ├── styles │ ├── Draft.css │ ├── Home.module.css │ └── globals.css │ └── yarn.lock ├── mock ├── dndHelper.js └── nested.js ├── package-lock.json ├── package.json ├── src ├── Context.ts ├── Editor.tsx ├── components │ ├── button │ │ ├── Blockquote.tsx │ │ ├── Bold.tsx │ │ ├── BulletedList.tsx │ │ ├── CodeBlock.tsx │ │ ├── Dragger.tsx │ │ ├── H1.tsx │ │ ├── H2.tsx │ │ ├── H3.tsx │ │ ├── H4.tsx │ │ ├── ImageAlignCenter.tsx │ │ ├── ImageAlignLeft.tsx │ │ ├── ImageAlignLeftFillContent.tsx │ │ ├── ImageAlignRightFillContent.tsx │ │ ├── ImageFillWidth.tsx │ │ ├── ImageInsetCenter.tsx │ │ ├── ImageOutsetCenter.tsx │ │ ├── InlineCode.tsx │ │ ├── Italic.tsx │ │ ├── Link.tsx │ │ ├── NumberedList.tsx │ │ ├── Plus.tsx │ │ ├── Selectable.tsx │ │ ├── StrikeThrough.tsx │ │ ├── Underline.tsx │ │ ├── Unlink.tsx │ │ └── utils │ │ │ ├── action.css │ │ │ ├── withAction.tsx │ │ │ └── withFillColor.tsx │ ├── image-toolbar │ │ ├── index.tsx │ │ └── styles.css │ ├── image │ │ ├── index.tsx │ │ ├── resizable.js │ │ └── styles │ │ │ └── index.css │ ├── inline-toolbar │ │ ├── Divider.tsx │ │ ├── InputBar.tsx │ │ ├── StyleControls.tsx │ │ ├── index.tsx │ │ └── styles.css │ ├── link-span │ │ ├── index.tsx │ │ └── styles.css │ ├── link │ │ ├── index.tsx │ │ └── styles.css │ ├── sidebar │ │ ├── index.tsx │ │ └── styles.css │ ├── style-controls │ │ ├── BlockStyleControls.tsx │ │ ├── InlineStyleControls.tsx │ │ ├── StyleControlButton.tsx │ │ ├── index.tsx │ │ └── styles │ │ │ └── index.css │ └── title │ │ ├── index.tsx │ │ └── styles │ │ └── index.css ├── constants.ts ├── createEditor.tsx ├── decoratorComposer.js ├── decorators │ ├── color │ │ └── index.js │ └── prism │ │ ├── index.tsx │ │ ├── multiple.ts │ │ └── theme │ │ ├── editor.css │ │ ├── prism-solarizedlight.css │ │ └── prism.css ├── editor │ ├── DocEditor.ts │ └── ModernEditor.ts ├── helpers │ └── clamp.ts ├── hooks │ ├── styles │ │ ├── useFocus.css │ │ └── useResize.css │ ├── useAlignment.ts │ ├── useFocus.ts │ └── useResize.ts ├── index.tsx ├── plugins │ ├── AddImagePlugin.ts │ ├── BlockStyleFnPlugin.ts │ ├── CustomStyleMapPlugin.ts │ ├── DefaultHandleKeyCommandPlugin.ts │ ├── FinalNewLinePlugin.ts │ ├── HandleDroppedFilesPlugin.ts │ ├── InlineToolbarPlugin.ts │ ├── LinkDecorator.ts │ ├── LinkSpanDecoratorPlugin.ts │ ├── PlaceholderPlugin.ts │ ├── SelectionChangePlugin.ts │ ├── StateFilterPlugin.ts │ ├── StyleControlPlugin.ts │ ├── UpdateBlockDepthData.ts │ ├── block-render-map-plugin │ │ ├── CodeBlock.tsx │ │ ├── NextDiv.tsx │ │ ├── index.tsx │ │ ├── nextDiv.css │ │ └── styles.css │ ├── blockStyleFnPlugin.css │ ├── dnd-plugin │ │ ├── configNest.ts │ │ ├── index-placeholder.js │ │ ├── index.js │ │ └── test-placeholder.js │ ├── dnd │ │ ├── Container.ts │ │ ├── Dragger.ts │ │ ├── README.md │ │ ├── closest.ts │ │ ├── collision.ts │ │ ├── configs │ │ │ ├── resolveConfig.ts │ │ │ └── resolveDndConfig.ts │ │ ├── dom.ts │ │ ├── find.ts │ │ ├── index.ts │ │ ├── key.ts │ │ ├── middleware │ │ │ ├── onMove │ │ │ │ ├── addIntermediateCtxValue.ts │ │ │ │ ├── effects │ │ │ │ │ ├── DndEffects.ts │ │ │ │ │ ├── EffectsManager.ts │ │ │ │ │ ├── handleEnterContainer.ts │ │ │ │ │ ├── handleEnterHomeContainer.ts │ │ │ │ │ ├── handleEnterOtherContainer.ts │ │ │ │ │ ├── handleImpactContainerEffect.ts │ │ │ │ │ ├── handleImpactDraggerEffect.ts │ │ │ │ │ ├── handleLeaveContainer.ts │ │ │ │ │ ├── handleLeaveHomeContainer.ts │ │ │ │ │ ├── handleLeaveOtherContainer.ts │ │ │ │ │ ├── handleReorder.ts │ │ │ │ │ ├── handleReorderOnHomeContainer.ts │ │ │ │ │ ├── handleReorderOnOtherContainer.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── removeIntermediateCtxValue.ts │ │ │ │ └── syncCopyPosition.ts │ │ │ ├── onStart │ │ │ │ ├── attemptToCreateClone.ts │ │ │ │ ├── getDimensions.ts │ │ │ │ ├── getDimensionsNested.ts │ │ │ │ └── validateContainers.ts │ │ │ └── shared │ │ │ │ ├── getContainer.ts │ │ │ │ └── getImpactRawInfo.ts │ │ ├── mutationHandler.ts │ │ ├── reporter.ts │ │ ├── sensors │ │ │ ├── mouse.ts │ │ │ └── utils.ts │ │ ├── setAttributes.ts │ │ ├── structure │ │ │ └── SortedItems.ts │ │ └── utils.ts │ └── sidebar-plugin │ │ ├── createAddOn.ts │ │ ├── index.ts │ │ └── styles.css ├── style.css ├── types │ ├── button.ts │ ├── components.ts │ ├── dnd.ts │ ├── draft-js │ │ └── index.ts │ ├── editor.ts │ ├── hooks.ts │ ├── imageToolbar.ts │ ├── index.ts │ ├── inlineToolBar.ts │ ├── plugin.ts │ ├── rect.ts │ ├── sidebar.ts │ ├── util.ts │ └── withEditor.ts ├── utils │ ├── block │ │ ├── appendChild.ts │ │ ├── blockMutationUtil.ts │ │ ├── blockUtil.ts │ │ ├── contains.ts │ │ ├── createEmptyBlock.ts │ │ ├── createEmptyBlockNode.ts │ │ ├── findRootNode.ts │ │ ├── findRootNodeSibling.ts │ │ ├── flattenBlocks.ts │ │ ├── horizontalTransfer.ts │ │ ├── insertBlockAfter.ts │ │ ├── insertBlockBefore.ts │ │ ├── insertChild.ts │ │ ├── removeBlock.ts │ │ ├── removeBlockWithClear.ts │ │ ├── resetSibling.ts │ │ ├── transfer.ts │ │ ├── updateBlockMapLinks.ts │ │ ├── verticalTransfer.ts │ │ └── wrapBlock.ts │ ├── compareArray.ts │ ├── contentBlock.ts │ ├── createEntity.ts │ ├── draft-js │ │ ├── decorateKeyCommandHandler.ts │ │ └── lib │ │ │ ├── DraftModifier.ts │ │ │ ├── NestedRichTextEditorUtil.ts │ │ │ ├── keyCommandPlainBackspace.ts │ │ │ ├── removeRangeFromContentState.ts │ │ │ └── removeTextWithStrategy.ts │ ├── editorState.ts │ ├── event │ │ └── bindEvents.ts │ ├── findNode.ts │ ├── getInlineToolbarInlineInfo.ts │ ├── getSelectionBlockTypes.ts │ ├── infoLog.ts │ ├── isBlockFocused.ts │ ├── keyHelper.ts │ ├── moveSelectionToEnd.ts │ ├── noop.ts │ ├── rect │ │ ├── findBlockContainsPoint.ts │ │ ├── getBoundingRectWithSafeArea.ts │ │ ├── getRootNode.ts │ │ ├── getSelectionBlockRect.ts │ │ ├── getSelectionRect.ts │ │ └── getSelectionRectRelativeToOffsetParent.ts │ ├── setSelectionToBlock.ts │ └── throttle.ts └── withEditor.tsx ├── stories └── Editor.stories.tsx ├── tsconfig.json └── tsdx.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | node_modules 5 | .cache 6 | dist 7 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | stories: ['../stories/**/*.stories.(ts|tsx)'], 5 | addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-docs'], 6 | webpackFinal: async (config) => { 7 | config.module.rules.push({ 8 | test: /\.(ts|tsx)$/, 9 | use: [ 10 | { 11 | loader: require.resolve('ts-loader'), 12 | options: { 13 | transpileOnly: true, 14 | }, 15 | }, 16 | { 17 | loader: require.resolve('react-docgen-typescript-loader'), 18 | }, 19 | ], 20 | }); 21 | 22 | // https://storybook.js.org/docs/configurations/custom-webpack-config/ 23 | config.module.rules.splice(6, 1) 24 | 25 | // https://github.com/webpack/webpack/issues/10843 26 | config.module.rules.unshift({ 27 | test: /\.css$/i, 28 | use: ['style-loader', 'css-loader'], 29 | // include: path.resolve(__dirname, '../src'), 30 | }); 31 | 32 | config.resolve.extensions.push('.ts', '.tsx', '.css', 'js'); 33 | 34 | return config; 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /.storybook/preview-body.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ISSUES.md: -------------------------------------------------------------------------------- 1 | # ISSUES 2 | 3 | ## InlineToolbar 4 | 5 | [] 当用户点击`link`以后,没有输入任何东西;点击其它区域使`inlineBar`消失,这个时候刚刚选中的部分背景色应该移除 6 | 7 | ## Layout design need to refer to.. 8 | 9 | [What's New?](https://www.notion.so/What-s-New-157765353f2c4705bd45474e5ba8b46c) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-tapable-editor 2 | 3 | _A pluginable, intuitive medium/notion like rich text editor_ 4 | 5 | The original idea is to build an easy used rich text editor. `react-tapable-editor` is built on [draft-js](https://github.com/facebook/draft-js), and its plugin system is besed on [tapable](https://github.com/webpack/tapable) which is famous as the engine of [webpack](https://github.com/webpack/webpack). 6 | 7 | ## Features 8 | 9 | ### BlockStyle 10 | 11 | - [x] header 12 | - [x] quotation 13 | - [x] list 14 | - [x] quotation 15 | 16 | ![gif](./docs/basic.gif) 17 | 18 | #### code block 19 | 20 | - [x] highlight with prism 21 | - [x] copy from vscode, style could be preserved. 22 | - [ ] copy from github, code will suppress into one line. 23 | 24 | ![code](./docs/code.gif) 25 | 26 | #### Image 27 | 28 | ![image](./docs/image.gif) 29 | 30 | ### Experimental feature 31 | 32 | #### Drag and drop block 33 | 34 | ![drag-block](./docs/drag-block.gif) 35 | 36 | [drag to make layout design](./docs/drag.md) 37 | 38 | #### TODO consider smooth reflow... 39 | 40 | ## How to start 41 | 42 | ```bash 43 | $ npm install 44 | $ npm run storybook 45 | ``` 46 | 47 | ## FAQ 48 | 49 | ### why choose draft-js 50 | 51 | - [awesome-draft-js](https://github.com/nikgraf/awesome-draft-js) 52 | - [Why Wagtail’s new editor is built with Draft.js](https://wagtail.io/blog/why-wagtail-new-editor-is-built-with-draft-js/) 53 | - [Rethinking rich text pipelines with Draft.js](https://wagtail.io/blog/rethinking-rich-text-pipelines-with-draft-js/) 54 | -------------------------------------------------------------------------------- /docs/Issues.md: -------------------------------------------------------------------------------- 1 | # Issues 2 | 3 | ## onSelect && onFocus 4 | 5 | 之所以会出现这个问题是在进行`focus` decorator开发时,如果说当前已经处于`blur`状态的话,这个时候,如果用户鼠标点击文中的位置;如果说,此次的点击和bur时的selection一致的话,它只会触发`onFocus`;但是如果说不一致,它首先触发`onFocus`然后再触发`onSelect`将光标移动到它应该在的位置 6 | 7 | ## copy and paste 8 | 9 | [Clicking on styling button steals focus from editor, sometimes doesn't apply style to document #696](https://github.com/facebook/draft-js/issues/696) 10 | 11 | [Copy/paste between editors #787](https://github.com/facebook/draft-js/issues/787) 12 | 13 | [editor.props.stripPastedStyles](https://github.com/facebook/draft-js/blob/4c4465f6c05b6dbb9eb769f98e659f917bbdc0f6/src/component/handlers/edit/editOnPaste.js#L111) 14 | 15 | ## filter 16 | 17 | [Rethinking rich text pipelines with Draft.js](https://wagtail.io/blog/rethinking-rich-text-pipelines-with-draft-js/) 18 | 19 | [draftjs-filters](https://github.com/thibaudcolas/draftjs-filters) 20 | 21 | [Why Wagtail’s new editor is built with Draft.js](https://wagtail.io/blog/why-wagtail-new-editor-is-built-with-draft-js/) 22 | 23 | [draftail](https://github.com/springload/draftail) 24 | 25 | ## create block 26 | 27 | DraftEditorContent-core.react.js 28 | 29 | 30 | ### createNestBlockPlugin 31 | 用来创建blockKey 32 | generateRandomKey 33 | 34 | 在AtomicBlockUtils有使用到。。。 35 | 参考:splitBlockInContentState;正常触发`split-block`时,它会调用`splitBlockInContentState`这个方法;现在通过拦截的方式对split-block进行细化。 36 | insertTextInContentState 37 | 38 | ### DraftOffsetKey 39 | 40 | 其中会引入`blockKey` 41 | 42 | ### experimental 43 | 44 | `DraftEditorBlockNode.js` -> `DraftEditorNode.js` -> -------------------------------------------------------------------------------- /docs/awesome-editor.md: -------------------------------------------------------------------------------- 1 | # awesome editor 2 | 3 | - [ProseMirror's view component](https://github.com/ProseMirror/prosemirror-view) 4 | -------------------------------------------------------------------------------- /docs/basic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryuever/react-tapable-editor/90b77c88e8b7ca8a2efd17a6b5a0a384df333e23/docs/basic.gif -------------------------------------------------------------------------------- /docs/code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryuever/react-tapable-editor/90b77c88e8b7ca8a2efd17a6b5a0a384df333e23/docs/code.gif -------------------------------------------------------------------------------- /docs/content.md: -------------------------------------------------------------------------------- 1 | ## drag and drop block 2 | 3 | Drag and drop block is an experimental feature. It is highly inspired by notion blog style. Normally, the paragraph is placed one after another, However, if we can redesign the style with sibling paragraph, it will be more readable and attractive. 4 | -------------------------------------------------------------------------------- /docs/drag-block.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryuever/react-tapable-editor/90b77c88e8b7ca8a2efd17a6b5a0a384df333e23/docs/drag-block.gif -------------------------------------------------------------------------------- /docs/funcs.md: -------------------------------------------------------------------------------- 1 | 2 | # findRangeImmutable 3 | 4 | # modifyInlineStyle 5 | 6 | # getTextAfterNearestEntity 7 | 8 | # DraftModifier => applyEntity => removeEntitiesAtEdges 9 | 10 | [How to remove an entity? #182](https://github.com/facebook/draft-js/issues/182) 11 | 12 | # decorator 13 | 14 | [Coloring hexadecimal color codes](https://github.com/Soreine/draft-js-simpledecorator#example-coloring-hexadecimal-color-codes) 15 | 16 | [DraftDecorator.js](https://github.com/facebook/draft-js/blob/master/src/model/decorators/DraftDecorator.js) -------------------------------------------------------------------------------- /docs/hooks.md: -------------------------------------------------------------------------------- 1 | # hooks 2 | 3 | ## waterfallResult 4 | 5 | 类似bailResult都是会返回一个result,但是它会接受所有的参数设置,最后返回一个合集的东西 6 | 7 | 1. 使用waterfall替代,你必须得提供一个tap放到最后,来接受;这样会造成值只能够在它的callback中来获取 8 | 2. bailResult的话,缩短了链路长度这种需求只能为1 -------------------------------------------------------------------------------- /docs/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryuever/react-tapable-editor/90b77c88e8b7ca8a2efd17a6b5a0a384df333e23/docs/image.gif -------------------------------------------------------------------------------- /docs/mouseup-drop.md: -------------------------------------------------------------------------------- 1 | # mouse event and drop 2 | 3 | - [javascript-events— 'mouseup' not firing after mousemove](https://stackoverflow.com/questions/9506041/javascript-events-mouseup-not-firing-after-mousemove) 4 | - [mouseup is not fired after mousedown](https://stackoverflow.com/questions/39971069/mouseup-is-not-fired-after-mousedown) 5 | - [jquery-ui: immediate draggable on mousedown](https://stackoverflow.com/questions/40464357/jquery-ui-immediate-draggable-on-mousedown) 6 | 7 | ## Drag'n'Drop with mouse events 8 | 9 | [Drag'n'Drop with mouse events](https://javascript.info/mouse-drag-and-drop) 10 | -------------------------------------------------------------------------------- /docs/plugin.md: -------------------------------------------------------------------------------- 1 | # plugin 2 | 3 | ## pending teardown 4 | -------------------------------------------------------------------------------- /docs/selection.md: -------------------------------------------------------------------------------- 1 | # Selection 2 | 3 | 经过一系列操作的时候,经常会处理`selection`... 4 | 5 | ## methods 6 | 7 | ### Modifier.splitBlock 8 | 9 | 执行完以后,它的selection应该是放置到刚刚触发split时创造出的block的开头; 10 | 11 | 可以通过什么形式验证? 12 | 13 | ```js 14 | const newState = Modifier.split(editorState, selection) 15 | ``` 16 | 17 | ## how to forceSelection 18 | -------------------------------------------------------------------------------- /docs/tips.md: -------------------------------------------------------------------------------- 1 | # tips 2 | 3 | ## get last change type 4 | 5 | [Is there a way to distinguish what changed in onChange callback? #830](https://github.com/facebook/draft-js/issues/830)调用方法得到[EditorChangeType](https://draftjs.org/docs/api-reference-editor-change-type) 6 | 7 | ```js 8 | editorState.getLastChangeType() 9 | ``` -------------------------------------------------------------------------------- /examples/basic/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import PluginEditor from 'react-tapable-editor' 4 | 5 | ReactDOM.render(, document.getElementById('app')) 6 | -------------------------------------------------------------------------------- /examples/basic/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | editor 4 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/next/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /examples/next/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on Vercel 27 | 28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /examples/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "next": "9.5.2", 12 | "react": "16.13.1", 13 | "react-dom": "16.13.1", 14 | "react-tapable-editor": "^0.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/next/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import '../styles/Draft.css' 3 | // https://nextjs.org/docs/basic-features/built-in-css-support#import-styles-from-node_modules 4 | // import 'react-tapable-editor/dist/my-custom-file-name.css' 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return 8 | } 9 | 10 | export default MyApp 11 | -------------------------------------------------------------------------------- /examples/next/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | class MyDocument extends Document { 4 | static async getInitialProps(ctx) { 5 | const initialProps = await Document.getInitialProps(ctx) 6 | return { ...initialProps } 7 | } 8 | 9 | render() { 10 | return ( 11 | 12 | 13 |