this.changeContent needs to be bound to the component instance via this.changeContent.bind(this) before being passed as the onChange prop, otherwise the this variable in the body of the function will not refer to the component instance but to window. See Function::bind. This issue is happening because this.changeContent and onClick={this.sendContent} are not bound to this of the instance of the component . Refs are set on the component instance and not on React.refs: you need to change React.refs.someref to this.refs.someref. You'll also need to bind the sendContent method to the component instance so that this refers to it.
When using React.createClass
instead of ES6 classes, every non-lifecycle method defined on a component is automatically bound to the component instance. See Autobinding.
Be aware that binding a function creates a new function. You can either bind it directly in render, which means a new function will be created every time the component renders, or bind it in your constructor, which will only fire once.
constructor() {
this.changeContent = this.changeContent.bind(this);
}
vs
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
Morhaus is correct, but this can be solved without bind
.
You can use an arrow function together with the class properties proposal:
class SomeClass extends React.Component {
changeContent = (e) => {
this.setState({inputContent: e.target.value})
}
render() {
return <input type="text" onChange={this.changeContent} />;
}
}
I am writing a simple component in ES6 (with BabelJS), and functions this.setState is not working. Because the arrow function is declared in the scope of the constructor, and because arrow functions maintain this from their declaring scope, it all works. The downside here is that these wont be functions on the prototype, they will all be recreated with each component. However, this isn't much of a downside since bind results in the same thing. You can use an arrow function together with the class properties proposal: How to solve this error?
Typical errors include something like
Cannot read property 'setState' of undefined
or
this.setState is not a
function
Here is the code:
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}
export default SomeClass
React doesn’t need error boundaries to recover from errors in event handlers. Unlike the render method and lifecycle methods, the event handlers don’t happen during rendering. So if they throw, React still knows what to display on the screen. React 15 included a very limited support for error boundaries under a different method name: unstable_handleError. This method no longer works, and you will need to change it to componentDidCatch in your code starting from the first 16 beta release. Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; }
componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service logErrorToMyService(error, errorInfo); }
render() {
if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; }
return this.props.children;
}
}
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
try {
showButton();
} catch (error) {
// ...
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
try { // Do something that could throw } catch (error) { this.setState({ error }); } }
render() {
if (this.state.error) { return <h1>Caught an error.</h1> } return <button onClick={this.handleClick}>Click Me</button> }
}
Starting with React 17, the onScroll event does not bubble in React. This matches the browser behavior and prevents the confusion when a nested scrollable element fires events on a distant parent. The onPointerEnter and onPointerLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase. The onMouseEnter and onMouseLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist()
DOMEventTarget target
number timeStamp
string type
onCopy onCut onPaste
DOMDataTransfer clipboardData
string data
onKeyDown onKeyPress onKeyUp
We will attach a ref to the root DOM element. Inside componentDidMount, we will get a reference to it so we can pass it to the jQuery plugin. We won’t pass this.props.onChange directly to Chosen because component’s props might change over time, and that includes event handlers. Instead, we will declare a handleChange() method that calls this.props.onChange, and subscribe it to the jQuery change event: It is important that we also call root.unmount() in the remove method so that React unregisters event handlers and other resources associated with the component tree when it is detached.
class SomePlugin extends React.Component {
componentDidMount() {
this.$el = $(this.el); this.$el.somePlugin(); }
componentWillUnmount() {
this.$el.somePlugin('destroy'); }
render() {
return <div ref={el => this.el = el} />; }
}
function Example() {
return (
<Chosen onChange={value => console.log(value)}>
<option>vanilla</option>
<option>chocolate</option>
<option>strawberry</option>
</Chosen>
);
}
class Chosen extends React.Component {
render() {
return (
<div> <select className="Chosen-select" ref={el => this.el = el}> {this.props.children}
</select>
</div>
);
}
}
<select className="Chosen-select" ref={el => this.el = el}>
componentDidMount() {
this.$el = $(this.el);
this.$el.chosen();
this.handleChange = this.handleChange.bind(this);
this.$el.on('change', this.handleChange);
}
componentWillUnmount() {
this.$el.off('change', this.handleChange);
this.$el.chosen('destroy');
}
handleChange(e) {
this.props.onChange(e.target.value);
}
Conceptually, you can think of refs as similar to instance variables in a class. Unless you’re doing lazy initialization, avoid setting refs during rendering — this can lead to surprising behavior. Instead, typically you want to modify refs in event handlers and effects. Any function inside a component, including event handlers and effects, “sees” the props and state from the render it was created in. For example, consider code like this: You can try moving that function outside of your component. In that case, the function is guaranteed to not reference any props or state, and also doesn’t need to be in the list of dependencies.
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import { act } from 'react-dom/test-utils';import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and effect
act(() => { ReactDOM.createRoot(container).render(<Counter />); }); const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and effect
act(() => { button.dispatchEvent(new MouseEvent('click', {bubbles: true})); }); expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
function Box() {
const [state, setState] = useState({
left: 0,
top: 0,
width: 100,
height: 100
});
// ...
}
// ...
useEffect(() => {
function handleWindowMouseMove(e) {
// Spreading "...state" ensures we don't "lose" width and height setState(state => ({ ...state, left: e.pageX, top: e.pageY })); }
// Note: this implementation is a bit simplified
window.addEventListener('mousemove', handleWindowMouseMove);
return () => window.removeEventListener('mousemove', handleWindowMouseMove);
}, []);
// ...
However, you have to be mindful about when you set the event listener in your code. You can’t, for example, add an event listener at the top of your component code, because then every time something changed, the component would re-render and add a new event listener. Since your component will likely re-render many times, that would create a lot of unused event listeners that take up memory. Now you have the tools to add and remove a global event listener safely by using useEffect to add the event listener whenever alert is true and remove it whenever alert is false. In the next step, you’ll add global event listeners to the Window object to capture events that occur outside the immediate component.
To demonstrate this, you will start by making your validating input. First, you will create a component called FileNamer
. This will be a <form>
element with an input for naming a file. As you fill in the input, you’ll see the information update a preview box above the component. The component will also include a submit button to run the validation, but for this example the form will not actually submit anything.
First, create the directory:
mkdir src / components / FileNamer
Then open FileNamer.js
in your text editor:
nano src / components / FileNamer / FileNamer.js
Inside FileNamer.js
, create a wrapper <div>
, then add another <div>
with a class name of preview
and a <form>
element inside the wrapper by writing the following lines of code:
import React from 'react';
export default function FileNamer() {
return(
<div className="wrapper">
<div className="preview">
</div>
<form>
</form>
</div>
)
}
Save and close the file.
Next, open App.js
:
nano src / components / App / App.js
Import FileNamer
, then render inside the App
function by adding the following highlighted lines:
import React from 'react';
import FileNamer from '../FileNamer/FileNamer';
function App() {
return <FileNamer />
}
export default App;
In React, we write event handlers directly on the elements in our JSX, like this: In the above example, we're adding an onClick attribute to the <button> element. The value of that attribute is a function that triggers a simple alert. The onClick attribute has special meaning here: it tells React to run a given function when the user clicks on the button. There are a couple of other things to note: Note: This may seem counter-intuitive regarding best-practice advice that tends to advise against use of inline event handlers on HTML, but remember that JSX is actually part of your JavaScript.
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
alert("hi!");
});
<button
type="button"
onClick={() => alert("hi!")}
>
Say hi!
</button>
function handleSubmit(e) {
e.preventDefault();
alert('Hello, world!');
}
function addTask(name) {
alert(name);
}
<Form addTask={addTask} />
Now look at each of these calls to removeEventListener() in turn. Any of them in which capture or useCapture is true fail; all others succeed. capture: A boolean value that specifies whether the event listener to be removed is registered as a capturing listener or not. If this parameter is absent, the default value false is assumed. A boolean value that specifies whether the event listener to be removed is registered as a capturing listener or not. If this parameter is absent, the default value false is assumed.
removeEventListener(type, listener) removeEventListener(type, listener, options) removeEventListener(type, listener, useCapture)
element.addEventListener("mousedown", handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, false); // Fails
element.removeEventListener("mousedown", handleMouseDown, true); // Succeeds
element.removeEventListener("mousedown", handleMouseDown, {
passive: true
}); // Succeeds
element.removeEventListener("mousedown", handleMouseDown, {
capture: false
}); // Succeeds
element.removeEventListener("mousedown", handleMouseDown, {
capture: true
}); // Fails
element.removeEventListener("mousedown", handleMouseDown, {
passive: false
}); // Succeeds
element.removeEventListener("mousedown", handleMouseDown, false); // Succeeds
element.removeEventListener("mousedown", handleMouseDown, true); // Fails
const body = document.querySelector('body')
const clickTarget = document.getElementById('click-target')
const mouseOverTarget = document.getElementById('mouse-over-target')
let toggle = false;
function makeBackgroundYellow() {
body.style.backgroundColor = toggle ? 'white' : 'yellow';
toggle = !toggle;
}
clickTarget.addEventListener('click',
makeBackgroundYellow,
false
);
mouseOverTarget.addEventListener('mouseover', () => {
clickTarget.removeEventListener('click',
makeBackgroundYellow,
false
);
});