In my navbar, I have a button that will display a submenu (list of items) when clicked. Each item is their own child component and when clicked I want them to fire an event. The onClick event listener is not responding at all. However, other mouse events do apply (onMouseEnter, onMouseOut etc). Anyone might know what's up?
Child Component: NotificationItem.js
import React from "react"
import { connect } from "react-redux"
import { updateNotification } from "../../actions/notificationActions"
class NotificationItem extends React.Component{
constructor(props){
super(props)
this.handleOnClick = this.handleOnClick.bind(this)
}
handleOnClick = (event) => {
console.log("clicked")
// let notificationId = this.props.notification._id
// this.props.updateNotification(notificationId)
}
render(){
let {avatar, description, seen} = this.props.notification
return(
<div
onClick={this.handleOnClick}
className="d-flex notification-wrapper"
style={ seen ? (
{ width: "250px", whiteSpace: "normal", padding: "0.5rem" }
):( { width: "250px", whiteSpace: "normal", padding: "0.5rem", backgroundColor: "#d7e2f4" }
)
}
>
<div>
<img src={avatar} style={{ width: "25px"}} className="mr-2 rounded-circle"/>
</div>
<div>
{description}
</div>
</div>
)
}
}
Parent component: NotificationFeed.js
import React from "react"
import { connect } from "react-redux"
import NotificationItem from "./NotificationItem"
class NotificationFeed extends React.Component{
constructor(props){
super(props)
}
render(){
let notifications = this.props.notification.notifications
return(
<div className="dropdown-menu">
{notifications.map((notification, index) => {
return(
<div key={index}>
<NotificationItem notification={notification}/>
</div>
)
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return{
notification: state.notification
}
}
export default connect(mapStateToProps)(NotificationFeed)
Edit: Something I noticed that might be of help. I'm using a bootstrap class to create this dropdown toggle-effect. When clicking on one of the items, the submenu closes immediately, without firing my desired event handler on the component.
<span className="dropdown" id="notifications-dropdown">
<Link to="#" className="nav-link text-light dropdown-toggle" data-toggle="dropdown">
<span
key={Math.random()}
>
<i className="fa fa-bell"></i>
</span> { windowWidth < 576 && "Notifications"}
<NotificationFeed/>
</Link>
</span>
For those still interested, this was a problem with Bootstrap. Because the elements were created inside a Bootstrap dropdown it had some logic I couldn't see. Whenever I would click on an element, the dropdown closes before the event-handler would even fire.
Opted, to create my own dropdown instead. Thanks all!
You created an arrow function, you do not need to bind it in the constructor
import React from "react"
import { connect } from "react-redux"
import { updateNotification } from "../../actions/notificationActions"
class NotificationItem extends React.Component{
state = {}
handleOnClick = (event) => {
console.log("clicked")
}
//or do not use arrow function then bind in the constructor
//constructor(props) {
//super(props);
//this.handleOnClick = this.handleOnClick.bind(this)
//}
// handleOnClick(event) {
// console.log("clicked")
// }
render(){
let {avatar, description, seen} = this.props.notification
return(
<div
onClick={this.handleOnClick}
className="d-flex notification-wrapper"
style={ seen ? (
{ width: "250px", whiteSpace: "normal", padding: "0.5rem" }
):( { width: "250px", whiteSpace: "normal", padding: "0.5rem", backgroundColor: "#d7e2f4" }
)
}
>
<div>
<img src={avatar} style={{ width: "25px"}} className="mr-2 rounded-circle"/>
</div>
<div>
{description}
</div>
</div>
)
}
try this
onClick={ (e) => this.handleOnClick(e)}
Try change your code, now it's like method:
handleOnClick(event){
console.log("clicked")
}
Related
so i'm trying to implement the line-through feature while having a delete button. clicking on the li text crosses out the item, and clicking on the del button removes it.
functionally, it works. the issue is when I delete an itme, say "2", it will apply the line-through style to the list item below it. i'm guessing this is because "onClick" is detected twice - both inside the list item and the button (because the button is technically nested within the list item). the moment I press on the DEL button for 2, the onClick is detected for list item 3, applying the line-through style. what would be the best way to go about correcting this?
my code with an App component and ListItem component:
import React, { useState } from "react";
import ListItem from "./ListItem";
function App() {
const [inputText, setInputText] = useState("");
const [items, setItems] = useState([]);
function handleChange(event) {
const newValue = event.target.value;
setInputText(newValue);
}
function addItem() {
setItems((prevItems) => {
return [...prevItems, inputText];
});
setInputText("");
}
function deleteItem(id) {
setItems((prevItems) => {
return prevItems.filter((item, index) => {
return index !== id;
});
});
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<input onChange={handleChange} type="text" value={inputText} />
<button onClick={addItem}>
<span>Add</span>
</button>
</div>
<div>
<ul>
{items.map((todoItem, index) => (
<ListItem
key={index}
id={index}
item={todoItem}
delete={deleteItem}
/>
))}
</ul>
</div>
</div>
);
}
export default App;
____________________________________________________________________________________________________
import React, { useState } from "react";
function ListItem(props) {
const [clickedOn, setClickedOn] = useState(false);
function handleClick() {
setClickedOn((prevValue) => {
return !prevValue;
});
}
return (
<div>
<li
onClick={handleClick}
style={{ textDecoration: clickedOn ? "line-through" : "none" }}
>
{props.item}
<button
onClick={() => {
props.delete(props.id);
}}
style={{ float: "right" }}
>
<span>Del</span>
</button>
</li>
</div>
);
}
export default ListItem;
As you already wrote, user events are propagated up the DOM tree. To stop the propagation, you can use event.stopPropagation() ref in your event handler
<button
onClick={(event) => {
event.stopPropagation();
props.delete(props.id);
}}
style={{ float: "right" }}
>
Line 11:5: 'state' is not defined no-undef
Line 15:5: 'handleToggle' is not defined no-undef
I don't understand why it shows me these errors, please help me resolve this, I would also appreciate an explanation
const Footer = () => {
state = {
langContent: false
}
handleToggle = (e) => {
e.preventDefault();
this.setState({
langContent: !this.state.langContent
})
}
return (
<FooterContainer>
<span style={{ marginLeft: '15%', fontSize: '1.125rem' }}>
Questions?
<Link> Call 1-877-742-1335</Link>
</span>
{/* Language Button */}
<div className= "lang-btn" onClick={this.handleToggle}>
<Icon icon={iosWorld} size={20}/>
English
<Icon icon={arrowSortedDown} />
</div>
{/* Toggle Language Content */}
{this.state.langContent && (
<div className="lang-toggle">
<ul>
<li>English</li>
</ul>
<ul>
<li>Hindi</li>
</ul>
</div>
)}
<span style={{ marginLeft: '15%', fontSize: '0.9rem'}}>
Netflix India
</span>
</FooterContainer>
)
}
I think you are confusing the syntax for using state in functional components with the syntax for using states in class components.
To use state in functional components, use it like this: (also you forgot to declare const before the function handleToggle, here you are declaring a function local variable thus const is needed. You are confusing it with declaring a method in a class)
const Footer = () => {
const [state, setState] = useState({ langContent: false })
const handleToggle = (e: { preventDefault: () => void; }) => {
e.preventDefault();
setState({
langContent: state.langContent
})
}
return (
<FooterContainer>
<span style={{ marginLeft: '15%', fontSize: '1.125rem' }}>
Questions?
<Link> Call 1-877-742-1335</Link>
</span>
{/* Language Button */}
<div className= "lang-btn" onClick={this.handleToggle}>
<Icon icon={iosWorld} size={20}/>
English
<Icon icon={arrowSortedDown} />
</div>
{/* Toggle Language Content */}
{state.langContent && (
<div className="lang-toggle">
<ul>
<li>English</li>
</ul>
<ul>
<li>Hindi</li>
</ul>
</div>
)}
<span style={{ marginLeft: '15%', fontSize: '0.9rem'}}>
Netflix India
</span>
</FooterContainer>
)}
If you want to use functional component style, read more about it here: React docs-Using the state hook
The component has been created as a functional component, which does not have state, to fix this issue you can use the useState hook.
const Footer = () => {
const [langContent, setLangContent] = useState(false)
const handleToggle = (e) => {
e.preventDefault();
setLangContent(!langContent);
}
return (
... // Use existing Code
)
}
If you want to continue to using class based components then you should use a class that extends React.Component
class Footer extends React.Component {
constructor(props) {
super(props);
this.state = {
langContent: false
};
}
render() {
... //Use existing Code
}
Additional Reading:
React Docs for hooks-state
There are two components which don't have parent-child or sibling relationship between them.
One of them build the Toolbar and another one contains a color picker. The idea is to change the color of the Toolbar based on the value set in the color picker.
Here is my code so far:
import React from 'react';
import { Button, Icon } from 'semantic-ui-react';
import { ChromePicker } from 'react-color';
export default class Banner extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
displayColorPicker: false,
background: '#fff',
};
}
handleClick = () => {
this.setState({ displayColorPicker: true });
};
handleClose = () => {
this.setState({ displayColorPicker: false });
};
handleChange = color => {
this.setState({ background: color.hex });
};
handleChangeComplete = color => {
this.setState({ background: color.hex });
};
render() {
const popover = {
position: 'absolute',
zIndex: '2',
};
const cover = {
position: 'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
};
return (
<div className="banner-container settings-banner">
<table className="settings-banner-container">
<tbody>
<tr className="setttings-container-tr">
<div
className="xx"
style={{ backgroundColor: this.state.background }}>
<div className="title-cell-value settings-banner-title">
Brand color
</div>
<div>
<Button onClick={this.handleClick}>Pick Color</Button>
{this.state.displayColorPicker ? (
<div style={popover}>
<div
style={cover}
onClick={this.handleClose}
onKeyDown={this.handleClick}
role="button"
tabIndex="0"
aria-label="Save"
/>
<ChromePicker
color={this.state.background}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
</div>
) : null}
</div>
</div>
</tr>
</tbody>
</table>
</div>
);
}
}
In the above file, the ChromePicker is used to choose a color and save its value in this.state.background. I'm using that value to update the color of div with class xx. This works good, the div's color is updated directly.
However, I don't know how to "export" that color value outside and use it in another component.
In this case it would be the Toolbar, I want to send the value from this.state.background to the style = {{ .. }}
Is there a way to do it?
import React from 'react';
import Logo from '../Logo/Logo';
export default class Toolbar extends React.PureComponent {
render() {
return (
<div className="corporate-toolbar" style={{ backgroundColor: 'green' }}>
<Logo corporate />
</div>
);
}
}
There is many ways to do it
You can use context(best solution), redux(if you app is really big) or just move the property to the common parent and pass it to components (it's the worst way, not recommended)
Documentation for context - https://reactjs.org/docs/context.html
Documentation for redux - https://react-redux.js.org
A simple example of using context https://www.digitalocean.com/community/tutorials/react-usecontext
Here is a working example using context:
//in file ColorContext.js (should export but breaks snippet)
const ColorContext = React.createContext();
const ColorProvider = ({ children }) => {
const [color, setColor] = React.useState('#fff');
return (
<ColorContext.Provider value={{ color, setColor }}>
{children}
</ColorContext.Provider>
);
};
//in file Banner.js
class Banner extends React.PureComponent {
handleChange = (color) => {
this.context.setColor(color);
};
render() {
return (
<div style={{ backgroundColor: this.context.color }}>
<select
value={this.context.color}
onChange={(e) =>
this.handleChange(e.target.value)
}
>
<option value="#fff">fff</option>
<option value="#f00">f00</option>
<option value="#f0f">f0f</option>
</select>
</div>
);
}
}
//ColorContext is imported from ColorContext.js
Banner.contextType = ColorContext;
//in file Toolbar.js
class Toolbar extends React.PureComponent {
render() {
return (
<h1 style={{ backgroundColor: this.context.color }}>
Toolbar
</h1>
);
}
}
//ColorContext is imported from ColorContext.js
Toolbar.contextType = ColorContext;
const App = () => (
<div>
<Banner />
<Toolbar />
</div>
);
ReactDOM.render(
<ColorProvider>
<App />
</ColorProvider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I want to roll my own custom alert, but don't want to touch the render or state of components that currently call the default window.alert().
I'm using React 15.x
function injectDialogComponent(message: string){
const modal = <Modal>{message}</Modal>
document.body.appendChild(modal) //this errors, but how would I do something like this?
}
I've tried
ReactDOM.render(modal,document.body.appendChild(document.createElement('div)
but doesn't work
You can create you html node, append it to you root element and then append your React <Modal> in you newly created div modal :
React.render(<Modal>{message}</Modal>, document.getElementById('modal'))
class Modal extends React.Component {
render() {
return (
<dialog id='modal' style={{width: "80%", height: "80%", marginTop: 10, backgroundColor: '#eee'}} open
>
<p>{this.props.children}</p>
</dialog>
);
}
}
class App extends React.Component {
openModal = message => {
let modal = document.createElement('div');
modal.id = 'modal';
document.getElementById('root').appendChild(modal)
React.render(<Modal>{message}</Modal>, document.getElementById('modal'));
}
render() {
return (
<div>
<button onClick={() => this.openModal("Hello")}>Open Modal</button>
</div>
);
}
}
React.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="root"></div>
I'm trying to change display of div.Filters on click of plus or minus button. The button changes on click, if it's a + changes to -, and vice versa.
Here's the code:
export class NavLeftMobile extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
isToggle: true
};
}
handleClick(e) {
this.setState({
isToggle: !this.state.isToggle
});
}
render() {
let button;
if (this.state.isToggle) {
button = (
<button className="Menos" onClick={() => this.handleClick()}>
-
</button>
);
} else {
button = (
<button className="Menos" onClick={() => this.handleClick()}>
+
</button>
);
}
return (
<div>
<div className="NavLeftMobile">
<h1>categories</h1>
<h3>Manufacturer{button}</h3>
<div
className="Filters"
style={{ display: this.state.isToggle ? "block" : "none" }}
>
<p>Lusograph (10)</p>
</div>
<h3>Color{button}</h3>
<div
className="Filters"
style={{ display: this.state.isToggle ? "block" : "none" }}
>
<p>Black (10)</p>
</div>
</div>
</div>
);
}
}
So, when clicking on one of the buttons, handleClick function is called and the display from div.Filters changes, if it's block to none and vice versa.
Basically, I want to toggle each section independent from each other.
If you want each section to be Collapsible and indepent, then the state should not live on your wrapper component, instead you can create another class component that keeps track of it.
class Collapsible extends React.Component {
constructor(props) {
super(props);
this.state = {
collapsed: true
};
}
onToggle = () => {
const { collapsed } = this.state;
this.setState(() => ({
collapsed : !collapsed
}))
}
render () {
const { title, children } = this.props;
const { collapsed } = this.state;
return (
<div>
<h3>
{title}
<a onClick={this.onToggle}>
{collapsed ? '+' : '-'}
</a>
</h3>
<div style={{
display : collapsed ? "block" : "none"
}}>
{children}
</div>
</div>
);
}
}
class NavLeftMobile extends React.Component {
render() {
return (
<div>
<div className="NavLeftMobile">
<h1>categories</h1>
<Collapsible title="Manufacturer">
Lusograph (10)
</Collapsible>
<Collapsible title="Color">
Black (10)
</Collapsible>
</div>
</div>
);
}
}
ReactDOM.render(
<NavLeftMobile/>,
document.querySelector('#app')
);