this.setState is not a function React - javascript

I know there are a lot of answers to this situation which involves using bind or arrow function but my situation is a bit different. Basically I want to create a div element with an EventListener that updates the state setIndex value with the index value of the specific child element that I clicked.The callback function for this process is the onModal
If I use arrow function for the onModal, the 'this' keyword gets transferred to the class instead of the individual elements and hence I wont be able to access the index for the individual elements.
If I using the normal function(as I have demonstrated in the code below), the this.setState gets transferred to the individual elements and I get the setState error
class AddRecipe extends Component{
constructor() {
super();
this.state={
name:[],
setIndex:0,
}
}
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
var recipecard = document.createElement("div")
recipecard.setAttribute("class","recipecard")
recipecard.setAttribute("key",index)
recipe.setAttribute("key",0)
var h1 = document.createElement("h1")
h1.setAttribute("id","keyhold")
h1.appendChild(document.createTextNode(data[index].name))
console.log(h1);
recipecard.appendChild(h1)
recipecard.addEventListener("click",this.onModal)
recipe.appendChild(recipecard)
})
}
componentDidMount() {
this.onLoad()
}
onModal() {
var index = this.getAttribute("key")
var recipe = document.querySelector(".recipe")
recipe.setAttribute("key",index)
var modal = document.getElementById('MyModal')
var btn = document.getElementById('myBtn')
var modal = document.getElementById('myModal');
var btn = document.getElementById("myBtn");
modal.style.display = "block";
this.setState({setIndex:index}).bind(this)
}
onClose() {
var modal = document.getElementById('myModal');
var span = document.getElementsByClassName("close")[0]
modal.style.display= "none"
}
render() {
return (
<div>
<div id="myModal" class="modal">
<div class="modal-content">
<span onClick={this.onClose.bind(this)} class="close">×</span>
<ModalWindow datas= {data} index={this.state.setIndex} />
</div>
</div>
<div className ="recipe">
</div>
</div>
);
}
}
export default AddRecipe

Pass the index argument over to the event handler and process accordingly.
Also, since you wanted the element context only for retrieving the index, you can send the index along to the onModal method by using function currying. This way you need not depend on the element's context anymore.
If your React project supports arrow functions for class instance methods then you can try passing the arguments like below.
class AddRecipe extends Component{
...
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
...
recipecard.addEventListener("click",this.onModal(index))
recipe.appendChild(recipecard)
})
}
onModal = (index) => (event) => {
...
this.setState({ setIndex:index })
}
...
}
export default AddRecipe
OR
You can even try the way it's documented in the React Docs to avoid any confusion
https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
class AddRecipe extends Component{
...
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
...
recipecard.addEventListener("click",(event) => this.onModal(index))
recipe.appendChild(recipecard)
})
}
onModal = (index) => {
...
this.setState({ setIndex:index })
}
...
}
export default AddRecipe
Check out this article to see the many ways of sending parameters to event handlers
https://medium.freecodecamp.org/reactjs-pass-parameters-to-event-handlers-ca1f5c422b9

Probably the error is in the line recipecard.addEventListener("click",this.onModal) you need to add a bind to the this.onModal call.
I think there's no need to use the bind method on setState.

bind will attach the first argument as the context (i.e. 'this") of the function you are calling bind on and return a new function with the new context.
this.setState({...}) is a function call, thus it is not possible to bind any this.
you need something like this:
onLoad(){
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
(...)
// !!! bind this to onModal
recipecard.addEventListener("click",this.onModal.bind(this))
(...)
}) }
so if you bind 'this' to onModal, the function called on the click event will use the context of your class and therefore it can call setState of your class.
edit
you can use the second part of Nandu Kalidindis answer, too.
arrow functions automatically have the context of the place where they are created therefore the context of onLoad which is the context of the class.

recipecard.addEventListener("click",this.onModal.bind(this)) onModal have to be bind
and I'm not sure if this will be working without binding
componentDidMount() {
this.onLoad()
}
in constructor add line of code
this.onLoad = this.onLoad.bind(this)

Related

connectedCallback () on custom elements cannot use forEach to loop data on javascript

can anyone provide a solution to the problem that I'm currently encountering? I created a custom element where this custom element must have been detected on the dom, but I need to have the data contained in this custom element loaded, so my program code is like this.
import './menu-item.js';
class MenuList extends HTMLElement {
// forEach cannot be used if I use the ConnectedCallback () method
connectedCallback() {
this.render()
}
// my data can be from this method setter
set menus(menus) {
this._menus = menus;
this.render();
}
render() {
this._menus.forEach(menu => {
const menuItemElement = document.createElement('menu-item');
menuItemElement.menu = menu;
this.appendChild(menuItemElement);
});
}
}
customElements.define('menu-list', MenuList);
and this is the data I sent in the main.js file
import '../component/menu/menu-list.js';
import polo from '../data/polo/polo.js';
const menuListElement = document.querySelector('menu-list');
menuListElement.menus = polo;
please give me the solution.
The connectedCallback runs before the menus=polo statement.
So there is no this._menus declared.
If all the menus setter does is call render, then why not merge them:
set menus(menus) {
this.append(...menus.map(menu => {
const menuItemElement = document.createElement('menu-item');
menuItemElement.menu = menu;
return menuItemElement;
}));
}

Javascript ES6 Classes - Method cant access class property defined inside class constructor

So i was trying to structure my code inside a class so it can be more organized, but iam struggling. I have the code:
class App {
constructor() {
// Get elements from DOM
const titleBox = document.getElementById('titleBox');
const navBox = document.getElementById('navBox');
const navLinks = document.querySelectorAll('.header__listLink');
const headerTitle = document.getElementById('headerTitle');
const headerSubtitle = document.getElementById('headerSubtitle');
const ulNav = document.getElementById('ulNav');
const ulNavLinks = document.querySelectorAll('.ulNavLink');
// for each nav link, add an event listener, expanding content area
navLinks.forEach((link) => {
link.addEventListener('click', this.clickedLinkState);
});
}
clickedLinkState(e) {
e.preventDefault();
titleBox.classList.remove("header__title-box");
titleBox.classList.add("header__title-box--linkClicked");
headerTitle.classList.remove("header__title");
headerTitle.classList.add("header__title--linkClicked");
headerSubtitle.classList.remove("header__subtitle");
headerSubtitle.classList.add("header__subtitle--linkClicked");
ulNav.classList.remove("header__listInline");
ulNav.classList.add("header__listInline--linkClicked");
navBox.classList.remove("header__nav-box");
navBox.classList.add("header__nav-box--linkClicked");
ulNavLinks.forEach((navLink) => {
navLink.classList.remove("header__listLink");
navLink.classList.add("header__listLink--linkClicked");
});
}
}
const app = new App();
And i got the error: "main.js:40 Uncaught ReferenceError: ulNavLinks is not defined
at HTMLLIElement.clickedLinkState (main.js:40)". the 'ulNavLinks' is a nodeList.
I was trying to define the elements using 'this.titleBox = ...', for exemple, but it got even worse, i could not access it from my clickedLinkState method. Outside the class it was working.
Why i cant access the 'ulNavLinks' inside my method? and why i cant access my propesties inside the method if i declare them 'this.titleBox', 'this.navBox'?
In JavaScript, as for now, instance properties can only being defined inside class methods using keyword this (here is the doc).
Also there is an experimental feature of supporting public/private fields, which you may use with some build steps due to poor browser support.
Make sure to use this:
class App {
constructor() {
// Get elements from DOM
this.titleBox = document.getElementById('titleBox');
this.navBox = document.getElementById('navBox');
this.navLinks = document.querySelectorAll('.header__listLink');
this.headerTitle = document.getElementById('headerTitle');
this.headerSubtitle = document.getElementById('headerSubtitle');
this.ulNav = document.getElementById('ulNav');
this.ulNavLinks = document.querySelectorAll('.ulNavLink');
// for each nav link, add an event listener, expanding content area
this.navLinks.forEach((link) => {
link.addEventListener('click', this.clickedLinkState.bind(this));
});
}
clickedLinkState(e) {
e.preventDefault();
this.titleBox.classList.remove("header__title-box");
this.titleBox.classList.add("header__title-box--linkClicked");
this.headerTitle.classList.remove("header__title");
this.headerTitle.classList.add("header__title--linkClicked");
this.headerSubtitle.classList.remove("header__subtitle");
this.headerSubtitle.classList.add("header__subtitle--linkClicked");
this.ulNav.classList.remove("header__listInline");
this.ulNav.classList.add("header__listInline--linkClicked");
this.navBox.classList.remove("header__nav-box");
this.navBox.classList.add("header__nav-box--linkClicked");
this.ulNavLinks.forEach((navLink) => {
navLink.classList.remove("header__listLink");
navLink.classList.add("header__listLink--linkClicked");
});
}
}
const app = new App();

Uncaught (in promise) TypeError: _this3.setState is not a function

I am getting above error while setting the data in state in reactjs,
Scenario: passing data from child component to parent component, in child component I am calling parent function and changing the state value using setstate,
ChildComponent
search(){
var input = this.refs.userInput.value;
this.props.findSearch(input);
input.value = '';
}
Parent Component:
findSearch(input){
axios.get(`http://localhost:3000/blogs`)
.then(res => {
input = input.toLowerCase();
let rest = res.data.filter((e)=>{
e.cardtitle === input;
});
this.setState({result:rest}); // here I am getting the error
})
}
can you please help me out or other alternatives.
Bind function to this context in your react component.
constructor(props) {
super(props);
this.findSearch = this.findSearch.bind(this);
}
Seems like a this context related issue.
You should bind your function to the class with this or just use an arrow function as a class field to get a lexical context for this:
findSearch = (input) => {
axios.get(`http://localhost:3000/blogs`)
.then(res => {
input = input.toLowerCase();
let rest = res.data.filter((e)=>{
e.cardtitle === input;
});
this.setState({result:rest}); // here I am getting the error
})
}
Note that class fields are a proposal in stage 3 and you would likely need to add babel-plugin-transform-class-properties

ReactJS bind a component method the correct way

I am trying to use .bind() when using a method in my component.
The reason is simple: In a loop I am returing Components and extend them with a property which is calling a method. But for every loop-item this I want to extend the this Object with some information (like a key).
Example:
Items.jsx
Items = React.createClass({
eventMethod() {
console.log('this event was triggered by key:', this.key);
},
items() {
let items = [];
let properties = {};
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
let {...props} = properties;
let item = <Item {...props} key={key} />;
items.push(item);
});
return items;
},
render() {
return(<div>{this.items()}</div>);
}
});
Item.jsx
Item = React.createClass(...);
In this case (and its working) when the Item Component is triggering the prop "eventMethodInItem" my method "eventMethod" will be called and this.key has the correct value!
So - whats now the question ? Its working perfect, right ?
Yes.
But ReactJS does not want me to do this. This is what ReactJS is telling me as a console log.
Warning: bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See Items
Maybe you think its a "bad" way to add children to the component like I am doing it but in my special case I need to do this in this way - so I need to bind new information to a method.
I'm not going to pretend that I understand what you are trying to do here, but maybe I can help clear it up anyway.
React takes all of the top level methods found on each component and automagically binds them to the context of the component.
This prevents other methods from overriding the context of this and as a result, if you try to rebind the method, React says "Hey don't bother. I already got it" — which is the warning you are seeing.
Assuming that you really want do this (each time you are mutating the outer properties object by overriding the eventMethodInItem property).
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
Then I can't see any reason that the eventMethod has to live on the component, rather than just in the scope of the items function.
items() {
const eventMethod = function() {
console.log('this event was triggered by key:', this.key);
}
// ...
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = eventMethod.bind(_.extend(this, {
key
}));
// ...
});
},
That way you don't have to fight React to get your program to work.
React is already autobinding this when using React.createClass http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#under-the-hood-autobinding-and-event-delegation
Change your binding to
properties.eventMethodInItem = this.eventMethod.bind(null,key);
and your eventMethod to
eventMethod(key) {
console.log('this event was triggered by key:', key);
}
I also suggest using _.map instead of _.each
items() {
return _.map(this.props.items, (itemData, key) => {
return <Item
handleEventMethod={this.eventMethod.bind(null,key)}
key={key} />;
});
},
Good pattern
https://www.newmediacampaigns.com/blog/refactoring-react-components-to-es6-classes
Before :
class ExampleComponent extends React.Component {
constructor() {
super();
this. _handleClick = this. _handleClick.bind(this);
this. _handleFoo = this. _handleFoo.bind(this);
}
// ...
}
After :
class BaseComponent extends React.Component {
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
}
class ExampleComponent extends BaseComponent {
constructor() {
super();
this._bind('_handleClick', '_handleFoo');
}
// ...
}
another good hacks for this topic http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html

OnClick Event binding in React.js

I would like to pass the parent div id, on click of that div or any child element of the same div. But I am unable to achieve it. Please tell me where I am making a mistake. Code is below:
viewMore: function(i,j){
console.log('You clicked: ', i );
},
render : function(){
var attributeId = "groups_";
attributeId+= index;
return(
//parent div
<div className="groups" id={attributeId} onClick={this.viewMore}>
<div className="floatLeft"> Group Name: <h3>My Name</h3></div>
<span className="floatRight typeCd">POC</span>
<div className="clearfix"> Key Attributes:
<ul>
<li> POC 1</li>
</ul>
</div>
</div>
)
};
viewMore = (i,j) => () => {
console.log(i,j)
}
To pass parameters to event handlers we need to use currying.
With the above method no new functions created all the time while render is called.
Since I see these kind of suggestions in multiple places, I am going to move my comment into an answer as well, to provide an additional view:
class TestComponent extends React.Component {
constructor() {
super();
this.onClick = this.handleClick.bind(this);
}
handleClick(event) {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
This allows to:
avoid unnecessary binds
access the id and whatever else properties in a much more React-ive manner.
Of course, the above example assumes that you receive the id as a prop, but you can do the necessary manipulations as well.
UPDATE 1 -- Nov 28, 2016
Added link to CodePen from comments above.
UPDATE 2 -- Mar 30, 2017
As mentioned, this wouldn't work if you use React.createClass to define your components. You don't have a constructor to pull this off. You can use other lifecycle methods, if you don't mind a little ugliness.
Having said that, it is 2017. Use ES6, would you?!
UPDATE 3 -- May 12, 2017
If you are using class properties transform, then you can simplify it further:
class TestComponent extends React.Component {
onClick = (event) => {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
UPDATE 4 -- Feb 4, 2018
Due to improvements of bind and friends in V8 (Chakra and such probably too), you just may be better off using the this.click.bind(this) or wrapping it in an arrow function when passing to onClick.
Why?
The previous method, created for performance reasons only, closed some possibilities for dynamically injecting functions onto the component's prototype.
NOTE 1 -- Apr 14, 2018
Keep in mind that the method mentioned in Update 4 still introduces some performance issues, as on each render pass a new function is created as a result of bind. This, in turn, will trickle down to the child component and cause unnecessary re-renders, as the function changes each time.
The same thing happens when you pass an arrow function inline.
All other methods, like using class properties, will mess with your inheritance (which you should be avoiding, but still), simply due to the fact that, currently, Babel transpiles them to "on-instance" functions, which are not on the prototype chain.
So, this:
class Person {
printA = () => { console.log('a') }
}
becomes:
function _classCallCheck(instance, Constructor) {...abridged...}
var Person = function Person() {
_classCallCheck(this, Person);
this.printA = function () {
console.log('a');
};
};
I've made an updated answer for ES6 here:
https://stackoverflow.com/a/35748912/76840
Essentially, you can use arrow function expressions, which have the benefit of preserving this:
onClick={(event)=>this.viewMore(attributeId, event)}
As of this edit, if you're using Babel with stage-2 enabled, you can use a property like so:
// Within your class...
viewMore = (event) => { /* ... */ }
// Within render method in your JSX
onClick = {this.viewMore}
You can use currying function.
ES5:
viewMore(param) { // param is the argument you passed to the function
return function(e) { // e is the event object that returned
};
}
ES6
viewMore = param => e => {
// param is the argument you passed to the function
// e is the event object that returned
};
And just use it like this:
onClick={this.viewMore("some param")}
Here is an update and an overview of previous answers:
Using onClick={this.viewMore.bind(this, attributeId)} by #HenrikAndersson
.While this approach serves the purpose it uses the bind syntax with which many are not comfortable.
Using public class field mentioned by #ZenMaster.This solution has more or less the same performance, it also comes with a better syntax. But it turns tricky when we have to pass a parameter.
class TestComponent extends React.Component {
onClick = (event) => {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
The above mentioned approach skips passing parameters and instead uses custom attributes to access the data required in click handler.
A better solution would be :
class MyComponent extends React.Component {
handleClick = (item) => (e) => {
e.preventDefault()
console.log(`This has access to item ${item}! and event(e)`)
}
render(){
const item={ id:'1', value: 'a' }
return(
<button onClick={ this.handleClick(item) } >Click</button>
)
}
}
Reference: Handle events by arrow functions in React app
Here is the code how to use on click event.
var attributeId = "groups_";
attributeId = 32;
const viewMore = (val) => () => {
console.log(val)
}
return (
//parent div
<div className="groups" id={attributeId} onClick={viewMore(attributeId)}>
<div className="floatLeft"> Group Name: <h3>My Name</h3></div>
<span className="floatRight typeCd">POC</span>
<div className="clearfix"> Key Attributes:
<ul>
<li> POC 1</li>
</ul>
</div>
</div>
)

Categories