onClick event never fires in React Child - javascript

I have a React-Redux application and I'm having issues launching a function on component click. When the logout button is clicked, it should trigger onClick={this.handleLogout}. However, Redux dev tools and adding a console log in the handleLogout prove that the function never executes.
Here are the two components, a standard navigation bar and a custom button component. I've left out some imports to shorten the code.
Navigation Bar
class Navigation extends React.Component {
handleLogout = () => {
this.props.logOut();
this.props.history.push("/login");
}
render() {
return (
<nav>
<span className="nav-left">
<Link className="light" to="/">
Home
</Link>
</span>
<span className="nav-right">
{this.props.authenticated ? (
<>
<Button to="/account">Account</Button>
<Button onClick={this.handleLogout} buttonStyle="danger">
Log Out
</Button>
</>
) : (
<Button to="/login">Login</Button>
)}
</span>
</nav>
);
}
}
Navigation.propTypes = {
authenticated: PropTypes.bool.isRequired,
logOut: PropTypes.func.isRequired
};
export default withRouter(Navigation);
Button
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}
Component Dev Tools
component dev tools

You forgot to pass onClick to the span that represents a button without a link
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span onClick={this.props.onClick} className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}

Related

React JS - How does 2 separated components able to receive 1 same state?

I am a beginner in using the React JS framework. Based on the official React JS documentation, an example is given for changing the state of a component that has a connected hierarchy. But in my case this time I split the components for Header and Main separately.
index.js
ReactDOM.render(
<React.StrictMode>
<Header />
<Main />
</React.StrictMode>,
document.getElementById('root')
);
In the Header component I also have another sub component that functions to activate / deactivate the sidebar which is also a sub menu for the Main component.
Header.js
import { BtnSidebarOnClick } from './Sidebar';
const Header = () => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebarOnClick />
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
export default Header;
Main.js
import { Sidebar } from './Sidebar';
const Main = () => {
return (
<main className="main">
<Sidebar />
<div className="main__content">
...
</div>
</main>
);
}
export default Main;
Notice that the BtnSidebarOnClick and Sidebar components are not connected. In my case, this time I want to make the Sidebar component accept state to detect whether the button contained in the BtnSidebarOnClick component is clicked / not.
Sidebar.js
class BtnSidebarOnClick extends React.Component {
constructor(props) {
super(props);
this.state = { onClick: false };
}
handleClick() {
this.setState(state => ({ onClick: !state.onClick }));
}
render() {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={this.handleClick.bind(this)}>
<i className="material-icons">menu</i>
</div>
</div>
);
}
}
const Sidebar = () => {
return (
<div className="main__sidebar"> {/* set style if BtnSidebarOnClick clicked */}
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}
export { Sidebar, BtnSidebarOnClick };
So how do you set these two components to receive the same state?
TLDR; You should pull out the button state into the parent and pass it into the children component.
By the way, it is a common way to have file App.js for your main Application file. In your case, it should be like this:
index.js
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = { isClicked: false };
}
handleClick() {
this.setState(state => ({ isClicked: !state.isClicked }));
}
render() {
return (
<div>
<Header onClick={this.handleClick} /> // --> notice
<Main isClicked={this.state.isClicked} /> // --> notice
</div>
)
}
}
Header.js
import BtnSidebar from './BtnSidebar';
const Header = (props) => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebar onClick={props.onClick} /> // --> notice
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
Main.js
import Sidebar from './Sidebar';
const Main = (props) => {
return (
<main className="main">
<Sidebar isClicked={props.isClicked} /> // --> notice
<div className="main__content">
...
</div>
</main>
);
}
BtnSidebar.js
const BtnSidebar = (props) => {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={props.onClick} // --> notice
>
<i className="material-icons">menu</i>
</div>
</div>
);
}
Sidebar.js
const Sidebar = (props) => {
return (
<div
className={
props.isClicked ? 'main__sidebar-clicked' : 'main__sidebar' // --> notice
}
>
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}

Rendering component on long mouse click

I am trying to render a modal component on a long mouse click. If I just try to fire an alert it works but rendering doesn't seem to do the trick. I am assuming maybe If I have to return? Not quite sure. I created a function handleButtonPressDown to perform this task and the handleButtonRelease to clear interval in the event the user decides not to perform this action.
export class Dropdown extends React.Component<IProps> {
buttonPressTimer: any;
constructor(props: IProps) {
super(props);
this.handleButtonPress = this.handleButtonPress.bind(this);
this.handleButtonRelease = this.handleButtonRelease.bind(this);
}
public render() {
return (
<div style={{ alignSelf: "center" }}>
<ul className="nav nav-pills">
{filteredProbes.length === 0 ? (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
display: "none"
}}
></div>
</li>
) : (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
position: "relative",
willChange: "transform",
top: "5px",
overflowY: "scroll",
maxHeight: "200px",
color: "white"
}}
>
{this.props.searchState.isActive === false
? probes.map(probe => (
<a
onClick={() => this.props.onUpdateSelectedProbe(probe)}
className="dropdown-item"
onMouseDown={this.handleButtonPress}
onMouseUp={this.handleButtonRelease}
>
<div
className="dropdown-divider"
style={{ backgroundColor: "black" }}
></div>
{probe.companyPN}: {probe.description}
</a>
))
: filteredProbes.map(filterprobe => (
<a
onClick={() =>
this.props.onUpdateSelectedProbe(filterprobe)
}
className="dropdown-item"
>
<div className="dropdown-divider"></div>
{filterprobe.companyPN}: {filterprobe.description}
</a>
))}
</div>
</li>
)}
</ul>
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
{/* Show the modal if showModal is true */}
this.props.modalState.showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
You need to move the code that you have inside setTimeout to render function and use state to render WedgeGroup:
export class Dropdown extends React.Component<IProps> {
...
constructor(props: IProps) {
super(props);
this.state = {
showModal: false
};
...
}
public render() {
const showModal = this.props.modalState.showModal &&
this.state.showModal;
return (
<div style={{ alignSelf: "center" }}>
{
showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}
//..... render other components
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
this.setState({
showModal: true
})
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
It will not render firstly because your are not triggering any mechanism that makes React render.
I'd suggest to you to remove this component from the setTimeout, place it inside the render (where it should be).
And finally manipulate your component state to show or hide your modal.
If you trigger a timer to show the modal view it will only appear after the change of the state, so in your case it will take 1s to show to the user, what may look not responsive.
// inside your handleButtonPress()
this.setState({
showModal: true
}}

Passing props.children to a conditionally rendered component

I have Product component that renders a modal component based on a showModal boolean value in the Product component state. This is the render method in Product
render() {
const { .... } = this.props.data;
return (
<>
{this.state.showModal && (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
/>
)}
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
</>
);
}
I want to pass down the content inside li to the Modal.How can I make use of props.children in this case for the Modal component, so I dont have to pass down the data to be displayed as props ?
If Modal is another component then you can pass the list as
render() {
const { .... } = this.props.data;
return (
<>
{this.state.showModal && (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
>
<ListItems {...this.props}/>
</Modal>
)}
<ListItems {...this.props}/>
</>
);
}
Let the ListItems be a separate component as
class ListItems extends React.Component {
render() {
return(
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
)}
}
class ListItems extends React.Component {
render(){
return(
<li className="product-body">
<img
src=".."
onClick={this.onToggleModal}
/>
<h2>{...}</h2>
<h3>{..}</h3>
<h3>{..}</h3>
<h2 className="product-price">{...}</h2>
<button type="button" onClick={this.props.onAddToCart}>
Buy now
</button>
</li>
)
}
}
class Wrapper extends React.Component {
render(){
return (
<Modal
product={this.props.data}
toggleModal={this.onToggleModal}
addToCart={this.props.onAddToCart}
>
<ListItems />
</Modal>
)
}
}
class Modal extends React.Component {
render(){
return (
<div>
<h1>Modal Title</h1>
<div>{this.props.children}</div>
</div>
)
}
}

React Tippy - Using method setIsOpen()

I'm using React Tippy - a React component based on Tippy.js. I want to use the documented setIsOpen method - but it's not working.
TypeError: setIsOpen is not a function.
Can't seem to find any documentation or issues related to this. Any ideas?
My code is:
<Tooltip
position="right"
animation="scale"
arrow="true"
arrowSize="big"
theme="light"
trigger="click focus"
interactive
open={open}
html={(
<div className="tooltip-body">
<span className="info icon-sm-info"></span>
<span className="close" onClick={() => { setIsOpen(false) }}>×</span>
<h5>Hello</h5>
<div>Tooltip Content</div>
</div>
)}
>
<span className="icon-sm-info">Hello</span>
</Tooltip>
your state :
this.state = {open : false}
setIsOpen = () => {
this.setState(state => { open : true});
}
Updated Code
<Tooltip
position="right"
animation="scale"
arrow="true"
arrowSize="big"
theme="light"
trigger="click focus"
interactive
open={open}
html={(
<div className="tooltip-body">
<span className="info icon-sm-info"></span>
<span className="close" onClick={() => { this.setIsOpen() }}>×</span>
<h5>Hello</h5>
<div>Tooltip Content</div>
</div>
)}
>
<span className="icon-sm-info">Hello</span>
</Tooltip>
I found out how to do it by using state.
class ToolTip extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
setIsOpen = (option) => {
this.setState({
open: option
});
}
<span className="close" onClick={() => {this.setIsOpen(false)}}>×</span>

pass value of clicked list to EditForm to edit

I am doing reactjs and redux for developing dashboard. I have done add, delete but editing is not working. When user clicks on item, the textfield should display with its current value and able to submit the changes. I can show textfield when clicked but could not show the current value of that item which is clicked. To display textField i have to use onClick on the li tag otherwise i could pass data like using this.props.editTab(tab). How can i now send data of clicked item to editTab action ?
constructor(props) {
super(props);
this.state = { open: false, editing: false };
}
editTab() {
const tabs = _.map(this.props.tabs, (tab) => {
if (tab.editable) {
return (
<li
className="list-group-items delete-tab-list"
onClick={() => this.setState({ editing: true })}
key={tab.id}
>
<i className="material-icons">{tab.icon}</i>{tab.name}
</li>
);
}
});
return (
<div className="device-action">
<Dialog
title="Update a Tab"
modal={false}
bodyStyle={{ background: '#fff' }}
contentStyle={customContentStyle}
actionsContainerStyle={{ background: '#fff' }}
titleStyle={{ background: '#fff', color: '#1ab394' }}
open={this.props.createTab.open}
onRequestClose={this.props.closeTabIcon}
>
<ul className="list-group">
{ this.state.editing ?
<EditForm
tab={this.props.tabs}
editing={this.state.editing}
/> :
tabs
}
</ul>
</Dialog>
</div>
);
}
handleEditSave = (name, icon) => {
this.props.editTab(name, icon);
}
render() {
return (
<div>
<form onSubmit={this.handleEditSave}>
<div className="tab-name">
<TextField
floatingLabelText="Name"
onChange={(name) => { this.setState({ name: name.target.value }); }}
/>
</div>
<div className="icon">
<AutoComplete
floatingLabelText="select any icon"
filter={AutoComplete.noFilter}
openOnFocus
onNewRequest={(e) => { this.setState({ icon: e.id }); }}
/>
</div>
<button className="btn">Save</button>
</form>
</div>
);
}
How can i pass clicked item data to EditForm component so i can trigger my action in this.props.editTab(tab) this way ?
You can simply track the tab you editing by saving it on the state.
This will work only if you want to edit 1 tab at time. otherwise you can use Object/Array.
constructor(props) {
super(props);
this.state = { open: false, editing: null };
}
editTab() {
const tabs = _.map(this.props.tabs, (tab) => {
if (tab.editable) {
return (
<li
className="list-group-items delete-tab-list"
onClick={() => this.setState({ editing: tab })}
key={tab.id}
>
<i className="material-icons">{tab.icon}</i>{tab.name}
</li>
);
}
});
const { editing } = this.state;
// editing is the Tab object that we edit
if (editing)
console.log("Editing tab: " + editable.name);
return (
<div className="device-action">
<Dialog
title="Update a Tab"
modal={false}
bodyStyle={{ background: '#fff' }}
contentStyle={customContentStyle}
actionsContainerStyle={{ background: '#fff' }}
titleStyle={{ background: '#fff', color: '#1ab394' }}
open={this.props.createTab.open}
onRequestClose={this.props.closeTabIcon}
>
<ul className="list-group">
{ this.state.editing ?
<EditForm
tab={this.props.tabs}
editing={this.state.editing}
/> :
tabs
}
</ul>
</Dialog>
</div>
);
}
handleEditSave = (name, icon) => {
this.props.editTab(name, icon);
}
render() {
return (
<div>
<form onSubmit={this.handleEditSave}>
<div className="tab-name">
<TextField
floatingLabelText="Name"
onChange={(name) => { this.setState({ name: name.target.value }); }}
/>
</div>
<div className="icon">
<AutoComplete
floatingLabelText="select any icon"
filter={AutoComplete.noFilter}
openOnFocus
onNewRequest={(e) => { this.setState({ icon: e.id }); }}
/>
</div>
<button className="btn">Save</button>
</form>
</div>
);
}

Categories

Resources