import React from 'react';
import ChildComponent from './ChildComponent';
class SampleComponent extends React.Component {
sampleCallbackOne = () => {
// does something
};
sampleCallbackTwo = () => {
// does something
};
render() {
return (
<div>
<ChildComponent
propOne={this.sampleCallbackOne}
propTwo={() => this.sampleCallbackTwo()}
/>
</div>
);
}
}
export default SampleComponent;
In this example, I have an onClick event that I am handling and saw that I can successfully pass this into the props of the component in two ways.
I was wondering what exactly the difference is in both ways since they appear to function in the same manner?
Why do both ways work?
It is a common point that seems weird.
Refer details in document of handling-events
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
handleClick() {
console.log('this is:', this);
}
<button onClick={this.handleClick}>
If you don't add () behind this.handleClick, you need to bind this in your constructor, otherwise, you may want to use the next two methods:
A. public class field syntax
which is enabled by default in Create React App
handleClick = () => {
console.log('this is:', this);
}
<button onClick={this.handleClick}>
B. arrow functions
which may cause performance problems and is not recommended, refer to the document above.
// The same on event handling but different in:
<button
onClick={(e) => this.deleteRow(id, e)} // automatically forwarded, implicitly
/>
<button
onClick={this.deleteRow.bind(this, id)} // explicitly
/>
Sample
Basically in our practice, we use public class field syntax with params which would look like below:
// No need to bind `this` in constructor
// Receiving params passed by elements as well as getting events of it
handler = (value: ValueType) => (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
// Do something with passed `value` and acquired `event`
}
<NumberFormat
...
onBlur={this.handler(someValue)} // Passing necessary params here
/>
We can share the handler function by passing different params to it.
// Justify via keyword of stored content in flat data structure
handler = (value: string) => (event: React.ChangeEvent<HTMLInputElement>, id: ValidationItems) => {
// Do something with
// passed `value`,
// acquired `event`,
// for each element diffenced via `id`
};
<YourComponent
id="ID_1"
value={store.name1}
onChange={this.handler("name1")}
/>;
<YourComponent
id="ID_2"
value={store.name2}
onChange={this.handler("name2")}
/>;
// ... more similar input text fields
<ChildComponent
propOne={this.sampleCallbackOne}
propTwo={() => this.sampleCallbackTwo()}
/>
for propOne: here you are passing the reference of sampleCallbackOne.
for propTwo: you are wrapping your sampleCallbackTwo in another function.
In both the case you will get the same results
Related
Im doing ReactJS course in Codeacademny and they confused me.
(EDIT - full code) Photo of the code :
and there's no constructor or anywhere call to any bind method for the scream class method.
However in further exercises they tell you can't do that.
I probably miss something.
Apparently this.scream is an arrow function. Arrow function does not require binding. It points to the right context by default.
scream = () => { ... }
and there's no constructor or anywhere call to any bind method for the scream class method.
You only have to bind this to the component instance when the method actually uses this internally.
That's not the case in your example, so there is no need to bind it. No matter how the method is executed, it will always produce the same output.
Here is an example without React to demonstrate the difference:
var obj = {
value: 42,
method1() { // doesn't use `this`
console.log("yey!");
},
method2() { // uses `this`
console.log(this.value);
},
};
obj.method1(); // works
obj.method2(); // works
var m1 = obj.method1;
var m2 = obj.method2;
m1(); // works
m2(); // BROKEN!
var m2bound = obj.method2.bind(obj);
m2bound(); // works
scream = () => { ... }
render() {
return <button onClick={()=>this.scream()}>AAAAAH!</button>;
}
ou have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound 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. Generally, if you refer to a method without () after it, such as
onClick={this.handleClick}, you should bind that method.
When you define a component using an ES6 class, 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:
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')
);```
You can simply use an arrow function (no need to bind in constructor).
scream = () => { console.log('Here') }
render() {
return <button onClick={this.scream}>AAAAAH!</button>;
}
Or you can call this function inline by.
render() {
return <button onClick={() => console.log('Here')}>AAAAAH!</button>;
}
You should use arrow functions for event handling to bind the function to the object. Other solution is to auto bind each function in the constructor like :
class Test{
constructor(){
Object.getOwnPropertyNames(Test.prototype).forEach(
method => this[method] = this[method].bind(this));
}
Read about #AutoBind decorator for more details.
I've imported a custom component into my screen and rendered it in the render() function. Then, created a ref to that custom component. Now, the render() function simply looks like this.
render() {
return (
<View>
<MyComponent ref={component => this.myComponent = component} />
</View>
)
}
Then, I've created another function to access the state of my custom component. I wrote it like this.
myFunction = (ref) => {
ref.setState({ myState: myValue })
}
Then, I called that function like this.
this.myFunction(this.myComponent)
But, it does not work. It gives me the following error.
null is not an object (evaluating 'ref.setState')
Actually what I need this myFunction to do is,
this.myComponent.setState({ myState: myValue })
Can you please help me to solve this problem?
ref is not your this object. it's dom for your componnet. For setState you need this of your component.
you can pass this as argument.
myFunction(this)
Now you will be able to do ref.setState in myFunction.
function myFunction(ref) {
ref.setState({ myState: myValue })
}
To use setState, just use your component's context (this keyword). The context also have your ref in it, so you don't need to pass it as an argument if you are inside one component(not forwarding down to children)
myFunction = (event) => {
this.myComponent // -> points to your ref, DOM element
this.setState() // use your setState like that
}
Don't forget to bind your context in parent component if you want to pass the handler to the child components. Refer to this useful topic
EDIT: Based on your comment, I guess you want to update the parent state by calling a handler in some other component. To do that, you need to create a handler in your parent component, bind the context and pass it as a property to the child component. Next up, you need to assign this handler in your child component. You cannot pass a context with setState method via argument or ref, this is just not how it works in javascript and in react.
Example:
// ParentComponent.js
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 1,
};
this.onChangeHandler = this.onChangeHandler.bind(this);
}
onChangeHandler(event) {
this.setState({
value: someNewValue // will update state on parent component
})
}
render() {
return (
<View>
<SomeComponent>{this.state.value}</SomeComponent>
<ChildrenComponent onChangeHandler={this.onChangeHandler} />
</View>
);
}
}
// ChildrenComponent.js
const ChildrenComponent = (props) => (
<View>
<Button
onPress={props.onChangeHandler}
title="click me to change parent state"
/>
</View>
);
Hopefully, this is what you need :)
I have some components in my app which are expected to handle some user inputs from the keyboard. For that I created the following function:
export default function withKeydownEventHandler (handler) {
id = id + 1
return lifecycle({
componentWillMount () {
$(window).on(`keydown.${id}`, evt => handler(evt))
},
componentWillUnmount () {
$(window).off(`keydown.${id}`)
}
})
}
This works fine, but the handlers are being fired off for different components at the same time. So if my handler does different things in each component, whenever I click a button it will be fired off from both components at the same time. Also, once one component is unmounted, the HoC will no longer work.
For example, say I have the following two containers:
export default compose(
withKeydownEventHandler((evt, props) => {
console.warn('hi from Component 1')
}),
withProps(() => {
// stuff
})
)(Component1)
export default compose(
withKeydownEventHandler((evt, props) => {
console.warn('hi from Component 2')
}),
withProps(() => {
// stuff
})
)(Component2)
If I click any button throughout the app, I will get the following output:
hi from Component 1
hi from Component 2
On the flip side, once one of the components becomes unmounted, I no longer get any events.
What am I doing wrong? How can I get a keydown event handler through an HoC that can be re-used throughout my app?
Firstly, may I bring to your attention that your id is set as a global variable. Are you sure you want to have such a variable name as a global?
Secondly, you are binding the keydown event to the windows with $(window).on('keydown.${id}', evt => handler(evt)) which explains your unwanted behaviour. You need to bind it once with the specific component you want the handler to act to.
Finally, why dont you create a HOC class and add the event listeners conditionally? like the following:
// src/Hoc.jsx
export default function(WrapperComponent) {
return class extends Component {
componentWillMount () {
const { onKeyDownHandler } = this.props;
if (isKeyDownEventNeeded) {
this.comp.addEventListener("keydown", onKeyDownHandler);
}
}
componentWillUnmount () {
const { onKeyDownHandler } = this.props;
if (isKeyDownEventNeeded) {
this.comp.removeEventListener("keydown", onKeyDownHandler);
}
}
render() {
const { onKeyDownHandler } = this.props;
if (onKeyDownHandler) {
// a "ref" callback which assigns the mounted
// Element to a prop "comp" whicu can be used later to add the DOM listener to.
return <WrapperComponent ref={elem => this.comp = elem} {...this.props} />
}
return <WrapperComponent {...this.props} />
}
}
export default HighOrderComponent;
Then
// somewhere-else.js
import highOrderComponent from 'src/Hoc'
highOrderComponent(<Component1 onKeyDownHandler={() => console.log('hey, Component 1'} />
highOrderComponent(<Component2 onKeyDownHandler={() => console.log('hey, Component 2'} />
For more info on how check out this answer
I'm new to React.JS and trying to create a click event on an element inside a rendered component.
Here is my code:
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick(i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};
My problem is that "handleClick" gets triggered after page load without any mouse clicked!
I've read about React.JS lifecycle and thought about registering to click event in componentDidMount method, but i'm really not sure about it:
Is there any easier way ? (or: Am I doing something wrong that triggers click ?)
If adding componentDidMount method is the right way - how can I get the element I create in render method ?
You should not use .bind when passing the callback as a prop. There’s a ESLint rule for that. You can read more about how to pass callback without breaking React performance here.
Summary:
make sure you aren’t calling functions but pass functions as handlers in your props.
make sure you do not create functions on every render, for that, you need to bind your handlers in parent component, pass correct the required data (such as indices of iteration) down the child component and have it call the parent’s handler with the data it has
Ideally you’d create another component for the rows and pass the callback there. Moreover, ideally you’d bind the onClick in the parent component’s constructor (or componentWillMount). Otherwise every time render runs a new function is created (in both anonymous function handler () => { this.onClick() } and this.onClick.bind and defeat React’s vdom diff causing every row to rerender every time.
So:
class InputPanel extends React.Component{
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{board.map((row, i) => <div>
{row.map((cell, j) => <Digit
onClick={this.handleClick})
i={i}
j={j}
cell={cell}
/>)}
</div>)}
</div>
);
}
};
class Digit extends React.Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onClick(this.props.i, this.props.j);
}
render() {
return <div
className="digit"
onClick={this.handleClick}
>{this.props.cell}</div>
}
}
It is because you are calling this.handleClick() function instead of providing a function definition as onClick prop.
Try changing the div line like this:
<div className="digit" onClick={ () => this.handleClick(i,j) }>{cell}</div>
Also you have to bind this.handleClick() function. You can add a constructor and bind all the member functions of a class there. that's the best practice in ES6.
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
You call this function in render. You should only transfer function and bind params.
onClick={this.handleClick.bind(null,i,j)}
You should use .bind().
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick.bind(null,i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};
I have a component that I have created:
class Create extends Component {
constructor(props) {
super(props);
}
render() {
var playlistDOM = this.renderPlaylists(this.props.playlists);
return (
<div>
{playlistDOM}
</div>
)
}
activatePlaylist(playlistId) {
debugger;
}
renderPlaylists(playlists) {
return playlists.map(playlist => {
return <div key={playlist.playlist_id} onClick={this.activatePlaylist(playlist.playlist_id)}>{playlist.playlist_name}</div>
});
}
}
function mapStateToProps(state) {
return {
playlists: state.playlists
}
}
export default connect(mapStateToProps)(Create);
When I render this page, activatePlaylist is called for each playlist in my map. If I bind activatePlaylist like:
activatePlaylist.bind(this, playlist.playlist_id)
I can also use an anonymous function:
onClick={() => this.activatePlaylist(playlist.playlist_id)}
then it works as expected. Why does this happen?
You need pass to onClick reference to function, when you do like this activatePlaylist( .. ) you call function and pass to onClick value that returned from activatePlaylist. You can use one of these three options:
1. using .bind
activatePlaylist.bind(this, playlist.playlist_id)
2. using arrow function
onClick={ () => this.activatePlaylist(playlist.playlist_id) }
3. or return function from activatePlaylist
activatePlaylist(playlistId) {
return function () {
// you code
}
}
I know this post is a few years old already, but just to reference the latest React tutorial/documentation about this common mistake (I made it too) from https://reactjs.org/tutorial/tutorial.html:
Note
To save typing and avoid the confusing behavior of this, we will use
the arrow function syntax for event handlers here and further below:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => alert('click')}>
{this.props.value}
</button>
);
}
}
Notice how with onClick={() => alert('click')}, we’re passing a
function as the onClick prop. React will only call this function after
a click. Forgetting () => and writing onClick={alert('click')} is a
common mistake, and would fire the alert every time the component
re-renders.
This behaviour was documented when React announced the release of class based components.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html
Autobinding
React.createClass has a built-in magic feature that bound all methods to this automatically for you. This can be a little confusing for JavaScript developers that are not used to this feature in other classes, or it can be confusing when they move from React to other classes.
Therefore we decided not to have this built-in into React's class model. You can still explicitly prebind methods in your constructor if you want.
import React from 'react';
import { Page ,Navbar, Popup} from 'framework7-react';
class AssignmentDashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
}
onSelectList=(ProjectId)=>{
return(
console.log(ProjectId,"projectid")
)
}
render() {
return (
<li key={index} onClick={()=> this.onSelectList(item.ProjectId)}></li>
)}
The way you passing the method this.activatePlaylist(playlist.playlist_id), will call the method immediately. You should pass the reference of the method to the onClick event. Follow one of the below-mentioned implementation to resolve your problem.
1.
onClick={this.activatePlaylist.bind(this,playlist.playlist_id)}
Here bind property is used to create a reference of the this.activatePlaylist method by passing this context and argument playlist.playlist_id
2.
onClick={ (event) => { this.activatePlaylist.(playlist.playlist_id)}}
This will attach a function to the onClick event which will get triggered on user click action only. When this code exectues the this.activatePlaylist method will be called.