-
-
Notifications
You must be signed in to change notification settings - Fork 182
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
Add an option to generate async component #126
Changes from all commits
95a039f
f745539
365f4fb
16c2f2e
58976de
b3d50da
493b37d
f223bcd
101e7d6
e7d762e
31fcc65
2f8a72d
84eb6e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React, {Component} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { {{cookiecutter.component_name}} as RealComponent } from '../LazyLoader'; | ||
|
||
/** | ||
* ExampleComponent is an example component. | ||
* It takes a property, `label`, and | ||
* displays it. | ||
* It renders an input with the property `value` | ||
* which is editable by the user. | ||
*/ | ||
export default class {{cookiecutter.component_name}} extends Component { | ||
render() { | ||
return ( | ||
<React.Suspense fallback={null}> | ||
<RealComponent {...this.props}/> | ||
</React.Suspense> | ||
); | ||
} | ||
} | ||
|
||
{{cookiecutter.component_name}}.defaultProps = {}; | ||
|
||
{{cookiecutter.component_name}}.propTypes = { | ||
/** | ||
* The ID used to identify this component in Dash callbacks. | ||
*/ | ||
id: PropTypes.string, | ||
|
||
/** | ||
* A label that will be printed when this component is rendered. | ||
*/ | ||
label: PropTypes.string.isRequired, | ||
|
||
/** | ||
* The value displayed in the input. | ||
*/ | ||
value: PropTypes.string, | ||
|
||
/** | ||
* Dash-assigned callback that should be called to report property changes | ||
* to Dash, to make them available for callbacks. | ||
*/ | ||
setProps: PropTypes.func | ||
}; | ||
|
||
|
||
export const defaultProps = {{cookiecutter.component_name}}.defaultProps; | ||
export const propTypes = {{cookiecutter.component_name}}.propTypes; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React, {Component} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
/** | ||
* ExampleComponent is an example component. | ||
* It takes a property, `label`, and | ||
* displays it. | ||
* It renders an input with the property `value` | ||
* which is editable by the user. | ||
*/ | ||
export default class {{cookiecutter.component_name}} extends Component { | ||
render() { | ||
const {id, label, setProps, value} = this.props; | ||
|
||
return ( | ||
<div id={id}> | ||
ExampleComponent: {label} | ||
<input | ||
value={value} | ||
onChange={ | ||
/* | ||
* Send the new value to the parent component. | ||
* setProps is a prop that is automatically supplied | ||
* by dash's front-end ("dash-renderer"). | ||
* In a Dash app, this will update the component's | ||
* props and send the data back to the Python Dash | ||
* app server if a callback uses the modified prop as | ||
* Input or State. | ||
*/ | ||
e => setProps({ value: e.target.value }) | ||
} | ||
/> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
{{cookiecutter.component_name}}.defaultProps = {}; | ||
|
||
{{cookiecutter.component_name}}.propTypes = { | ||
/** | ||
* The ID used to identify this component in Dash callbacks. | ||
*/ | ||
id: PropTypes.string, | ||
|
||
/** | ||
* A label that will be printed when this component is rendered. | ||
*/ | ||
label: PropTypes.string.isRequired, | ||
|
||
/** | ||
* The value displayed in the input. | ||
*/ | ||
value: PropTypes.string, | ||
|
||
/** | ||
* Dash-assigned callback that should be called to report property changes | ||
* to Dash, to make them available for callbacks. | ||
*/ | ||
setProps: PropTypes.func | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,5 @@ | ||
import React, {Component} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
/** | ||
* ExampleComponent is an example component. | ||
* It takes a property, `label`, and | ||
* displays it. | ||
* It renders an input with the property `value` | ||
* which is editable by the user. | ||
*/ | ||
export default class {{cookiecutter.component_name}} extends Component { | ||
render() { | ||
const {id, label, setProps, value} = this.props; | ||
|
||
return ( | ||
<div id={id}> | ||
ExampleComponent: {label} | ||
<input | ||
value={value} | ||
onChange={ | ||
/* | ||
* Send the new value to the parent component. | ||
* setProps is a prop that is automatically supplied | ||
* by dash's front-end ("dash-renderer"). | ||
* In a Dash app, this will update the component's | ||
* props and send the data back to the Python Dash | ||
* app server if a callback uses the modified prop as | ||
* Input or State. | ||
*/ | ||
e => setProps({ value: e.target.value }) | ||
} | ||
/> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
{{cookiecutter.component_name}}.defaultProps = {}; | ||
|
||
{{cookiecutter.component_name}}.propTypes = { | ||
/** | ||
* The ID used to identify this component in Dash callbacks. | ||
*/ | ||
id: PropTypes.string, | ||
|
||
/** | ||
* A label that will be printed when this component is rendered. | ||
*/ | ||
label: PropTypes.string.isRequired, | ||
|
||
/** | ||
* The value displayed in the input. | ||
*/ | ||
value: PropTypes.string, | ||
|
||
/** | ||
* Dash-assigned callback that should be called to report property changes | ||
* to Dash, to make them available for callbacks. | ||
*/ | ||
setProps: PropTypes.func | ||
}; | ||
{%- if cookiecutter.use_async == "True" -%} | ||
{%- include 'cookiecutter_templates/AsyncComponent.react.js' -%} | ||
{%- else -%} | ||
{%- include 'cookiecutter_templates/Component.react.js' -%} | ||
{%- endif -%} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React, {Component} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import {defaultProps, propTypes} from '../components/{{cookiecutter.component_name}}.react'; | ||
|
||
/** | ||
* ExampleComponent is an example component. | ||
* It takes a property, `label`, and | ||
* displays it. | ||
* It renders an input with the property `value` | ||
* which is editable by the user. | ||
*/ | ||
export default class {{cookiecutter.component_name}} extends Component { | ||
render() { | ||
const {id, label, setProps, value} = this.props; | ||
|
||
return ( | ||
<div id={id}> | ||
ExampleComponent: {label} | ||
<input | ||
value={value} | ||
onChange={ | ||
/* | ||
* Send the new value to the parent component. | ||
* setProps is a prop that is automatically supplied | ||
* by dash's front-end ("dash-renderer"). | ||
* In a Dash app, this will update the component's | ||
* props and send the data back to the Python Dash | ||
* app server if a callback uses the modified prop as | ||
* Input or State. | ||
*/ | ||
e => setProps({ value: e.target.value }) | ||
} | ||
/> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
|
||
{{cookiecutter.component_name}}.defaultProps = defaultProps; | ||
{{cookiecutter.component_name}}.propTypes = propTypes; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
const path = require('path'); | ||
const TerserPlugin = require('terser-webpack-plugin'); | ||
const webpack = require('webpack'); | ||
const WebpackDashDynamicImport = require('@plotly/webpack-dash-dynamic-import'); | ||
const packagejson = require('./package.json'); | ||
|
||
const dashLibraryName = packagejson.name.replace(/-/g, '_'); | ||
|
@@ -46,6 +49,7 @@ module.exports = (env, argv) => { | |
entry, | ||
output: { | ||
path: path.resolve(__dirname, dashLibraryName), | ||
chunkFilename: '[name].js', | ||
filename, | ||
library: dashLibraryName, | ||
libraryTarget: 'window', | ||
|
@@ -77,5 +81,43 @@ module.exports = (env, argv) => { | |
}, | ||
], | ||
}, | ||
optimization: { | ||
minimizer: [ | ||
new TerserPlugin({ | ||
sourceMap: true, | ||
parallel: true, | ||
cache: './.build_cache/terser', | ||
terserOptions: { | ||
warnings: false, | ||
ie8: false | ||
} | ||
}) | ||
], | ||
splitChunks: { | ||
name: true, | ||
cacheGroups: { | ||
async: { | ||
chunks: 'async', | ||
minSize: 0, | ||
name(module, chunks, cacheGroupKey) { | ||
return `${cacheGroupKey}-${chunks[0].name}`; | ||
} | ||
}, | ||
shared: { | ||
chunks: 'all', | ||
minSize: 0, | ||
minChunks: 2, | ||
name: '{{cookiecutter.project_shortname}}-shared' | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding the "shared" async chunk for code that repeats across multiple async chunks here would be good as this will have already solved the problem for component developers. https://github.com/plotly/dash-core-components/blob/dev/webpack.config.js#L124 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would still require the developer to add the entry in |
||
} | ||
} | ||
}, | ||
plugins: [ | ||
new WebpackDashDynamicImport(), | ||
new webpack.SourceMapDevToolPlugin({ | ||
filename: '[file].map', | ||
exclude: ['async-plotlyjs'] | ||
}) | ||
] | ||
} | ||
}; |
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 could add but not use the asyncDecorator here (comment out correct usage) like in https://github.com/plotly/dash-table/blob/dev/src/dash-table/dash/DataTable.js#L27 and just leave a comment to use it for components that have side-effects (e.g. prop values calculated from other props or that need to render in order to update/process their props)
For example: https://github.com/plotly/dash-table/blob/dev/src/dash-table/dash/DataTable.js#L27
This is an edge case but not one I'd like component developers to have to find out about. It's not terribly bad either if they do decide to turn it on even if not required.. worst case.