React JS, using parameters in onClick to change state? - javascript

I need to have parameters on my on click to send a string depending on what button someone clicks. I realized to set parameters i would need to create a function inside my render () {} but now when i try to use this.setState({zone: location}) I get the following error:
TypeError: Cannot read property 'setState' of undefined
Here is my code:
import React, { Component } from "react";
import "./styles/homeStyle.css";
import timerIcon from "./styles/media/timer_clock.png";
import streaksIcon from "./styles/media/streaks_fire.png";
import guideIcon from "./styles/media/guide_meditation.png";
class Main extends Component {
state = {
zone: "home",
};
render() {
return (
<React.Fragment>
<div id="diagonal_shape"></div>
<div className="row">
<div className="col s12" id="title">
<h4 className="center-align">
Peaceful<span id="title_steps"> Steps</span>
<br />
{this.state.zone}
</h4>
</div>
<div id="nav_bar">
<div className="col s4" id="goto_timer">
<p className="center-align">
<img src={timerIcon} width="60%" alt="clock" onClick={() => goTo("timer")} />
<br />
Timer
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={streaksIcon} width="60%" alt="fire" onClick={() => goTo("stats")} />
<br />
Stats
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={guideIcon} width="60%" alt="meditating" onClick={() => goTo("guides")} />
<br />
Guides
</p>
</div>
</div>
</div>
</React.Fragment>
);
function goTo(location) {
console.log("yes " + location);
this.setState({ zone: location });
}
}
}
export default Main;
I am pretty sure this is because i can't use the this.setState({}) inside the render function, but i am at a loss in how i should get this to work.

The reason that you are getting error is because you have defined a function inside render method after the return.
Note the word function here (not arrow fun). When you define a function the js engine will hoist it to the top of its scope with a value of undefined. Thats why you got the error.
Technically, you can define handler inside render method but you have to make sure to use an arrow function and define it before the render method return statement. See demo here
HOWEVER, as a best practice don't define handlers inside render method.
Code snippet of writing handler inside render
export class App extends Component {
state = {
name: "hello"
};
render() {
console.log("state", this.state.name);
const clickMe = () => {
console.log(this.setState({ name: "hi!!!!" }));
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={() => clickMe()}>Click Me</button>
</div>
);
}
}
export default App;

If you are doing state update in the function then You cannot define a function inside a class method. define the goTo method as the class method and then call it like this.goto("some string") on button click.
You need to bind this to the goTo method or just use arrow function.
class Main extends Component {
constructor(props) {
super(props)
this.state = {
zone: "home",
};
this.goTo = this.goTo.bind(this);
}
goTo(location) {
console.log("yes " + location);
this.setState({ zone: location });
}
render() {
return (
<React.Fragment>
<div id="diagonal_shape"></div>
<div className="row">
<div className="col s12" id="title">
<h4 className="center-align">
Peaceful<span id="title_steps"> Steps</span>
<br />
{this.state.zone}
</h4>
</div>
<div id="nav_bar">
<div className="col s4" id="goto_timer">
<p className="center-align">
<img src={timerIcon} width="60%" alt="clock" onClick={() => this.goTo("timer")} />
<br />
Timer
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={streaksIcon} width="60%" alt="fire" onClick={() => this.goTo("stats")} />
<br />
Stats
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={guideIcon} width="60%" alt="meditating" onClick={() => this.goTo("guides")} />
<br />
Guides
</p>
</div>
</div>
</div>
</React.Fragment>
);
}
}
export default Main;
If you want to use function within the class method and want to access the class fields/methods then bind the class this variable to the function or just use arrow function. With this the this variable inside the function will refer to class.
class Main extends Component {
state = {
zone: "home",
};
render() {
function goTo(location) {
console.log("yes " + location);
this.setState({ zone: location });
}
goTo = goTo.bind(this);
return (
<React.Fragment>
<div id="diagonal_shape"></div>
<div className="row">
<div className="col s12" id="title">
<h4 className="center-align">
Peaceful<span id="title_steps"> Steps</span>
<br />
{this.state.zone}
</h4>
</div>
<div id="nav_bar">
<div className="col s4" id="goto_timer">
<p className="center-align">
<img src={timerIcon} width="60%" alt="clock" onClick={() => this.goTo("timer")} />
<br />
Timer
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={streaksIcon} width="60%" alt="fire" onClick={() => this.goTo("stats")} />
<br />
Stats
</p>
</div>
<div className="col s4">
<p className="center-align">
<img src={guideIcon} width="60%" alt="meditating" onClick={() => this.goTo("guides")} />
<br />
Guides
</p>
</div>
</div>
</div>
</React.Fragment>
);
}
}
export default Main;
I would suggest the first approach as it's more readable an clean.

Related

How to setState onclick for two components in React?

I have created an ecommerce site. Within my Product.js I have an onclick function for each product that pushes the product to local storage and updates the state of the shopping cart.
However, my cart.js contains the totals like total products, taxes, total amount...etc.
How can I add setState to get them to update when a product is added? I tried adding setState within the return section of the Cart.js but that ended up creating an endless loop of error messages. Below is the code:
import React,{Component} from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {faCartPlus} from "#fortawesome/free-solid-svg-icons";
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
shoppingCart:[]
}
}
addToCart=(item)=>{
this.state.shoppingCart.push(item)
this.setState({shoppingCart:this.state.shoppingCart})
localStorage.setItem('cart',JSON.stringify(this.state.shoppingCart))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
import React, { Component } from "react";
import plus from "./assets/images/plus.svg";
import minus from "./assets/images/minus.svg";
class Cart extends Component{
constructor(props){
super(props)
this.state = {
totalItems: 0,
amount:0,
taxes: 0,
totalAmount: 0
}
}
render(){
const cartItems = JSON.parse( localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount = cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;},0) ||0;
const taxes = (amount * 0.065);
const totalAmount = amount + taxes;
return(<>
<div>
<h2>YOUR CART</h2>
<p>Total Items <span>{this.state.totalItems} </span></p>
<p>Amount <span>{this.state.amount}</span></p>
<p>Total Taxes <span>{this.state.taxes}</span></p>
<p>Total Amount <span>{this.state.totalAmount}</span></p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className = "quantity-container">
<img src={minus} className ="minus"/> <p className ="quantity" >QUANTITIY:<span className="qnty-txt"></span></p> <img src={plus} className ="plus"/>
</div>
<button onClick={localStorage.removeItem("item")}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
</>)
}
}
export default Cart;
you have to create createRef in class component, as you can see the below code I'm using createRef, You have to push the values in this.items and then you have to set the this.items in setState. I hope this would be helpful.
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
this.items = React.createRef([]);
}
addToCart=(item)=>{
this.items.current.push(item);
localStorage.setItem('cart',JSON.stringify(this.items.current))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
here second method:
let items = [];
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
}
componentDidMount() {
items = [];
}
addToCart=(item)=>{
items.push(item);
console.log(items)
localStorage.setItem('cart',JSON.stringify(items))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
Cart.js
import React, {Component} from 'react';
import plus from './assets/images/plus.svg';
import minus from './assets/images/minus.svg';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {
totalItems: 0,
amount: 0,
taxes: 0,
totalAmount: 0
};
}
removeItem = (id) => {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const filter = cartItems.filter(item => item.id !== id);
if (filter) {
JSON.stringify(localStorage.setItem('cart', filter));
}
}
render() {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount =
cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;
}, 0) || 0;
const taxes = amount * 0.065;
const totalAmount = amount + taxes;
return (
<>
<div>
<h2>YOUR CART</h2>
<p>
Total Items <span>{this.state.totalItems} </span>
</p>
<p>
Amount <span>{this.state.amount}</span>
</p>
<p>
Total Taxes <span>{this.state.taxes}</span>
</p>
<p>
Total Amount <span>{this.state.totalAmount}</span>
</p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map(element => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img src={element.image} alt="product img" className="prod-img" />
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className="quantity-container">
<img src={minus} className="minus" />{' '}
<p className="quantity">
QUANTITIY:<span className="qnty-txt"></span>
</p>{' '}
<img src={plus} className="plus" />
</div>
<button onClick={() => this.removeItem(element.id)}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div></div>
</div>
</>
);
}
}
export default Cart;
re-rendering the components after the button click will solve your problem.
I have done one sample example for your type problem here:
https://codesandbox.io/s/stateupdatetest-pb811e
if you find any difficulties regarding the solution. Please reply on this thread.

ReactJS, onScroll doesn't work for dynamic navbar

I'm trying to make a navbar visible only when the user passes the header section. I set up a simple function to be triggered by "onScroll". However it's not really working.
Component:
function App() {
const [productArray, setProductArray] = useState([]);
const [navBarClass, setNavBarClass] = useState("hidden");
const navBarScroll = () => {
setNavBarClass("visible");
}; // this should make the navbar visible
return (
<div className="App">
<Header onScroll={navBarScroll} /> // Here's the onScroll
<ProductContainer
productArray={productArray}
setProductArray={setProductArray}
/>
<About />
<Footer />
<NavBar className={navBarClass} /> // This is the nav bar
</div>
);
}
Header Component:
const Header = () => {
return (
<div className="header" id="home">
<div className="header-sub">
<div className="div-1">
<img src={logo} alt="logo" className="logo" />
<div className="menu">
Home
About
Shop
Contact
<ShoppingCartIcon className="btn" />
<p></p>
</div>
</div>
<div className="div-2">
<FadeIn transitionDuration="2500">
<h1>AS UNIQUE AS YOU ARE.</h1>
</FadeIn>
</div>
<div className="div-3"></div>
</div>
</div>
);
};
Issue is onScroll is not being consumed in Header component. We need to add onScroll event on HTML tag in order for it to be invoked.
You can read onScroll prop in Header component and pass it to parent div as below and it should get called:
const Header = ({onScroll}) => {
return (
<div className="header" id="home" onScroll={onScroll}>
<div className="header-sub">
<div className="div-1">
<img src={logo} alt="logo" className="logo" />
<div className="menu">
Home
About
Shop
Contact
<ShoppingCartIcon className="btn" />
<p></p>
</div>
</div>
<div className="div-2">
<FadeIn transitionDuration="2500">
<h1>AS UNIQUE AS YOU ARE.</h1>
</FadeIn>
</div>
<div className="div-3"></div>
</div>
</div>
);
};

How to change a text inside an element, corresponding to an onClick event in ReactJs?

I am displaying two divisions corresponding, to two button's onClick event:
class Home extends React.Component {
constructor() {
super();
this.state = {
isShowaMale: false
};
this.toggleShowMale = this.toggleShowMale.bind(this);
}
toggleShowMale(show) {
this.setState({ isShowaMale:show });
}
render() {
const { isShowaMale } = this.state;
return (
<div className="container py-5">
<div className="row">
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={() => this.toggleShowMale(true)} >
<img className="humanbody profile" src={malebody} />
</button>
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={() => this.toggleShowMale(false)} >
<img className="humanbody profile" src={femalebody} alt="" />
</button>
</div>
{/* Hidden div */}
<div className="row mx-auto">
{isShowaMale && (
<div className="mx-auto">
Hey man!
</div>
)}
{!isShowaMale && (
<div>
Hey woman!
</div>
)}
</div>
{/* Hidden div */}
</div>
)
}
}
export default Home;
But, can I just display one div and change just the word man and woman in the text Hey ____? And there is also a problem that, after reloading the web page, it always shows Hey woman due to isShowaMale: false being default state. How can I solve these?
Can I just display one div and change just the word man and woman in
the text Hey ____
<div className="row">
{`Hey ${isShowMan? " man" : "woman"}!`}
</div>
And there is also a problem that, after reloading the web page, it always shows Hey woman due to isShowaMale: false being the default
state.
You can think about localStorage
Due to Why are we disallowed to use HTML5 local storage on code snippets?
So you can test the live demo
constructor() {
super();
const isShow = window.localStorage.getItem("data");
this.state = { isShowMan: isShow === "false" || !isShow ? false : true };
}
toggleShowMan(isShow) {
window.localStorage.setItem("data", isShow);
this.setState({ isShowMan: isShow });
}
class Home extends React.Component {
constructor() {
super();
this.state = { isShowMan: true };
}
toggleShowMan(isShow) {
this.setState({ isShowMan: isShow });
}
render() {
const { isShowMan } = this.state;
return (
<div className="container py-5">
<div className="row">
<button
disabled={isShowMan}
className="btn col-6 bg-transparent col-12 col-sm-6"
onClick={() => this.toggleShowMan(true)}
>
malebody{" "}
</button>
<button
disabled={!isShowMan}
className="btn col-6 bg-transparent col-12 col-sm-6"
onClick={() => this.toggleShowMan(false)}
>
femalebody
</button>
</div>
<div className="row">
{`Hey ${isShowMan? " man" : "woman"}!`}
</div>
</div>
);
}
}
ReactDOM.render(<Home />, document.getElementById('root'));
<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="root"></div>
[UPDATE] following further clarification in the comments, the issue can be handles in a way similar to this:
class Home extends React.Component {
constructor() {
super();
this.state = {
isShowaMale: false
};
this.toggleShowMale = this.toggleShowMale.bind(this);
}
toggleShowMale(show) {
this.setState({ isShowaMale:show });
}
render() {
const formProps = this.state.isShowMale
? { title: 'Mr', name: 'John', surname: 'Doe' }
: { title: 'Ms', name: 'Jane', surname: 'Doe' };
return (
<div className="container py-5">
<div className="row">
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={()=> this.toggleShowMale(true)} >
<img className="humanbody profile" src={malebody} />
</button>
<button className="btn col-6 bg-transparent col-12 col-sm-6" onClick={()=> this.toggleShowMale(false)} >
<img className="humanbody profile" src={femalebody} alt="" />
</button>
</div>
{/* Hidden div */}
<div className="row mx-auto">
<div className="mx-auto">
<form>
<input type="text" id="title" name="title" placeholder={formProps.title} />
<input type="text" id="name" name="name" placeholder={formProps.title} />
<input type="text" id="surname" name="surname" placeholder={formProps.title} />
</form>
</div>
</div>
{/* Hidden div */}
</div>
)
}
}
export default Home;
Or the entire form can be placed into a separate function (or even a separate component).
[ORIGINAL ANSWER]
You simply replace
{/* Hidden div */}
<div className="row mx-auto">
{isShowaMale && (
<div className="mx-auto">
Hey man!
</div>
)}
{!isShowaMale && (
<div>
Hey woman!
</div>
)}
</div>
{/* Hidden div */}
with
{/* Hidden div */}
<div className="row mx-auto">
<div className="mx-auto">
Hey {isShowaMale ? 'man' : 'woman'}!
</div>
</div>
{/* Hidden div */}

React How to show individual data into popup

I am learning react I want to show movie data when clicking on particular div. currently, I called fancy box which is not right method to get the result
So I need help to show movie data once click on particular div.
class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
search: '',
};
}
updateSearch(event) {
this.setState({search: event.target.value.substr(0, 20)});
}
componentDidMount() {
fetch('http://www.omdbapi.com/?apikey=MyKey&s=fast&plot=full')
.then((Response) => Response.json())
.then((findresponse) => {
console.log(findresponse);
this.setState({
data: findresponse.Search,
});
});
}
render() {
let filteredMovie = this.state.data.filter((dynamicData) => {
return dynamicData.Title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
});
return (
<div className="container movies_list">
<div className="row">
<div className="col-md-12 p-4">
<form>
<input
type="text"
className="form-control"
placeholder="Search"
value={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
</form>
</div>
{filteredMovie &&
filteredMovie.map((dynamicData, key) => (
<div className="col-md-3 mb-3" key={key}>
<div className="card">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<div className="card-body">
<h6 className="card-title">{dynamicData.Title} </h6>
<h6 className="card-title">Year: {dynamicData.Year} </h6>
<p className="card-text">{dynamicData.Plot} </p>
<a
data-fancybox
data-src="#hidden-content"
href="javascript:;"
className="btn btn-info"
>
View
</a>
<div id="hidden-content">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<h2>{dynamicData.Title}</h2>
<p>{dynamicData.Year}</p>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
I highly recommend Reakit for modal & popovers.

Show infinite scrolling with loader

I am learning reactjs by creating a movie listing app. I have created a movie list using reactjs, react-mdl. I could not use infinite scrolling feature. For example at first, 3 movies are shown on the list and when user scrolls down more 3 movies list should be shown but before showing further 3 movies in list a loader icon should be shown. How can i show such on this scenario of code? I know i have to define the state isLoading and isLoadingMore. If the state of isLoading is false loader should be hidden else a loader should be shown.
App.js
class App extends Component {
constructor(props){
super(props);
this.state = {
movie:[
{
title:'The Flash',
id:1,
imageUrl:'http://cdn3-www.superherohype.com/assets/uploads/gallery/the-flash-2x15/12771634_1000420230050280_1499915054664837033_o.jpg',
rottenTomatoes:'85%'
},
{
title:'Silicon Valley',
id:2,
imageUrl:'https://www.wired.com/wp-content/uploads/2016/03/silicon_valley_opener4.jpg',
rottenTomatoes:'90%'
},
{
title:'Scorpion',
id:3,
imageUrl:'http://cdn-uploads.gameblog.fr/images/blogs/36115/202746.jpg',
rottenTomatoes:'80%'
},
],
isLoading:true,
isLoadingMore:false
}
}
showLoaderIcon(){
this.setState({
isLoading:true
});
}
hideLoaderIcon(){
this.setState({
isLoading:false
});
}
render() {
return (
<div className="App">
<div className="demo-big-content">
<Layout fixedHeader>
<Header>
<HeaderRow title="Find TV Shows">
</HeaderRow>
<HeaderRow>
<div className="search">
<span className="material-icons search-icon">search</span>
<input type="text" className="form-control" placeholder="Name of show(e.g Friends)" />
</div>
</HeaderRow>
</Header>
<Drawer title="Title">
</Drawer>
<Content>
<div className="page-content" />
<MovieList movies={this.state.movie} />
</Content>
</Layout>
</div>
</div>
);
}
}
export default App;
MovieListItem.js
const MovieListItem = ({movie}) => {
const imageUrl = movie.imageUrl;
const mainCast = _.map(movie.mainCast, (cast) => {
return(
<li className="list-item">
{cast}
</li>
)
})
return (<li className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-object" src={imageUrl} alt={movie.title} />
</div>
<div className="media-body">
<div className="media-heading">
<h4 className="title">{movie.title}</h4>
</div>
<div className="main-cast">
<ul id="cast-list">
{mainCast}
</ul>
</div>
<div className="reviewer">
<div className="col-sm-3 col-xs-3">
<img src={imdb} className="reviewer-img img-responsive" alt="imdb" />
</div>
<div className="col-sm-3 col-xs-3">
<p className="reviewer-score">{movie.imdb}</p>
</div>
<div className="col-sm-3 col-xs-3">
<img src={rottenTomatoes} className="reviewer-img img-responsive" alt="rottenTomatoes"/>
</div>
<div className="col-sm-3 col-xs-3">
<p className="reviewer-score">{movie.rottenTomatoes}</p>
</div>
</div>
</div>
</div>
</li>
)
};
export default MovieListItem;

Categories