I'm trying to make a simple dropdown menu but I have some problems with onClick event on list items which are in child component, the 'handleClick' function is suppose to trigger when click on a list item, but the function is trigged twice when page loaded (because there are two lists here), and when I click on list again, it will trigger twice again, I'm sure it's a simple question but I passed few hours on it but I don't really understand what it happen here, hope someone can help me please, thx!
here is my code:
import React from 'react';
import Dropdown from './../elements/dropdown.js!jsx';
export class TestPage extends React.Component {
constructor() {
super();
this.noteLIst = [
{name: 'note', label: 'Note global'},
{name: 'digitalCulture', label: 'Digital Culture'}
];
}
handleItemClick(company) {
console.log(company);
}
render() {
return (
<div className="widget">
<Dropdown list={this.noteLIst} selected={this.noteLIst[0]} whenItemClicked={this.handleItemClick} />
</div>
);
}
}
Dropdown.js
import React from 'react';
export class Dropdown extends React.Component {
constructor() {
super();
this.state = {
listVisible: false
};
}
showMenu() {
this.setState({
listVisible: true
});
document.addEventListener("click", this.hide);
}
hide() {
this.setState({
listVisible: false
});
document.removeEventListener("click", this.hide);
}
handleClick(item) {
console.log(item); // Here will log 2 items
this.props.whenItemClicked(item);
}
render() {
let listItems = _.map(this.props.list, (list, index) => {
return (
<li key={index} className={list} onClick={this.handleClick(list)}>{list.name}</li>
);
});
return (
<div className = {"dropdown-container" + (this.state.listVisible ? " show" : "")}>
<div className = {"dropdown-display" + (this.state.listVisible ? " clicked" : "")} onClick = {this.show}>
<span>
<img className="icon-arrow-bottom" src="build/image/arrow_bottom_black.png" />
</span>
<span>
{this.props.selected.name}
</span>
</div>
<ul className="dropdown-list" >
{listItems}
</ul>
</div>
);
}
}
I'm trying to make a simple dropdown menu but I have some problems with onClick event on list items which are in child component, the 'handleClick' function is suppose to trigger when click on a list item, but the function is trigged twice when page loaded (because there are two lists here), and when I click on list again, it will trigger twice again, I'm sure it's a simple question but I passed few hours on it but I don't really understand what it happen here, hope someone can help me please, thx!
here is my code:
import React from 'react';
import Dropdown from './../elements/dropdown.js!jsx';
export class TestPage extends React.Component {
constructor() {
super();
this.noteLIst = [
{name: 'note', label: 'Note global'},
{name: 'digitalCulture', label: 'Digital Culture'}
];
}
handleItemClick(company) {
console.log(company);
}
render() {
return (
<div className="widget">
<Dropdown list={this.noteLIst} selected={this.noteLIst[0]} whenItemClicked={this.handleItemClick} />
</div>
);
}
}
Dropdown.js
import React from 'react';
export class Dropdown extends React.Component {
constructor() {
super();
this.state = {
listVisible: false
};
}
showMenu() {
this.setState({
listVisible: true
});
document.addEventListener("click", this.hide);
}
hide() {
this.setState({
listVisible: false
});
document.removeEventListener("click", this.hide);
}
handleClick(item) {
console.log(item); // Here will log 2 items
this.props.whenItemClicked(item);
}
render() {
let listItems = _.map(this.props.list, (list, index) => {
return (
<li key={index} className={list} onClick={this.handleClick(list)}>{list.name}</li>
);
});
return (
<div className = {"dropdown-container" + (this.state.listVisible ? " show" : "")}>
<div className = {"dropdown-display" + (this.state.listVisible ? " clicked" : "")} onClick = {this.show}>
<span>
<img className="icon-arrow-bottom" src="build/image/arrow_bottom_black.png" />
</span>
<span>
{this.props.selected.name}
</span>
</div>
<ul className="dropdown-list" >
{listItems}
</ul>
</div>
);
}
}
Share
Improve this question
asked Oct 4, 2015 at 19:14
Tachun LinTachun Lin
9723 gold badges11 silver badges18 bronze badges
3 Answers
Reset to default 12When you use the onClick event and you want to pass parameters to the handler method, you have to use the function bind
. Thus, the handler will be triggered only when you click on the list item.
onClick={this.handleClick.bind(this, list)}
This would be your Dropdown.js:
import React from 'react';
export class Dropdown extends React.Component {
constructor() {
super();
this.state = {
listVisible: false
};
}
showMenu() {
this.setState({
listVisible: true
});
document.addEventListener("click", this.hide);
}
hide() {
this.setState({
listVisible: false
});
document.removeEventListener("click", this.hide);
}
handleClick(item) {
console.log(item); // it will be log 1 item when you click
this.props.whenItemClicked(item);
}
render() {
let listItems = _.map(this.props.list, (list, index) => {
return (
<li key={index} className={list} onClick={this.handleClick.bind(this, list)}>{list.name}</li>
);
});
return (
<div className = {"dropdown-container" + (this.state.listVisible ? " show" : "")}>
<div className = {"dropdown-display" + (this.state.listVisible ? " clicked" : "")} onClick = {this.show}>
<span>
<img className="icon-arrow-bottom" src="build/image/arrow_bottom_black.png" />
</span>
<span>
{this.props.selected.name}
</span>
</div>
<ul className="dropdown-list" >
{listItems}
</ul>
</div>
);
}
}
It should be noted that this works even when the function does not belong to this
! Example:
config/GetFireBaseConfigFromEnv.ts
import dotenv from "dotenv";
import path from "path";
import { FirebaseOptions } from "firebase/app";
const GetFireBaseConfigFromEnv = (): => {
dotenv.config({path: path.resolve(__dirname, "../.env.development")});
const config: FirebaseOptions = {
apiKey: process.env.API_KEY,
appId: process.env.APP_ID,
authDomain: process.env.AUTH_DOMAIN,
measurementId: process.env.MEASUREMENT_ID,
messagingSenderId: process.env.MESSAGING_SENDER_ID,
projectId: process.env.PROJECT_ID,
storageBucket: process.env.STORAGE_BUCKET
};
return config;
}
export default GetFireBaseConfigFromEnv;
auth/SignInWithGoogle.ts
import { GoogleAuthProvider, Auth, signInWithPopup } from "firebase/auth";
const SignInWithGoogle = (auth: Auth): void => {
const provider: GoogleAuthProvider = new GoogleAuthProvider(auth);
signInWithPopup(auth, provider);
}
export default SignInWithGoogle;
components/app.ts
import React, { JSX } from "react";
import GetFireBaseConfigFromEnv from "config/GetFireBaseConfigFromEnv";
import SignInWithGoogle from "auth/SignInWithGoogle";
const App: React.FC = (): JSX.Element => {
return (
<>
<div>
<h1>App</h1>
<button onClick={SignInWithGoogle.bind(this, GetFireBaseConfigFromEnv())}
value="Login with Google"/>
</div>
</>
);
}
To work it out easily with functional components, since the class components became almost deprecated as of React v16, call the handler function from the body of an arrow function. I called the handler function (handleSearch) from inside a new arrow function because my handler function was an async function which returns a result so it was being called on every component render rather than on click event.
<button style={{height:'55px'}} onClick={ () => {
handleSearch(search, fetchData, exerciseOptions, setSearch, setSearchedExercises, setShowHeaderTitle, setBodyPart)}}>
Search
</button>