this.someFunction is not a function - javascript

After having read about the bind requirement for methods to be bound to a React ES6 class, I am still having some difficulty with this example:
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
this.updateState = this.updateState.bind(this);
}
componentDidMount() {
this.updateState();
}
handleProductUpvote(productId) {
Data.forEach((item) => {
if (item.id === productId) {
item.votes = item.votes + 1;
return;
}
});
this.updateState();
}
updateState() {
const products = Data.sort((a,b) => {
return b.votes - a.votes;
});
this.setState({ products });
}
render() {
const products = this.state.products.map((product) => {
return (
<Product
key={'product-' + product.id}
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitter_avatar_url={product.submitter_avatar_url}
product_image_url={product.product_image_url}
onVote={this.handleProductUpvote}
/>
);
});
return (
<div className='ui items'>
{products}
</div>
);
}
}
class Product extends React.Component {
constructor() {
super();
this.handleUpvote = this.handleUpvote.bind(this);
}
handleUpvote() {
this.props.onVote(this.props.id);
}
render() {
return (
<div className='item'>
<div className='image'>
<img src={this.props.product_image_url} />
</div>
<div className='middle aligned content'>
<div className='header'>
<a onClick={this.handleUpvote}>
<i className='large caret up icon'></i>
</a>
{this.props.votes}
</div>
<div className='description'>
<a href={this.props.url}>
{this.props.title}
</a>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src={this.props.submitter_avatar_url}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<ProductList />,
document.getElementById('content')
);
This returns
Uncaught TypeError: this.updateState is not a function(...) at handleProductUpvote
Is the initialized binding not sufficient in this case?

Whenever you see this issue, you don't want to be adding the bind to the method that it's trying to call right then, but the method that you are inside of when the "this.xxx not defined" issue occurs.
Currently, it's getting the function handleProductUpvote just fine - but it's calling it in the wrong object context. So you need to do the same thing as you did with updateState in the constructor, but with that function. Though I have limited react knowledge I believe it's common to do that for every function that's used as an event listener or callback.

Related

Why TypeError for onclick function? - ReactJS

I am developing an app to fetch posts from a API and load posts.Everything is working fine except gotopost function.After rendering this error comes TypeError: Cannot read property 'gotopost' of undefined
But don't know why this is happening.Though i bind that function and declared that function above the render() function
below is the code
import React, { Component } from 'react';
import './App.css';
class Todos extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
posts: [],
singleLoaded: false,
singlepost: []
}
this.fetchposts = this.fetchposts.bind(this);
}
fetchposts(){
fetch("http://smashingdevs.com/wp-json/wp/v2/posts/")
.then((response) => {
return response.json();
})
.then((posts) => {
this.setState({
isLoaded: true,
posts: posts
});
});
}
render() {
let container;
if(this.state.isLoaded){
container = <Allposts posts={this.state.posts}/>;
}else if(this.state.singleLoaded){
container = <Singleposts />;
}
return (
<div>
<h2>Hello there</h2>
<button onClick={this.fetchposts}>
fetch posts
</button>
{container}
</div>
);
}
}
class Allposts extends React.Component{
constructor(props){
super(props);
}
gotopost(){}
render(){
return (<div className="row mt-5">
{
this.props.posts.map(function(post){
return <div className="col-md-6 mb-2" key={post.id}>
<div className="card" >
// Problem is here on onClick
Go somewhere
</div>
</div>
})
}
</div>
)
}
}
class Singleposts extends React.Component{
}
export default Todos;
Use arrow function:
this.props.posts.map((post) => {
this will not be what you expect inside the function given to map. You can bind the function to this, or you can use an arrow function to use the surrounding lexical scope, and this will be what you want.
class Allposts extends React.Component {
gotopost() {}
render() {
return (
<div className="row mt-5">
{this.props.posts.map(post => {
return (
<div className="col-md-6 mb-2" key={post.id}>
<div className="card">
<a
href={post.link}
onClick={this.gotopost.bind(this)}
className="btn btn-primary"
>
Go somewhere
</a>
</div>
</div>
);
})}
</div>
);
}
}
This is common error of JS. When you go to internal function, var this in this scope is no longer outside class anymore. The workaround is assigning this to another variable at the top of method.
render(){
const self = this;
return (<div className="row mt-5">
{
this.props.posts.map(function(post){
return <div className="col-md-6 mb-2" key={post.id}>
<div className="card" >
// use self instead this
Go somewhere
</div>
</div>
})
}
</div>
)
}

bind(this) in ReactJS and onChange update to state

I have the following code:
index.js
class App extends React.Component {
constructor() {
super();
this.state = {
homeLink: "Home"
};
}
onGreet() {
alert("Hello!");
}
onChangeLinkName(newName) {
this.setState({
homeLink: newName
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
<Header homeLink={this.state.homeLink}/>
</div>
</div>
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
<Home
name={"Max"}
initialAge={27}
greet={this.onGreet}
changeLink={this.onChangeLinkName.bind(this)}
initialLinkName={this.state.homeLink}
/>
</div>
</div>
</div>
);
}
}
And
Home.js
export class Home extends React.Component {
constructor(props) {
super();
this.state = {
age: props.initialAge,
status: 0,
homeLink: props.initialLinkName
};
}
onMakeOlder() {
this.setState({
age: this.state.age + 3
});
}
onChangeLink() {
this.props.changeLink(this.state.homeLink);
}
onHandleChange(event) {
this.setState({
homeLink: event.target.value
});
}
render() {
return (
<div>
<p>In a new Component!</p>
<p>Your name is {this.props.name}, your age is {this.state.age}</p>
<p>Status: {this.state.status}</p>
<hr/>
<button onClick={() => this.onMakeOlder()} className="btn btn-primary">Make me older!</button>
<hr/>
<button onClick={this.props.greet} className="btn btn-primary">Greet</button>
<hr/>
<input type="text" value ={this.state.homeLink}
onChange={(event) => this.onHandleChange(event)}/>
<button onClick={this.onChangeLink.bind(this)} className="btn btn-primary">Change Header Link</button>
</div>
);
}
}
Would onChange in the input tag be triggered as soon as I write something in the input field and update to state? I can't see the state change in the React Developer Tool extension in Chrome when I write something in the input field.
When I click the button this.onChangeLink it triggers the onChangeLink function. The onChangeLink doesn't seem to take any arguments since the brakets are empty, still I'm able to pass this.state.homeLink to this.props.changeLink inside the onChangeLink function. this.props.changeLink which is also a function in index.js takes an argument newName. I guess this is where the bind(this) comes in. What does bind(this) do? Could I rewrite it with a fat arrow function like (event) => this.onChangeLink(event)?
bind is a function in Function.prototype which returns a function object which is bound to the current this.
onClick={this.onChangeLink.bind(this)}
Here onClick function will be passed as a handler with the current context.
An arrow function does not create a new this context at all. The this will refer to the context in which the function is defined.
An arrow function does not create its own this context, so this has its original meaning from the enclosing context.
onChange={(event) => this.onChangeLink(event)}
Here even though onChangeLink is not bound, it is called within an arrow function within the same this context.
So.. yes, you can replace it with a fat arrow notation to get the same effect. Although you have to rewrite the list of arguments in this case twice.
You dont need to onChangeLink in Home component. You can directly pass the value to App component.
class Home extends React.Component {
constructor(props) {
super();
this.state = {
age: props.initialAge,
status: 0,
homeLink: props.initialLinkName
};
}
onMakeOlder() {
this.setState({
age: this.state.age + 3
});
}
onHandleChange(event) {
this.setState({
homeLink: event.target.value
});
}
render() {
return (
<div>
<input type="text" value ={this.state.homeLink}
onChange={(event) => this.onHandleChange(event)}/>
<button onClick={()=>this.props.changeLink(this.state.homeLink)} className="btn btn-primary">Change Header Link</button>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
homeLink: "Home"
};
}
onGreet() {
alert("Hello!");
}
onChangeLinkName(newName) {
this.setState({
homeLink: newName
});
}
render() {
console.log(this.state)
return (
<div className="container">
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
</div>
</div>
<div className="row">
<div className="col-xs-10 col-xs-offset-1">
<Home
name={"Max"}
initialAge={27}
greet={this.onGreet}
changeLink={this.onChangeLinkName.bind(this)}
initialLinkName={this.state.homeLink}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

how to properly iterate through array of post objects retrieved through Wp Rest Api in react js

hello :) i am working on wp rest api and react js and i successfully retrieved data from wp rest api and displayed it, but its not the proper way to display data in react. console is showing the error message of ' Each child in an array or iterator should have a unique "key" prop ' . I read the document in react regarding the issue but didn't understood . Here is what i have written so far. Any help would be great thank you
class Home extends React.Component{
componentDidMount(){
const API_URL = 'http://localhost/wordpress/wp-json/wp/v2/posts/';
this.serverRequest = $.get(API_URL, function (result) {
this.setState({
result:result,
loaded:true
});
}.bind(this));
}
constructor(props) {
super(props);
this.state ={result:null,loaded:false};
autoBind(this);
}
render(){
if (this.state.loaded) {
return(
<div>
{this.state.result && <Portfolio data = {this.state.result}/>}
</div>
)
}
return(
<div>loading...
</div>
)
}
}
export default Home;
and the Portfolio component to which prop data is passed renders the data like this which is not the proper way
class Portfolio extends React.Component{
constructor(props) {
super(props);
autoBind(this);
}
render(){
var contents=[];
for (var i = 0; i < this.props.data.length; i++) {
if (this.props.data[i].categories[0] == 5) {
var productImage ={
backgroundImage:'url('+ this.props.data[i].featured_image + ')',
backgroundSize: '100% 100%'
}
contents.push(
<div id="portfolio-product-item" style ={productImage} >
<div id ="portfolio-product-item-details" >
<h3>{this.props.data[i].slug}</h3>
<div dangerouslySetInnerHTML={{__html: this.props.data[i].content.rendered}} />
</div>
</div>
);
}
}
return(
<section className="portfolio">
{contents}
</section>
)
}
}
export default Portfolio;
I haven't went over all of your code, just relating to the "key" error you got.
The issue is here -
contents.push(
<div id="portfolio-product-item" style ={productImage} >
<div id ="portfolio-product-item-details" >
<h3>{this.props.data[i].slug}</h3>
<div dangerouslySetInnerHTML={{__html: this.props.data[i].content.rendered}} />
</div>
</div>
);
The father div with the id of portfolio-product-item should also have an attribute key so the proper way to write it is -
contents.push(
<div key={i} id="portfolio-product-item" style ={productImage} >
<div id ="portfolio-product-item-details" >
<h3>{this.props.data[i].slug}</h3>
<div dangerouslySetInnerHTML={{__html: this.props.data[i].content.rendered}} />
</div>
</div>
);
Have a look at - https://facebook.github.io/react/docs/multiple-components.html#dynamic-children for further info.
To make your Portfolio component cleaner.
I suggest, split the logic and create a separate method from the main render.
class Portfolio extends React.Component{
constructor(props) {
super(props);
autoBind(this);
}
renderPortfolioOption(index, option) {
const { data } = this.props;
if (option.categories[0] == 5) {
var productImage ={
backgroundImage:'url('+ option.featured_image + ')',
backgroundSize: '100% 100%'
}
return(
<div key={index} id="portfolio-product-item" style ={productImage} >
<div id ="portfolio-product-item-details" >
<h3>{option.slug}</h3>
<div dangerouslySetInnerHTML={{__html: option.content.rendered}} />
</div>
</div>
);
}
}
render() {
const { data } = this.props;
if (data.length) return null;
return(
<section className="portfolio">
{ data.map((index, option) => { return this.renderPortfolioOption(index, option); }) }
</section>
)
}
}
export default Portfolio;

How to call setState function onClick ES6 way

class BlogPost extends React.Component{
//getInitialState
constructor(){
super();
this.onLike = this.onLike.bind(this);
this.state = {
like :0
}
}
onLike(){
this.setState({
like: this.state.like++
});
}
render(){
var postListItem = this.props.postList.map(function(post){
return <li> {post} </li>
});
return (
<div className="blogPost">
<h2>Posts</h2>
<ul>{postListItem}</ul>
<button onClick={(e) => {this.onLike}}>{this.state.like}</button>
</div>
);
}
}
Nothing happens on clicking Button.
There is no function on button and index.js has added Empty Function to it. IDK why?please explain
In this case you need remove arrow function ((e) => { }), it is not necessary here, because you bind this in constructor., also inside onLike change this.state.like++ to this.state.like + 1 because you can't mutate state
<button onClick={ this.onLike }>{this.state.like}</button>
class BlogPost extends React.Component{
constructor(){
super();
this.onLike = this.onLike.bind(this);
this.state = {
like: 0
}
}
onLike(){
this.setState({
like: this.state.like + 1
});
}
render(){
var postListItem = this.props.postList.map(function(post){
return <li> {post} </li>
});
return (
<div className="blogPost">
<h2>Posts</h2>
<ul>{ postListItem }</ul>
<button onClick={ this.onLike }>{this.state.like}</button>
</div>
);
}
}
ReactDOM.render(<BlogPost postList={ [] } />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
I think you're not invoking the onLike function in the annonymous function you're passing to onClick.
Try this:
<button onClick={(e) => {this.onLike()}}>{this.state.like}</button>
Note the () after onLike.

react show button on mouse enter

I have a react component which hold method like:
mouseEnter(){
console.log("this is mouse enter")
}
render(){
var album_list;
const {albums} = this.props
if(albums.user_info){
album_list = albums.user_info.albums.data.filter(album => album.photos).map((album => {
return
<div className={"col-sm-3"} key={album.id} onMouseEnter={this.mouseEnter}>
<div className={(this.state.id === album.id) ? 'panel panel-default active-album' : 'panel panel-default'} key={album.id} onClick={this.handleClick.bind(this, album.id)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive center-block"} src={album.photos.data[0].source} />
</div>
</div>
</div>
}))
}
return (
<div className={"container"}>
<div className="row">
{album_list}
</div>
</div>
)
}
}
Here I have onMouseEnter on album_list. When it is hover or mouse enter I want to dispalay a button on that div.
How can I do that ??
Thank you
Update the component's state to reflect whether the mouse is inside the component, then use the state value to conditionally render a button.
getInitialState() {
return {
isMouseInside: false
};
}
mouseEnter = () => {
this.setState({ isMouseInside: true });
}
mouseLeave = () => {
this.setState({ isMouseInside: false });
}
render() {
return (
<div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave}>
{this.state.isMouseInside ? <button>Your Button</button> : null}
</div>
);
}
Inside the render function we use the conditional operator (?) to return the button component if this.state.isMouseInside is truthy.
There is another approach that uses a reusable render component that would make components 'hoverable' or 'revealable' - whatever makes sense.
class Hoverable extends Component {
constructor() {
super();
this.state = {
isMouseInside: false
};
}
mouseEnter = () => {
this.setState({ isMouseInside: true });
}
mouseLeave = () => {
this.setState({ isMouseInside: false });
}
render() {
return this.props.children(
this.state.isMouseInside,
this.mouseEnter,
this.mouseLeave
)
}
}
Then create the functional component that represents the hoverable element. E.g an album
const HoverableElement = props => (
<Hoverable>
{(isMouseInside, mouseEnter, mouseLeave) => (
<div className="menu-item">
<div onMouseEnter={mouseEnter} onMouseLeave={mouseLeave}>
<h2>{props.title}</h2>
</div>
{isMouseInside && props.children}
</div>
)}
</Hoverable>
)
Finally, use the HoverableElement to render a list of elements that will each be 'hoverable' e.g an array of albums
class HoverableElementsList extends Component {
render() {
return (
<div>
<HoverableElement title="First Menu">
<p>Some children content</p>
</HoverableElement>
</div>
)
}
}

Categories

Resources