ref is not defined using arrow function in react? - javascript

I try to make a toggleable content when user clicked on outside of the element I got error of this.node is not defined error?
handleOutsideClick(e) {
// ignore clicks on the component itself
if (this.node.contains(e.target)) {
return;
}
this.handleClick();
}
render() {
return (
<div ref={node => { this.node = node; }}>
<button onClick={this.handleClick}>Button handler</button>
{this.state.visibleContent && <div>Toggle content</div>}
</div>
);
}
Code https://codesandbox.io/s/v38q4zrq7
In the render method I've used ref={node => { this.node = node; }} why is it still undefined? Here's a working example that used exactly the same technique https://codepen.io/graubnla/pen/EgdgZm

Your function handleOutsideClick is out of scope. If you're using babel, you can turn it into an arrow function directly
handleOutsideClick = (e) => {
// ignore clicks on the component itself
if (this.node.contains(e.target)) {
return;
}
this.handleClick();
}
or if that is not an option, bind it in your constructor
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}

Related

using a simple component (dumb) to create a list of buttons and use parent method

I'm trying to create a simple dashboard. I'm just exploring some new ideas I have in react and it's been so long I'm running into a strange problem I can't seem to understand.
I have a very simple class:
export default class Dashboard extends React.Component {
constructor(){
super();
}
HandleClick = (e) => {
if (e.name === "createEvent") {
console.log('event clicked');
}
console.log(e.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={this.HandleClick}/>
</div>
)
}
}
and then I have a simple function outside of the class that creates a button list:
function ButtonList(props) {
return (
<button name="createEvent" onClick={props.HandleClick}>Create Event</button>
)
}
the idea behind this was instead of having so much stuff inside one superclass I wanted to separate simple functionality, like a button or command list if you will, that opon clicking would eventually change the state of the navbar.
I'm not sure how I would return that values of the button, or aside from that pass a parameter into the button from a child prop.
For example instead of doing HandleClick = (e) => and actually look for a parameter, how would I pass that in the child function where it gets used (if there were many more buttons)?
This is what you should be doing instead:
On your parent component, you can use arrow functions to pass the parameters within handleClick. This will allow you to listen to the events on your child ButtonList component with the parameters passed onto the method.
In addition, if you want to access to name attribute of your button, you should be calling event.target.name, as name is part of the target property of the Event interface.
export default class Dashboard extends React.Component {
constructor(){
super();
}
handleClick = (e) => {
if (e.target.name === "createEvent") {
console.log('event clicked');
}
console.log(e.target.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={(e) => this.handleClick(e)} />
</div>
)
}
}
And on your ButtonList functional component, you should pass the onClick event to the onClick props which was defined as part of the ButtonList component.
function ButtonList(props) {
const onClick = (e) => {
props.onClick(e);
};
return (
<button name="createEvent" onClick={(e) => onClick(e)}>Create Event</button>
)
}
I have created a demo over here.

Should functions that call a callback function be bound?

If I pass a callback function from Parent to GrandChild, should handleClick be bound in Child and GrandChild?
Parent.js
class Parent extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Clicked!');
}
render() {
return (
<Child onClick={this.handleClick} />
);
}
}
Child.js
class Child extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { onClick: callback } = this.props;
callback();
}
render() {
return (
<GrandChild onClick={this.handleClick} />
);
}
}
GrandChild.js
class GrandChild extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { onClick: callback } = this.props;
callback();
}
render() {
return (
<div onClick={this.handleClick} />
);
}
}
Functions can be accessed via props without being bound when passed down to children. It's only necessary to bind to this inside the component where the function is originally defined.
You only need to do onClick={this.props.handeClick}
or if you want to pass some data, you can do it like this:
onClick={(someData) => this.props.handeClick(someData)}
EDIT: Just to clarify, you only need to bind handleClick in Parent.js. You can then just pass this function down via props and access it in the child components using this.props.
The answer is that the context this should always be the one where the logic is, so if the logic that handles the handleClick is in the class Parent so, the context is.
Other than that there are some problems in your code.
1.Your component classes must extend React.Component or React.PureComponent and not React itself (maybe it's a copy-paste error, but if not fix it).
See: https://reactjs.org/docs/components-and-props.html#function-and-class-components
2.You don't have to name every single props that should be passed through all child components, you can use the spread syntax if you code using ES6.
See: https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
class Child extends React.Component {
render() {
return (
// this is passing all props of Child to GrandChild
<GrandChild {...this.props} />
);
}
}
3.For components that don't have state, use function instead of class, it's more performant and also the code is smaller.
function Child(props) {
return (
<GrandChild {...props} />
);
}
Finally your code could look like this:
function Parent(props) {
function handleClick() {
console.log('clicked');
}
return <Child onClick={handleClick} />;
}
function Child(props) {
return <GrandChild {...props} />;
}
function GrandChild(props) {
return <div onClick={props.onClick} />;
}
Arrow function is better. And context this will be automatically bind.
handleClick = () => {}
Inline function is bad (unnecessary render possible). It is better like this:
handleClick = (someData) => this.props.handeClick(someData)
And
onClick={this.handleClick}

Call parent component function from child in React.JS

I am trying to make a simple component in React.JS which displays a list of items, then the user can select an item from the list. I am trying to handle the clicks on the list-items by handing down a function from the parent component to the child, so it can notify the parent when it was clicked and the parent can update the selected item. For some reason the function from the child component is not calling the parent function properly as it never gets to the point to write to the console ... I guess it must something to do with binds, but I literally tried every combination possible to make it work.
Tbh, I don't even understand why I have to use "clicked={()=>this.clickedSub}" in the parent component when I already used bind in the constructor, but I guess I don't have to understand everything XD
var months = [
'January','February','March','April','May','June','July','August','September','October','November','December'
];
class SubItem extends React.Component {
constructor(props){
super(props);
this.clickedMe = this.clickedMe.bind(this);
}
clickedMe () {
let i = this.props.id;
console.log("from child: "+i);
this.props.clicked(i);
}
render () {
if (this.props.isSelected) return <a href="#" className="selected" onClick={this.clickedMe}>{this.props.text}</a>;
else return <a href="#" onClick={this.clickedMe}>{this.props.text}</a>;
}
}
class SideMenu extends React.Component {
constructor() {
super();
this.state = {
selected: 0,
open: true
};
this.clickedHead = this.clickedHead.bind(this);
this.clickedSub = this.clickedSub.bind(this);
}
clickedHead () {
this.setState({
open: !this.state.open
});
}
clickedSub(i) {
console.log("from parent:"+i);
this.setState({
selected: i
});
}
render() {
let sel = this.state.selected;
var sublist = this.props.subitems.map(function (item, index){
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={()=>this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={()=>this.clickedSub}/>;
});
if (this.state.open) return (
<div className="side_menu">
<div className="menu_item open">
<div className="header" onClick={this.clickedHead}>{this.props.header}</div>
<div className="sub_items">
{sublist}
</div>
</div>
</div>
);
else return(
<div className="side_menu">
<div className="menu_item open">
<div className="header" onClick={this.clickedHead}>{this.props.header}</div>
<div className="sub_items"></div>
</div>
</div>
);
}
}
ReactDOM.render(
<SideMenu header="Month" subitems={months}/>,
document.getElementById('menu')
);
See the Pen vertical collapsible side-menu by Ize8 on CodePen.
Alright, so I struggled with this one for a little while. You have to be really careful when you do NOT use es6 in react. Arrow functions are your friend, and generally just make more sense.
This is where all your trouble is coming from:
var sublist = this.props.subitems.map(function (item, index){
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={()=>this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={()=>this.clickedSub}/>;
});
You want to use arrow functions here because you're messing with the scope. You can pass down the function as intended, and you do not have to do this clicked={() => this.clickedSub} syntax which is confusing.
var sublist = this.props.subitems.map((item, index) => {
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={this.clickedSub}/>;
});
This will pass down your function as intended, but you have some other issues with your code. It causes an infinite loop, but I'll let you implement this and work through it.
First of all if you don't wont to have .bind(this) in constructor use an arrow function
clickedSub(i){} it is clickedSub = (i)=>{}
Now. I don't get what function you pass to the children. but I will show you an example.
class Parent extends Component {
constructor() {...}
parentFunction = () => {
console.log('This will be called when we click `a` tag in Child component');
}
render() {
return (
<Child funct = {this.parentFunction}/>
)
}
}
class Child extends Component {
handleClick = () => {
this.props.func();
}
render() {
return(
<a onClick={this.handleClick}> Click me </a>
)
}
}

Scoping issue react parent-child method ES6 [duplicate]

This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 7 years ago.
Im currently a little stuck with the following.
function Tag(props){
let {text, onRemove, key} = props;
return (
<span onClick={onRemove(key)} key={key}>{text}</span>
)
}
function TagInput (props) {
let {onChange, value, onKeyDown} = props;
return (
<input type="text" onChange={onChange} value={value} onKeyDown={onKeyDown} />
)
}
class TagsInput extends Component {
render() {
let { Tag, TagInput, state } = this.props;
console.log(state.tags);
return (
<div ref="div" onClick={(e) => this.handleClick(e)}>
{state.tags.map((tag) =>
<Tag
text={tag.text}
key={tag.id}
onRemove={this.handleRemove}
/>
)}
<TagInput
onChange={(e) => this.handleChange(e)}
onKeyDown={(e) => this.handleKeyDown(e)}
/>
</div>
)
}
handleClick(e) {
console.log(e.target.value);
}
handleChange(e){
//console.log(e.target.value);
}
handleKeyDown(e){
//console.log('keycode', e.keyCode);
const { dispatch } = this.props;
if (e.keyCode === 32) {
dispatch(addTag(e.target.value));
}
if (e.keyCode === 8 && e.target.value.length === 0) {
dispatch(removeTag());
}
}
handleRemove(id){
const { dispatch } = this.props;
dispatch(removeTag(id));
}
}
TagsInput.propTypes = {
TagInput: React.PropTypes.func,
Tag: React.PropTypes.func,
removeKeyCodes: React.PropTypes.array,
addKeyCodes: React.PropTypes.array
};
TagsInput.defaultProps = {
TagInput: TagInput,
Tag: Tag,
removeKeyCodes: [8],
addKeyCodes: [9, 13]
};
I get the following error in console Uncaught TypeError: Cannot read property 'props' of undefined from method handleRemove line const { dispatch } = this.props. It seems like a scoping issue but I cannot seem to understand why this (no pun intended lol) is occurring.
ES6 classes do not automatically bind this to functions with the exception of the ones provided by extending Component such as componentDidMount etc..
from the docs
The ES6 way - bind this to your methods in your constructor, OR where you call them:
class TagsInput extends Component {
constructor (props) {
super(props)
this.handleRremove = this.handleRemove.bind(this)
}
OR
render() {
return (
<Tag
onRemove={this.handleRemove.bind(this)}
/>
}
The ES7 way #1: bind syntax
this.handleRemove = ::this.handleRemove
The ES7 way #2: class arrow functions (I think this is the best way):
handleRemove = (id) => {
const { dispatch } = this.props;
dispatch(removeTag(id));
}
Then call it like:
onRemove={ () => this.handleRemove(tag.id) }
UPDATE: Read #Road's answer as well. When binding the method at point of use you need to pass the argument along.
Have you tried binding this? Try this.handleRemove.bind(this, tag.id). The tag.id there is the argument you're passing since handleRemove(id) needs an id as an arg.

Can't call function onSubmit from embedded form element [duplicate]

The most stupid thing is happening with my code right now. I have a list of items render in the DOM, I need to put a button in order to call another function, if I put the button like this <button></button> everything is ok, but if I assign a function to that button, then everything goes down <button onClick={function}></button> I will show you my code, look
#connectToStores
export default class Dealers extends Component {
static contextTypes = {
router : React.PropTypes.func,
}
static propTypes = {
title : React.PropTypes.func,
}
constructor (props) {
super(props);
this.state = {
modal : false,
}
}
static getStores () {
return [ GetDealersStore ];
}
static getPropsFromStores () {
return GetDealersStore.getState();
}
render () {
let dealersInfo;
if (this.props.dealerData !== null) {
dealersInfo = this.props.dealerData.dealersData.map(function(dealer) {
return (<div key={dealer.DealerId} style={Styles.dealerCard}>
<Card>
<CardHeader title={dealer.NickName}
subtitle={dealer.DealerId}
avatar={dealer.Picture}/>
<CardText>
<FloatingActionButton> ////////////////////////
<IconAdd /> //////THIS IS THE BUTTON/////
</FloatingActionButton>//////////////////////
</CardText>
</Card>
</div>
);
});
} else {
dealersInfo = <p>Loading . . .</p>;
}
return (
<Grid>
<Row>
<Column><h4>Dealers</h4></Column>
</Row>
<div style={Styles.mainCont}>
{dealersInfo}
</div>
</Grid>
);
}
componentWillMount () {
GetDealersActions.getDealers();
}
_openUpdateDealer = () => {
console.log(123);
}
}
as you can see there is an statement
if (this.props.dealerData !== null) {
...
}else {
dealersInfo = <p>Loading . . .</p>;
}
as I pasted the code above everything works awesome, but if I add <FloatingActionButton onClick={this._openUpdateDealer.bind(this)}><IconAdd /></FloatingActionButton> then everything goes down, all I see in the screen is Loading . . . which is the else in the statement above.
So, I want to know, what is going on with react here ?
You're rendering the button in the middle of a .map operation:
this.props.dealerData.dealersData.map(function(dealer) {
which uses a different value for this; thus, this.props doesn't exist inside the function. I would expect to see cannot read property dealerData of undefined in the browser console.
You need to use the optional thisArg parameter:
this.props.dealerData.dealersData.map(function(dealer) {
// ...
}, this);
bind the mapping function to this manually:
this.props.dealerData.dealersData.map(function(dealer) {
// ...
}.bind(this));
or use an arrow function (since you're using ES6 features):
this.props.dealerData.dealersData.map((dealer) => {
// ...
});

Categories

Resources