A "React for a Python Developer" guide
See original GitHub issueI think that we should do whatever we can to reduce the barriers of entry in creating custom Dash components. One of the largest barriers to entry is React and JavaScript itself: our community is (by definition) mostly Python developers.
One way that we can help out our community in creating or customizing Dash components is by providing a really friendly, step-by-step guide to creating a Dash component. I’ll call this the “React.js for the Python Developer Guide”.
Below is a rough outline of what I’m imagining. Would love feedback on this!
For Tutorial 1, here’s a rough syllabus that I’ve used in the workshops:
-
Introduction:
- React and JavaScript is easier than it seems, you can do it!
- Most Dash component’s are simple wrappers around existing components. And there are hundreds of thousands of them.
-
Developer Setup
- Installing Node, NPM, and
create-react-app
- In practice, it would be nice to use our
dash-component-archetype
here once we make it better
- In practice, it would be nice to use our
- Installing Node, NPM, and
-
Making your first change
- Opening up
App.js
and changing “React” to “Dash” - Observing the hot-reload
- Making an error and seeing where to find error messages:
throw error
- Doing
console.log
and seeing the data
- Opening up
-
Quick React vs Python:
- React has classes’s like Python classes
- Declare variables in JavaScript:
const
andlet
- JavaScript “objects” are Python’s “dict”
- Except the keys are always strings
console.log
instead ofprint
import
andexport
-
Quick create your own component in the same file as
App.js
:- Create
Header
and include it inApp
:
class Header extends Component { render() { return 'Header' } } class App extends Component { ... render() { ... return <Header/> } }
- Thread a property through:
return <div>{this.props.name}</div>
const myHeaderName = 'Dash Header' return <Header name={myHeaderName}/>
- Add another property like style:
return <div style={this.props.style}>{this.props.name}</div>
const myHeaderName = 'Dash Header'; const style={'color': 'blue', 'fontSize': 20} return <Header name={myHeaderName}/>
- Create
-
Making this component Dash-ready:
- Quick pause: this “component” is hydrated from JavaScript, now let’s make it a standalone Dash component
- Move it to a new file
- Add
propTypes
- Run the toolchain
- Fire up
usage.py
and have:
app.layout = html.Div([ my_component.Header(name='Dash Header', style={'color': 'blue', 'fontSize': 20}) ]) - Isn't that amazing? Dash components are 1-1 with their React components
-
Making it more complex with updates and events in react:
- Making an
Input
component
class TextInput extends Component { render() { return ( <input value={this.props.value}/> ) } }
class App extends Component { render() { const myValue = 'Text input' return <TextInput value={myValue}/> } }
- Inputs are “controlled”:
- Note how you can’t enter any text in that input. It’s
value
is fixed. - Note how if you remove
value={...}
, then you could type into it - So, we need a way to handle keypresses and feed those values back into the input
- Note how you can’t enter any text in that input. It’s
- Adding an onChange:
<input value={this.props.value} onChange={(e) => { console.log(e.target.value) }}/>
- Woah, that
(e) => {...}
thing? That’s basically like a Pythonlambda
function. Inline, anonymous function. e.target.value
? That’s just “something you know”, it’s the standard syntax for input events with JavaScript
- Woah, that
- Start typing and check out your console. Letters!
- Adding state:
class TextInput extends Component { constructor() { this.state = { value: 'test' } } render() { return ( <input value={this.state.value}/> ) } }
this.state
is a special react class property. We refer to it withthis
, just like as in python classes- React provides a special way to update
this.state
:this.setState
. When you callthis.setState
, it will call therender
component automatically to “rerender” your component. Let’s try it:
class TextInput extends Component { constructor() { this.state = { value: 'test' } } render() { console.log('rendering') return ( <input value={this.props.value} onChange={e => { console.log(e.target.value); this.setState({value: e.target.value}); }} /> ) } }
- Now type: notice how we’re just renderering and rendering our component. And notice how our input is now getting updated!
- Now, let’s move the state from
TextInput
toApp
:
class App extends Component { constructor() { this.state = { value: 'test' } } render () { console.log('rendering App') return ( <div> <TextInput value={this.state.value} updateValue={newValue => this.setState({value: newValue})} /> </div> ); } } class TextInput extends Component { render() { console.log('rendering TextInput') return ( <input value={this.props.value} onChange={e => { console.log(e.target.value); this.props.updateValue(e.target.value) }} /> ) } }
- data is flowing in a big loop! From the input up to the app, re-rendering the app and re-rendering the
TextInput
with the new value.
- Making an
-
Now here’s the kicker.
App
? That’s basicallydash-renderer
. It’s rendering all of the components and it’s providing the data down into the components as props. It also provides anupdateValue
-like prop, except it’s calledsetProps
and it takes an object. So, to make this Dash-ready, let’s:- Move
TextInput
into it’s own file - Add
propTypes
- Rename
updateValue
tosetProps
and update the call signature tosetProps({value: e.target.value})
- Run the toolchain
- Add it to
usage.py
and create a callbackvalue
is now a property that we “listen” to as acallback
Input
- Move
-
Now, in practice we won’t create our own components. We’ll frequently just provide wrappers around existing components. Let’s try it out with the react colorpicker:
npm install react-colorpicker
npm
is thepip
for node
import ColorPicker from 'react-colorpicker'
- Add
this.setProps
- Add propetypes
- Run the toolchain
- Magic
-
Advanced:
- If the component’s properties don’t update with callbacks, then it can manage its own state completely. This will be a quite a bit faster as the dash-renderer re-render cycle can take a moment or two.
- You can check if a component can manage its own state by seeing if
this.props.setProps
is defined or not. - For an example, see the
Input
component indash-core-components
source
- You can check if a component can manage its own state by seeing if
- We use Ramda as our “standard library”, here’s the docs: https://ramdajs.com/
- Read the source for the existing components in
dash-core-components
- Subscribe to this issue to be aware of breaking changes in the components: https://github.com/plotly/dash-components-archetype/issues/40
- If the component’s properties don’t update with callbacks, then it can manage its own state completely. This will be a quite a bit faster as the dash-renderer re-render cycle can take a moment or two.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:9
- Comments:17 (12 by maintainers)
Top GitHub Comments
@AmirAlavi - Definitely!
For what its worth, we have a new version that should make this easier: https://github.com/plotly/dash-component-boilerplate. With the new version, you shoulnd’t need to worry about webpack, dev vs normal, hot reloading, etc. You should just be able to follow the new instructions.
For external dependencies, in a nutshell we need a section that explains that:
is the equivalent of
pip install
and thatnode_modules
folder is basically like a virtualenv folder: local installations.Also, an explanation of how
import
andexport
work and how it’s slightly different than pythonAs a mostly Python developer, another major issue I’ve run into besides ReactJS is the javascript toolchain that is required for building a custom Dash component. It’s something you can ignore if you aren’t using external libraries, but as soon as you need to add a dependency, things get confusing. I’ve found it so difficult to understand what’s happening with webpack, webpack.config files, bundle.js, “hot loading”, dev vs “normal” configurations, etc.
Honestly, this has personally been my greatest barrier to creating custom dash components, not learning ReactJS (as someone mentioned above, there are many great tutorials on this from Facebook or even codecademy).
Is there any room for this in the tutorial? I’m thinking something different than the wrapper around react-colorpicker from above. That is already a react component, that doesn’t really require external JS libraries/dependencies, and so doesn’t really touch on toolchain issues.