Skip to content
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

Move 'Handling Events' example from Codepen to CodeSandbox #2378

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 5 additions & 78 deletions content/docs/handling-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,104 +39,31 @@ Another difference is that you cannot return `false` to prevent default behavior

In React, this could instead be:

```js{2-5,8}
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
```
`embed:handling-events/action-link-example.js`

Here, `e` is a synthetic event. React defines these synthetic events according to the [W3C spec](https://www.w3.org/TR/DOM-Level-3-Events/), so you don't need to worry about cross-browser compatibility. See the [`SyntheticEvent`](/docs/events.html) reference guide to learn more.

When using React you should generally not need to call `addEventListener` to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.

When you define a component using an [ES6 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes), a common pattern is for an event handler to be a method on the class. For example, this `Toggle` component renders a button that lets the user toggle between "ON" and "OFF" states:

```js{6,7,10-14,18}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
```
`embed:handling-events/toggle-example.js`

[**Try it on CodePen**](https://codepen.io/gaearon/pen/xEmzGg?editors=0010)
**[Try it on CodeSandbox](codesandbox://handling-events/toggle-example,handling-events/toggle-example.css)**

You have to be careful about the meaning of `this` in JSX callbacks. In JavaScript, class methods are not [bound](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind) by default. If you forget to bind `this.handleClick` and pass it to `onClick`, `this` will be `undefined` when the function is actually called.

This is not React-specific behavior; it is a part of [how functions work in JavaScript](https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/). Generally, if you refer to a method without `()` after it, such as `onClick={this.handleClick}`, you should bind that method.

If calling `bind` annoys you, there are two ways you can get around this. If you are using the experimental [public class fields syntax](https://babeljs.io/docs/plugins/transform-class-properties/), you can use class fields to correctly bind callbacks:

```js{2-6}
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}

render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
```
`embed:handling-events/login-button-public-class-field-example.js`

This syntax is enabled by default in [Create React App](https://github.com/facebookincubator/create-react-app).

If you aren't using class fields syntax, you can use an [arrow function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) in the callback:

```js{7-9}
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}

render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
```
`embed:handling-events/login-button-arrow-function.js`

The problem with this syntax is that a different callback is created each time the `LoggingButton` renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.

Expand Down
13 changes: 13 additions & 0 deletions examples/handling-events/action-link-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// highlight-range{2-5,8}
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
15 changes: 15 additions & 0 deletions examples/handling-events/login-button-arrow-function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// highlight-range{7-9}
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}

render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={e => this.handleClick(e)}>
Click me
</button>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// highlight-range{2-6}
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
};

render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
3 changes: 3 additions & 0 deletions examples/handling-events/toggle-example.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
padding: 5px
}
34 changes: 34 additions & 0 deletions examples/handling-events/toggle-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* hide-range{1-4} */
// highlight-range{10,11,14-18,22}
import React from 'react';
import ReactDOM from 'react-dom';
import './toggle-example.css';

class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn,
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
"gatsby-plugin-twitter": "^2.0.0",
"gatsby-remark-code-repls": "^2.0.0",
"gatsby-remark-copy-linked-files": "^2.0.0",
"gatsby-remark-embed-snippet": "^3.0.0",
"gatsby-remark-embed-snippet": "4.1.6",
"gatsby-remark-external-links": "^0.0.4",
"gatsby-remark-images": "^2.0.0",
"gatsby-remark-prismjs": "^3.0.2",
"gatsby-remark-prismjs": "3.3.12",
"gatsby-remark-responsive-iframe": "^2.0.0",
"gatsby-remark-smartypants": "^2.0.0",
"gatsby-source-filesystem": "^2.0.0",
Expand Down
50 changes: 38 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,13 @@
dependencies:
regenerator-runtime "^0.12.0"

"@babel/runtime@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
dependencies:
regenerator-runtime "^0.13.2"

"@babel/[email protected]":
version "7.0.0-beta.44"
resolved "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
Expand Down Expand Up @@ -5489,15 +5496,15 @@ gatsby-remark-copy-linked-files@^2.0.0:
probe-image-size "^4.0.0"
unist-util-visit "^1.3.0"

gatsby-remark-embed-snippet@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gatsby-remark-embed-snippet/-/gatsby-remark-embed-snippet-3.0.0.tgz#9ed032d51274bbabe7307e6e03879cd49941818f"
integrity sha512-vqxx15/J+AylOvpVAfKhYDr/TGGnJ7DNx3ZdukEaQrT/aGc+/8ztfir61+gsHIFa8qIpmpoTpXvUAEbcK0lzIg==
gatsby-remark-embed-snippet@4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/gatsby-remark-embed-snippet/-/gatsby-remark-embed-snippet-4.1.6.tgz#c7a6a9b6f6724303528303274ef8a9ce19e30bc1"
integrity sha512-37618jCRC7iDpPxEkhieDUWHDAMaNT+unyV7pqUro3+zN6nvUeDzwWkCFQzg1QcAWv/uSqlDkIBrfm5wxcR92w==
dependencies:
"@babel/runtime" "^7.0.0"
"@babel/runtime" "^7.5.5"
normalize-path "^2.1.1"
parse-numeric-range "^0.0.2"
unist-util-map "^1.0.3"
unist-util-map "^1.0.5"

gatsby-remark-external-links@^0.0.4:
version "0.0.4"
Expand All @@ -5521,14 +5528,14 @@ gatsby-remark-images@^2.0.0:
unist-util-select "^1.5.0"
unist-util-visit-parents "^2.0.1"

gatsby-remark-prismjs@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.0.2.tgz#38a67c4e020ac26102e850ccefbe8b0e696501dc"
integrity sha512-tjgGzDVkDX3EtBgKI8uNxArNjLF97M3ipMzh1PLGtIn1naVrixw36xV7s6BkiZigdpybj9rjF7mvBKPtZekh2w==
gatsby-remark-prismjs@3.3.12:
version "3.3.12"
resolved "https://registry.yarnpkg.com/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.3.12.tgz#4239bb48a5e2eb5fcff5adbbac1a6821012bd9bf"
integrity sha512-PE744uWg0M09hFZSOpQac+oUpmwKHAYJc0unLIszNdXvbihPL8rhi9aahKiz3EvQDbQ35WNHHPOsS/OjtegmIw==
dependencies:
"@babel/runtime" "^7.0.0"
"@babel/runtime" "^7.5.5"
parse-numeric-range "^0.0.2"
unist-util-visit "^1.3.0"
unist-util-visit "^1.4.1"

gatsby-remark-responsive-iframe@^2.0.0:
version "2.0.5"
Expand Down Expand Up @@ -10491,6 +10498,11 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==

regenerator-runtime@^0.13.2:
version "0.13.3"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==

regenerator-transform@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb"
Expand Down Expand Up @@ -12487,6 +12499,13 @@ unist-util-map@^1.0.3:
dependencies:
object-assign "^4.0.1"

unist-util-map@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/unist-util-map/-/unist-util-map-1.0.5.tgz#701069b72e1d1cc02db265502a5e82b77c2eb8b7"
integrity sha512-dFil/AN6vqhnQWNCZk0GF/G3+Q5YwsB+PqjnzvpO2wzdRtUJ1E8PN+XRE/PRr/G3FzKjRTJU0haqE0Ekl+O3Ag==
dependencies:
object-assign "^4.0.1"

unist-util-modify-children@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz#c7f1b91712554ee59c47a05b551ed3e052a4e2d1"
Expand Down Expand Up @@ -12539,6 +12558,13 @@ unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.3, unist
dependencies:
unist-util-visit-parents "^2.0.0"

unist-util-visit@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3"
integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==
dependencies:
unist-util-visit-parents "^2.0.0"

universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
Expand Down