Accessing this.props from React Component functions - javascript

I'm new to React.js and is confused by how the following 2 functions displayGender and toggleGender access this.props differently.
import React, { Component } from 'react';
export default class User extends Component {
displayGender() {
console.log('displayGender: ', this.props)
}
toggleGender() {
console.log('toggleGender: ', this.props)
}
render() {
return (
<Button onClick={ this.toggleGender.bind(this) } >
{ this.displayGender.bind(this) }
</Button>
);
}
}
Why is this.toggleGender.bind(this) able to access this.props that was passed to this React Component User, but this.displayGender.bind(this) sees this.props as undefined?
So far I am only able to access this.props from within this.displayGender if I pass it to the function. Is this the usual practice?
export default class User extends Component {
displayGender(props) {
console.log(props.user)
}
render() {
return (
<Button onClick={ this.toggleGender.bind(this) } >
{ this.displayGender(this.props) }
</Button>
);
}
}

Why is this.toggleGender.bind(this) able to access this.props that was
passed to this React Component User, but this.displayGender.bind(this)
sees this.props as undefined?
I'm not certain it's necessary to bind "this" to displayGender because unlike toggleGender it should already be bound to the correct object by default. I believe you should be able to access this.props from displayGender just by invoking the method.
The Handling Events section in the React docs might be helpful.

That's because your this.toggleGender.bind(this) function is added as a click event in your component and this.displayGender.bind(this) is added as a child node inside the <button>
What bind usually refers is put the given function inside some other anonymous enclosed function (and preserve the context ie, this if supplied)
So: toggleGender.bind(this) usually refers to function(){toggleGender()}
When the above is placed in click function it makes sense as we're attaching a function to click event. But it wouldn't make sense to add it as a child node

Related

ReactJS - Child's onClick event calls parent method but the scope is not what I expected

I have a parent and child class. The parent class has a store and a method that needs to be called when the child is clicked. The parent method modifies the store. Unfortunately when I call the parent's method from the child's onClick (using props), the this object is not available. I believe this is a scoping issue but I don't know whether it's a JS level issue or a misunderstanding of React's scoping (I'm new to the framework).
The components are defined below, when I click on the Button span I get the following error out of toggleAlarmState:
Uncaught TypeError: Cannot read property 'setState' of undefined
Parent component
import Button from "../components/buttons/Button"
export default class TestStatus extends Component {
state = {
alarmIsOn: false,
};
toggleAlarmState(event) {
this.setState({
alarmIsOn: !this.state.alarmIsOn
}
)
}
render() {
return (
<Button
className="alarm-toggle-btn"
text={`Alarm is ${this.state.alarmIsOn ? 'On' : 'Off'}`}
onClick={this.toggleAlarmState}
/>
);
}
}
Child Component
import React, {Component} from "react";
export default class Btn extends Component {
render() {
return (
<span onClick={this.props.onClick}>{this.props.text}</span>
)
}
}
Why is this undefined and what is the best practice for modifying the store when I click a button component?
I think if you convert toggleAlarmState to an arrow function, it will work! or if you insist on using the same function, you have to bind toggleAlarmState with this inside your constructor.
toggleAlarmState = event => {
this.setState({
alarmIsOn: !this.state.alarmIsOn
}
)
}
This is another solution with binding if you are not using an arrow function.
constructor( props ){
super( props );
this.toggleAlarmState = this.toggleAlarmState.bind(this);
}
state = {
alarmIsOn: false,
};
toggleAlarmState(event) {
this.setState({
alarmIsOn: !this.state.alarmIsOn
}
)
}

How to unit test React functions passed in as prop to child component with Enzyme shallow wrapper

I am very new to front-end dev & I am having some trouble getting my Enzyme unit tests using Shallow. Basically, I have something like
class MyComponent extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
const {
handleClick,
...other
} = this.props;
return (
<div className="someClass">
// a bunch of stuff
<div className="buttonArea">
<MyComponentChild onClick={handleClick} />
</div>
</div>
);
}
MyComponent.propTypes = {
handleClick: PropTypes.func.isRequired,
...other
};
export default MyComponent;
}
handleClick is a callback function defined in the container (i.e ComponentContainer) that MyComponent belongs to. I am passing it as a prop into MyComponent, and subsequently MyComponentChild (which is a button component). I want to test whether handleClick fires when MyComponentChild is clicked.
My current Enzyme test
it('handleClick should fire when clicked', () => {
const mockCallbackFn = jest.fn();
const wrapper = shallow(<MyComponent {handleClick = { mockCallbackFn }} />);
wrapper.find('MyComponentChild').simulate('click');
expect(mockCallbackFn).toHaveBeenCalled();
});
However, this test is currently failing, as mockCallbackFn is apparently never called. But, this is also passing
expect(wrapper.find('MyComponentChild').prop('handleClick')).toEqual(mockCallbackFn);
What am I doing wrong? Any help much appreciated!
simulate(someEventName) does really simple thing: it calls prop with name of "on" + someEventName. So simulate('click') runs .props().onClick().
But your component uses handleClick prop, that's why it does not called by simulate()
wrapper.find('MyComponentChild').props().handleClick();
Name simulate is so confusing that team is going to remove it out(https://github.com/airbnb/enzyme/issues/2173).
Side note: you don't need extra braces when declaring props. I mean {handleClick = { mockCallbackFn }} better be handleClick={mockCallbackFn} since it's typical for React code and looks less confusing.
You need to use mount instead of shallow. shallow only mounts the first level of components, so your ComponentChild is not being mounted and your click handler isn't being passed in.
You can see this yourself by calling debug() on your wrapper and console.log-ing it.

My sort function is being called automatically upon render instead of onClick [duplicate]

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.

this.props 'missing' method using conect from react-redux

https://codesandbox.io/s/6l8zr5k94k
Why when I do this.props I see only object but not the function? but this.props.approveItem is there, this is so strange.
import React, { Component } from "react";
import { connect } from "react-redux";
import { approveItem } from "./actions";
#connect(state => state.items, { approveItem })
export default class Items extends Component {
render() {
console.log('where is approveItem?', this.props);
console.log('approveItem is here', this.props.approveItem);
return (
<div>
<div>status: {this.props.item.status}</div>
<button onClick={() => this.props.approveItem()}>Approve </button>
</div>
);
}
}
I'm not sure what exactly you are missing but running your code does print the props and shows the action as expected:
Edit
I think i know why you are not seeing the function when you log it.
You are looking at the console of the code sandbox application, which probably is doing a serialization of the props object.
The problem is that functions are not serialize-able.
From the docs:
Functions are not a valid JSON data type so they will not work.
However, they can be displayed if first converted to a string
You can run the code below to see how JSON.stringify for instance, is not serializing the function inside the object.
const obj = {
someKey: 'some Value',
someFunc: function() {}
};
console.log(JSON.stringify(obj));
FYI: You don't need to create an inline arrow function to pass it down to the onClick event, you can just pass the reference via the props.
So change this:
<button onClick={() => this.props.approveItem()}>Approve </button>
To this:
<button onClick={this.props.approveItem}>Approve </button>
approveItem function is available in this.props
this.props.approveItem()
this.props is always an object, never be a function, Just think if we need to have multiple functions in props, we can have multiple function only if this.props is object. Not possible if this.props is itself as function.
Seeing methods in this.props object. Please look into console - https://codesandbox.io/s/xpy0lmolr4

refs does not work in react in order to call child method

There are two component in react project.
1, Parent
2, Child
Now, I'd like to use childMethod in Parent component.
In some pages of stackoverflow, everyone said refs is effective.
But in my project, it's not working.
class Parent extends Component {
parentMethod(){
this.refs.child.childMethod();
}
render() {
return (
<Child ref='child'/>
);
}
}
class Child extends Component {
childMethod() {
alert('You made it!');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
When I use above code, there is one error code in browser console.
_this3.refs.child.childMethod is not a function
I'd like to use child method, so I have 2 questions.
1, What's "_this3" ? How can I use refs correctly?
2, Do you have any other idea about it?
class Parent extends React.Component {
constructor(props) {
super(props);
// binding methods to the class so they don't lose 'this'
// when invoked from another environment.
this.parentMethod = this.parentMethod.bind(this);
this.setChildRef = this.setChildRef.bind(this);
}
parentMethod() {
this.childNode.childMethod();
}
// intentionally avoided using an arrow fuction inside JSX
// for we don't want a new anonymous fn created on every render.
setChildRef(node) { // receives reference to component as argument
this.childNode = node;
}
render() {
return (
<div>
<Child ref={this.setChildRef}/>
<button onClick={this.parentMethod}>Parent Button - Click me :)</button>
</div>
);
}
}
class Child extends React.Component {
childMethod() {
alert('You made it!');
}
render() {
return (
<h1>Child</h1>
);
}
}
Working fiddle: https://jsfiddle.net/free_soul/9vrLrw8h/
What's "_this3"?
Probably a variable that you may see in browser while debugging. It just represents an execution context.
How can I use refs correctly?
It is preferred to treat the ref as a callback attribute and no longer depend on the refs Object. If you do use the refs Object, avoid
accessing refs of descendant components. You should treat refs as a
private accessor and not part of a component's API. Treat only the
methods exposed on a component instance as its public API.

Categories