In my app component below I want to hide a div based on a function I have defined but it brings an error saying
TypeError: Cannot read property 'setState' of undefined
Please what may be wrong
class Apps extends Component {
constructor(props) {
super(props);
// Don't do this!
this.state = { showing: true };
}
render() {
return (
<div>
<div className="container">
<div style={{ display: (this.state.showing ? 'block' : 'none') }}>
A Single Page web application made with react
</div>
</div>
<div className="buttons">
<a href='' onClick={this.onclick} >Login</a>
<br/>
<a href='' >Signup</a>
<br />
<a href='' >Members</a>
</div>
</div>
);
}
onclick(e){
e.preventDefault();
this.setState({showing: false});
}
}
You can use bind or you can just use an arrow ES6 function instead of binding it
onclick = (e) => {
e.preventDefault();
this.setState({ showing: false });
}
You can e.g. bind the function to this in the render method so that this will be what you expect in your onclick method. You can read more about why this is the case in the documentation.
<a href='' onClick={this.onclick.bind(this)}>Login</a>
#Tholle is right. You have to bind your onclick function to current class instance using this. By default this here points to global window obj, which does not have setState function.
Either you bind your function to this, where you are calling it as tholle suggests.
You can also bind it to this in constructor after calling super(props):
this.onclick = this.onclick.bind(this);
this can also be bound implicitly using arrow function like this:
< a href="" onClick={e=>this.onclick(e)} ... ./>
Best practice is to use 2nd option. As using 1st and 3rd options will create new references for the functions each time component renders. However, that's ok to use 1st or 3rd option if you have no problem with memory consumption or creating a small app.
You can use ES6 arrow functions to bind lexical this:
href='' onClick= {(e) => this.onclick(e)}
Related
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 3 years ago.
In the following example I'm trying to better understand the bind method. Specifically, what do the two instances of 'this' refer to and why do we need the second one? Also, why don't I need to include 'this' in the callback:
UPDATE:
I understand now that they both refer to FontChooser but why do we want to bind FontChooser.checkbox to FontChooser? Isn't that redundant? or in other words if 'this' refers to the class why do we need to bind a class callback (this.checkbox) to the class (this.checkbox.bind(this))?
It's almost like we are binding a specific instance back to the class callback but (a) where are we creating the specific instance and (b) shouldn't the specific instance already have the class callback
class FontChooser extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: true,
checked: this.props.bold ? true : false
};
}
displayButtons() {
this.setState({
hidden: !this.state.hidden
});
}
checkbox() {
//why not checkbox(this){
this.setState({ checked: !this.state.checked });
}
render() {
console.log(this.state);
var weight = this.state.checked ? "bold" : "normal";
return (
<div>
<input
type="checkbox"
id="boldCheckbox"
hidden={this.state.hidden}
checked={this.state.checked}
onChange={this.checkbox.bind(this)}
/>
<button id="decreaseButton" hidden={this.state.hidden}>
{" "}
-{" "}
</button>
<span id="fontSizeSpan" hidden={this.state.hidden}>
{" "}
{this.state.size}
</span>
<button id="increaseButton" hidden={this.state.hidden}>
{" "}
+{" "}
</button>
<span
id="textSpan"
style={{ fontWeight: weight, fontSize: this.state.size }}
onClick={this.displayButtons.bind(this)}
>
{" "}
{this.props.text}
</span>
</div>
);
}
}
In javascript, the this keyword points to a different object depending on the context it was executed in. When using a function in the JSX 'template', the function is not executed within your class, but in some other context in React. As a consequence, whatever this refers to, is changed.
One was to work around this, is using the bind() method. This method will replace whatever function it is used on, and replace whatever the this keyword points to, to a new location you provided.
In your example, you're using this.displayButtons.bind(this). Here, this refers to this class (FontChooser), and will make sure that this will point to that class regardless of the execution context.
Take a look at the MDN documentation, there are also easy to understand examples.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
I am new to react and am trying to toggle a body class using two different buttons. One is supposed to add a class using an onClick event and the other is supposed to remove the class. Below is an example of my code.
Right now in the console I can see the event fire twice but the class remains. As I stated I am new to React so I know I may be doing this incorrectly.
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
You are trying to modify the dom directly like you would with vanilla js or JQuery, but this is not how react is meant to be used. React creates a virtual dom that you create and manage, and then react handle changing the page for you.
I recommend following a guide like this one to learn basic setup and concepts (skip to the part where he uses JSX).
I can further point you in the right direction if you show your whole component file.
You want to toggle a className prop value in the React way.
The React way is having a state prop and having a handler function that will toggle the state value, rather than manipulating the DOM node directly (the way you're doing it).
I would suggest you to take a look at React Main Concepts: Handling events and later once you feel a little bit more comfortable to read about Virtual DOM and Reconciliation in React.
Here's how can you do it:
const { classNames } = window
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isToggled: true
}
this.toggleClass = this.toggleClass.bind(this)
}
toggleClass() {
const { isToggled } = this.state
this.setState({
isToggled: !isToggled
})
}
render() {
const { isToggled } = this.state
const className = classNames({
'body-fixed': isToggled
})
return <div className={className}>
<div>Current `className`: <b>{ className }</b></div>
<button onClick={this.toggleClass}>Toggle class</button>
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://unpkg.com/classnames#2.2.6/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>
I was able to use the code I listed earlier. I had my onClick events positioned incorrectly. Here is an example of the code I used:
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
<Toggle>
{({on, getTogglerProps, setOn, setOff, toggle, showAlert}) =>
<div>
<button className="search-icon-top" {...getTogglerProps()}>{on ? <img className="times" onClick={this.bodyRelative} src={require('img/times.svg')} alt=" " /> : <i className="fa fa-search" onClick={this.bodyFixed}></i>}</button>
<div>
{on ? <TodaySearchBox /> : ''}
</div>
</div>}
</Toggle>
This is just a start for now. Thank you for the input.
EDIT: I am open to suggestions. Like I said I am new to React.
I'm new to react and have to make a project for my bootcamp with it and I am having trouble getting every movie component I render to have an individual button. Every time I click one button, the rest of the buttons on the page act like they are also clicked. Here is my movie component that is being called on.
Heres my first row of components and the buttons are the green ones on the bottom left corner. https://files.slack.com/files-pri/T571CRHGE-F826BKX7S/api.png.
importReact, { Component} from"react";
importAPIfrom"../utils/API"
classMovieextendsComponent{
constructor(){
super();
this.state={
color:'green',
icon:'add',
result:[]
};
}
componentDidMount() {
this.topMovies();
}
topMovies=() =>{
API.topMovies()
.then(res=>this.setState({ result:res.data.results}))
.catch(err=>console.log(err));
}
handleClick=event=>{
if(this.state.color==='green'){
this.setState({color:'red'});
} else{
this.setState({color:'green'});
}
if(this.state.icon==='add') {
this.setState({icon:'remove'});
} else{
this.setState({icon:'add'});
}
}
render() {
constimgURL="https://image.tmdb.org/t/p/w300/"
return(
<div>
{
this.state.result.map((movieList) =>(
<div className="col s4 movieBox">
<div className="card">
<div className="card-image">
<img src={imgURL +movieList.poster_path} />
<span className="card-title"><a id={this.state.color} onClick={this.handleClick} className="btn-floating btn waves-effect waves-light"><i className="material-icons">{this.state.icon}</i></a></span>
</div>
<div className="card-content movieInfo">
<p>Title:{movieList.title}</p>
<p>Genre:{movieList.genre_ids}</p>
<p>Rating:{movieList.vote_average}</p>
</div>
</div>
</div>
))
}
</div>
)
}
}
exportdefaultMovie;
You need to bind the handleClick function ( in fact all functions ) inside the constructor:
constructor(){
super();
this.state={
color:'green',
icon:'add',
result:[]
};
this.handleClick = this.handleClick.bind(this);
}
onClick={()=> this.handleClick()} will also work.
.map() calls a provided callback for each element in your array. So this means that you are creating several buttons that will execute the same event on each of your elements. I suggest creating a row component that handles your onClickEvent and then you could pass the component an id or use an in line arrow function () => this.handleClick(). (this does create a anonymous function on each click and could potentially be bad for performance in larger apps but could work in your case if you don't want to create a row component)
Also you should read up on why using keys with unique ids is important.
https://coderwall.com/p/jdybeq/the-importance-of-component-keys-in-react-js
Hope that helps.
I am converting a project from the old React.createComponent to new the ES6 class definition. I have the following component:
class AnswerFrame extends React.Component {
constructor(props) {
super(props);
}
render() {
var props = this.props;
var selectedNumbers = props.selectedNumbers.map(function (i) {
return (
<span onClick={props.unselectNumber.bind(null, i)}>
{i}
</span>
)
});
return (
<div id="answer-frame">
<div className="well">
{selectedNumbers}
</div>
</div>
);
}
};
The idea is to define an array, selectedNumbers which is an array of spans that each have an onclick event. These are then rendered.
This code worked fine before converting to ES6 but now is giving me the following error:
Error: Objects are not valid as a React child
(found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, screenX, screenY, clientX, clientY, ctrlKey, shiftKey, altKey, metaKey, getModifierState, button, buttons, relatedTarget, pageX, pageY, isDefaultPrevented, isPropagationStopped, _dispatchListeners, _dispatchInstances}).
If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `AnswerFrame`.
Why is this not working anymore?
Yep turns out the problem was in props.selectedNumbers. Trying to figure out the change in the way this is bound when you use ES6, I removed a null from a bind somewhere else in the code.
This:
numbers.push(
<div className={className} onClick={selectNumber.bind(i)}>
{i}
</div>
);
should've been
numbers.push(
<div className={className} onClick={selectNumber.bind(null,i)}>
{i}
</div>
);
So props.selectedNumbers was the object causing the problem.
Binding in react props is bad practice (https://daveceddia.com/avoid-bind-when-passing-props/), consider making selectNumber a higher order function:
const selectNumber = (i) => () => {
// your original function code here
doWhateverWith(i);
}
And setting onClick to:
<div className={className} onClick={selectNumber(i)}>
I know that this is just another way of doing the same thing but...
members.push(
<div className={className} onClick={() => {selectedNumber(i);}}>
{i}
</div>
);
You don't have to read the whole code, just read the comment in the editQuantity function and in showOrderItem function, specially in the showOrderItem function and my problem is i think just silly, as both of my function are working as they supposed to work,
*editQuantity function supposed to change the state, it changing it, i checked by adding the console line.
*showOrderItem function supposed display the item, he is doing that job as well.
My problem is, i try to add conditional rendering in the showOrderItem function that not working, even though i am able to change the state.
Please read the comment in showOrderItem function, to see where is the problem:
import React from 'react';
export default class ShowOrder extends React.Component{
constructor(props){
super(props);
this.state={
quantityEditing:this.props.orderItems,
}
}
editQuantity(item){
const order=this.state.quantityEditing;
for(var i =0; i<order.length; i++){
if(order[i].item==item){
console.log(order[i].orderQuantityEditing)
order[i].orderQuantityEditing=true;
this.setState({order:this.state.quantityEditing})
console.log(order[i].orderQuantityEditing)
}
}
}
showOrderItem(){
const style = {cursor:'pointer'}
const orderItems=this.state.quantityEditing;
console.log(orderItems)
const orderItem=orderItems.map((item,index)=>
<p>
{orderItems.orderQuantityEditing ? 'some':
<span style={style} onClick={this.editQuantity.bind(this,item.item)}>
//as you can see in here i added conditional rendering, that if orderItems.orderQuantityEditing is true display me some, but that's not working --orderItems.orderQuantityEditing ? 'some'(this part) does not matter how many times i click on property it never display me my string 'some'
{item.quantity}</span>}
<span style={style}> {item.item}</span>
<span style={style}> Q</span>
<span style={style}> N</span>
<span style={style}> X</span>
</p>
);
return orderItem;
}
render(){
return(
<div>
{this.showOrderItem()}
</div>
);
}
}
Instead of
{orderItems.orderQuantityEditing ?
'some'
:
<span style={style} onClick{this.editQuantity.bind(this,item.item)}>
I think you need to write this:
{item.orderQuantityEditing ?
'some'
:
<span style={style} onClick={this.editQuantity.bind(this,item.item)}>
Because you are using map, and item will be each object of array, so you need to use item to access that property. During the for loop, when changing the state you wrote:
order[i].orderQuantityEditing=true;
That it proper, order will be an array and you need to provide the index to access particular object of that.