{"action":"created","issue":{"url":"https://api.github.com/repos/plotly/dash-docs/issues/115","repository_url":"https://api.github.com/repos/plotly/dash-docs","labels_url":"https://api.github.com/repos/plotly/dash-docs/issues/115/labels{/name}","comments_url":"https://api.github.com/repos/plotly/dash-docs/issues/115/comments","events_url":"https://api.github.com/repos/plotly/dash-docs/issues/115/events","html_url":"https://github.com/plotly/dash-docs/issues/115","id":339622471,"node_id":"MDU6SXNzdWUzMzk2MjI0NzE=","number":115,"title":"A \"React for a Python Developer\" guide","user":{"login":"chriddyp","id":1280389,"node_id":"MDQ6VXNlcjEyODAzODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1280389?v=4","gravatar_id":"","url":"https://api.github.com/users/chriddyp","html_url":"https://github.com/chriddyp","followers_url":"https://api.github.com/users/chriddyp/followers","following_url":"https://api.github.com/users/chriddyp/following{/other_user}","gists_url":"https://api.github.com/users/chriddyp/gists{/gist_id}","starred_url":"https://api.github.com/users/chriddyp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chriddyp/subscriptions","organizations_url":"https://api.github.com/users/chriddyp/orgs","repos_url":"https://api.github.com/users/chriddyp/repos","events_url":"https://api.github.com/users/chriddyp/events{/privacy}","received_events_url":"https://api.github.com/users/chriddyp/received_events","type":"User","site_admin":false},"labels":[{"id":989674897,"node_id":"MDU6TGFiZWw5ODk2NzQ4OTc=","url":"https://api.github.com/repos/plotly/dash-docs/labels/sponsored","name":"sponsored","color":"aff99d","default":false}],"state":"open","locked":false,"assignee":{"login":"valentijnnieman","id":6509622,"node_id":"MDQ6VXNlcjY1MDk2MjI=","avatar_url":"https://avatars2.githubusercontent.com/u/6509622?v=4","gravatar_id":"","url":"https://api.github.com/users/valentijnnieman","html_url":"https://github.com/valentijnnieman","followers_url":"https://api.github.com/users/valentijnnieman/followers","following_url":"https://api.github.com/users/valentijnnieman/following{/other_user}","gists_url":"https://api.github.com/users/valentijnnieman/gists{/gist_id}","starred_url":"https://api.github.com/users/valentijnnieman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/valentijnnieman/subscriptions","organizations_url":"https://api.github.com/users/valentijnnieman/orgs","repos_url":"https://api.github.com/users/valentijnnieman/repos","events_url":"https://api.github.com/users/valentijnnieman/events{/privacy}","received_events_url":"https://api.github.com/users/valentijnnieman/received_events","type":"User","site_admin":false},"assignees":[{"login":"valentijnnieman","id":6509622,"node_id":"MDQ6VXNlcjY1MDk2MjI=","avatar_url":"https://avatars2.githubusercontent.com/u/6509622?v=4","gravatar_id":"","url":"https://api.github.com/users/valentijnnieman","html_url":"https://github.com/valentijnnieman","followers_url":"https://api.github.com/users/valentijnnieman/followers","following_url":"https://api.github.com/users/valentijnnieman/following{/other_user}","gists_url":"https://api.github.com/users/valentijnnieman/gists{/gist_id}","starred_url":"https://api.github.com/users/valentijnnieman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/valentijnnieman/subscriptions","organizations_url":"https://api.github.com/users/valentijnnieman/orgs","repos_url":"https://api.github.com/users/valentijnnieman/repos","events_url":"https://api.github.com/users/valentijnnieman/events{/privacy}","received_events_url":"https://api.github.com/users/valentijnnieman/received_events","type":"User","site_admin":false}],"milestone":null,"comments":12,"created_at":"2018-07-09T22:01:09Z","updated_at":"2018-08-10T14:47:27Z","closed_at":null,"author_association":"MEMBER","body":"I 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.\r\n\r\nOne 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\".\r\n\r\nBelow is a rough outline of what I'm imagining. Would love feedback on this!\r\n\r\n***\r\n\r\n\r\nFor Tutorial 1, here's a rough syllabus that I've used in the workshops:\r\n1. Introduction:\r\n    - React and JavaScript is easier than it seems, you can do it!\r\n    - Most Dash component's are simple wrappers around existing components. And there are hundreds of thousands of them.\r\n2. Developer Setup\r\n    - Installing Node, NPM, and `create-react-app`\r\n        - In practice, it would be nice to use our `dash-component-archetype` here once we make it better\r\n3. Making your first change\r\n    - Opening up `App.js` and changing \"React\" to \"Dash\"\r\n    - Observing the hot-reload\r\n    - Making an error and seeing where to find error messages: `throw error`\r\n    - Doing `console.log` and seeing the data\r\n4. Quick React vs Python:\r\n    - React has classes's like Python classes\r\n    - Declare variables in JavaScript: `const` and `let`\r\n    - JavaScript \"objects\" are Python's \"dict\"\r\n        - Except the keys are always strings\r\n    - `console.log` instead of `print`\r\n    - `import` and `export`\r\n5. Quick create your own component in the same file as `App.js`:\r\n    1. Create `Header` and include it in `App`:\r\n    ```\r\n    class Header extends Component {\r\n        render() {\r\n            return 'Header'\r\n        }\r\n    }\r\n\r\n    class App extends Component {\r\n        ...\r\n\r\n        render() {\r\n            ...\r\n            return <Header/>\r\n        }\r\n    }\r\n    ```\r\n\r\n    2. Thread a property through:\r\n    ```\r\n        return <div>{this.props.name}</div>\r\n    ```\r\n\r\n    ```\r\n        const myHeaderName = 'Dash Header'\r\n        return <Header name={myHeaderName}/>\r\n    ```\r\n\r\n    3. Add another property like style:\r\n    ```\r\n        return <div style={this.props.style}>{this.props.name}</div>\r\n    ```\r\n\r\n    ```\r\n        const myHeaderName = 'Dash Header';\r\n        const style={'color': 'blue', 'fontSize': 20}\r\n        return <Header name={myHeaderName}/>\r\n    ```\r\n6. Making this component Dash-ready:\r\n    - Quick pause: this \"component\" is hydrated from JavaScript, now let's make it a standalone Dash component\r\n    - Move it to a new file\r\n    - Add `propTypes`\r\n    - Run the toolchain\r\n    - Fire up `usage.py` and have:\r\n    ```\r\n    app.layout = html.Div([\r\n        my_component.Header(name='Dash Header', style={'color': 'blue', 'fontSize': 20})\r\n    ])\r\n    - Isn't that amazing? Dash components are 1-1 with their React components\r\n7. Making it more complex with updates and events in react:\r\n    - Making an `Input` component\r\n    ```\r\n    class TextInput extends Component {\r\n        render() {\r\n            return (\r\n                <input value={this.props.value}/>\r\n            )\r\n        }\r\n    }\r\n    ```\r\n\r\n    ```\r\n    class App extends Component {\r\n        render() {\r\n            const myValue = 'Text input'\r\n            return <TextInput value={myValue}/>\r\n        }\r\n    }\r\n    ```\r\n    - Inputs are \"controlled\":\r\n        - Note how you can't enter any text in that input. It's `value` is fixed.\r\n        - Note how if you remove `value={...}`, then you could type into it\r\n        - So, we need a way to handle keypresses and feed those values back into the input\r\n    - Adding an onChange:\r\n        ```\r\n        <input value={this.props.value} onChange={(e) => {\r\n            console.log(e.target.value)\r\n        }}/>\r\n        ```\r\n        - Woah, that `(e) => {...}` thing? That's basically like a Python `lambda` function. Inline, anonymous function.\r\n        - `e.target.value`? That's just \"something you know\", it's the standard syntax for input events with JavaScript\r\n    - Start typing and check out your console. Letters!\r\n    - Adding state:\r\n    ```\r\n    class TextInput extends Component {\r\n        constructor() {\r\n            this.state = {\r\n                value: 'test'\r\n            }\r\n        }\r\n        render() {\r\n            return (\r\n                <input value={this.state.value}/>\r\n            )\r\n        }\r\n    }\r\n    ```\r\n    - `this.state` is a special react class property. We refer to it with `this`, just like as in python classes\r\n    - React provides a special way to update `this.state`: `this.setState`. When you call `this.setState`, it will call the `render` component automatically to \"rerender\" your component. Let's try it:\r\n    ```\r\n    class TextInput extends Component {\r\n        constructor() {\r\n            this.state = {\r\n                value: 'test'\r\n            }\r\n        }\r\n\r\n        render() {\r\n            console.log('rendering')\r\n            return (\r\n                <input \r\n                    value={this.props.value}\r\n                    onChange={e => {\r\n                        console.log(e.target.value);\r\n                        this.setState({value: e.target.value});\r\n                    }}\r\n                />\r\n            )\r\n        }\r\n\r\n    }\r\n    ```\r\n    - Now type: notice how we're just renderering and rendering our component. And notice how our input is now getting updated!\r\n    - Now, let's move the state from `TextInput` to `App`:\r\n    ```\r\n    class App extends Component {\r\n        constructor() {\r\n            this.state = {\r\n                value: 'test'\r\n            }\r\n        }\r\n        render () {\r\n            console.log('rendering App')\r\n            return (\r\n                <div>\r\n                    <TextInput\r\n                        value={this.state.value}\r\n                        updateValue={newValue => this.setState({value: newValue})}\r\n                    />\r\n                </div>\r\n            );\r\n        }\r\n    }\r\n\r\n    class TextInput extends Component {\r\n        render() {\r\n            console.log('rendering TextInput')\r\n            return (\r\n                <input \r\n                    value={this.props.value}\r\n                    onChange={e => {\r\n                        console.log(e.target.value);\r\n                        this.props.updateValue(e.target.value)\r\n                    }}\r\n                />\r\n            )\r\n        }\r\n    }\r\n    ```\r\n    - 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.\r\n8. Now here's the kicker. `App`? That's basically `dash-renderer`. It's rendering all of the components and it's providing the data down into the components as props. It also provides an `updateValue`-like prop, except it's called `setProps` and it takes an object. So, to make this Dash-ready, let's:\r\n    - Move `TextInput` into it's own file\r\n    - Add `propTypes`\r\n    - Rename `updateValue` to `setProps` and update the call signature to `setProps({value: e.target.value})`\r\n    - Run the toolchain\r\n    - Add it to `usage.py` and create a callback\r\n        - `value` is now a property that we \"listen\" to as a `callback` `Input`\r\n9. 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:\r\n    - `npm install react-colorpicker`\r\n        - `npm` is the `pip` for node\r\n    - `import ColorPicker from 'react-colorpicker'`\r\n    - Add `this.setProps`\r\n    - Add propetypes\r\n    - Run the toolchain\r\n    - Magic\r\n10. Advanced:\r\n    - 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. \r\n        - You can check if a component can manage its own state by seeing if `this.props.setProps` is defined or not.\r\n        - For an example, see the `Input` component in `dash-core-components` source\r\n    - We use Ramda as our \"standard library\", here's the docs: https://ramdajs.com/\r\n    - Read the source for the existing components in `dash-core-components`\r\n    - Subscribe to this issue to be aware of breaking changes in the components: https://github.com/plotly/dash-components-archetype/issues/40\r\n"},"comment":{"url":"https://api.github.com/repos/plotly/dash-docs/issues/comments/412104964","html_url":"https://github.com/plotly/dash-docs/issues/115#issuecomment-412104964","issue_url":"https://api.github.com/repos/plotly/dash-docs/issues/115","id":412104964,"node_id":"MDEyOklzc3VlQ29tbWVudDQxMjEwNDk2NA==","user":{"login":"chriddyp","id":1280389,"node_id":"MDQ6VXNlcjEyODAzODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1280389?v=4","gravatar_id":"","url":"https://api.github.com/users/chriddyp","html_url":"https://github.com/chriddyp","followers_url":"https://api.github.com/users/chriddyp/followers","following_url":"https://api.github.com/users/chriddyp/following{/other_user}","gists_url":"https://api.github.com/users/chriddyp/gists{/gist_id}","starred_url":"https://api.github.com/users/chriddyp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chriddyp/subscriptions","organizations_url":"https://api.github.com/users/chriddyp/orgs","repos_url":"https://api.github.com/users/chriddyp/repos","events_url":"https://api.github.com/users/chriddyp/events{/privacy}","received_events_url":"https://api.github.com/users/chriddyp/received_events","type":"User","site_admin":false},"created_at":"2018-08-10T14:47:27Z","updated_at":"2018-08-10T14:47:27Z","author_association":"MEMBER","body":"@AmirAlavi - Definitely!\r\n\r\nFor 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.\r\n\r\nFor external dependencies, in a nutshell we need a section that explains that:\r\n```\r\nnpm install some-package --save\r\n```\r\nis the equivalent of `pip install` and that `node_modules` folder is basically like a virtualenv folder: local installations.\r\n\r\nAlso, an explanation of how `import` and `export` work and how it's slightly different than python"}}
{"actor":{"display_login":"chriddyp"}}