React - Event bubbling - javascript

I am using react. I am not quite how should I write a event handling so when I click on a, I can alert data-href. Right now if I click on div or img, it will trigger an event, but since the target is actually not a it will alert null, which is not what I want.
test(e){alert(e.target.getAttribute('data-href'))}
<div>
<ui>
<li >
<a data-href = 'http://cnn.com' onClick = {e = > this.test(e)>
<div>
<img src = 'someimage.jpg'}>
hi there
</div>
</a>
<li>
</ui>
</div>
edit:
Something like this
conversationList() & selectConversation() is what I am talking about here~
import React from 'react';
import { connect, Provider } from 'react-redux';
import fetch from 'isomorphic-fetch';
import * as actions from './actions/index';
import io from 'socket.io-client';
class Chat extends React.Component{
constructor(props){
super(props);
this.state = {recipientId: '', messageBuffer:'asdfadsfasdf'};
this.userList = this.userList.bind(this);
this.changeRecipient = this.changeRecipient.bind(this);
this.insertText = this.insertText.bind(this);
}
componentWillMount(){
this.props.loadConversationsSocket();
this.props.loadConversations();
this.props.loadRecipients();
}
participantsNames(participants){
console.log('in par ');
console.log(participants);
return participants.map(participant => (<div key= {participant._id}>{participant.name}</div>));
}
selectConversation(e){
e.preventDefault();
console.log(this.tagName);
let data = this.getAttribute('data-href');
alert(data);
}
conversationList(){
if(!(this.props.conversations.length === 0)){
console.log('in conversation if');
return this.props.conversations.map(conversation =>(<li key = {conversation.conversation._id} >
<a data-href = {'http://localhost:8000/messaages/'+conversation.conversation._id} onClick = {(e)=>this.selectConversation(e)}>
<div>
participants: {this.participantsNames(conversation.conversation.participants)}
</div>
<div>
{conversation.message[0].auther}
{conversation.message[0].body}
</div>
</a>
</li>))
}
}
render(){
return (
<div>
<ul>
{this.conversationList()}
</ul>
</div>)
}
}
function mapStateToProps(state) {
return { recipients: state.recipients, conversations: state.conversations};
}
export default connect(mapStateToProps, actions)(Chat);

Your onClick handler is attached to the img, not to a. This is the first problem.
Then you should update your code to query event.currentTarget.
event.currentTarget - identifies the current target for the event, as the event traverses
the DOM. It always refers to the element to which the event handler
has been attached, as opposed to event.target which identifies the
element on which the event occurred.
You should write your event handler like this:
class Hello extends React.Component {
test(e){
alert(e.currentTarget.getAttribute('data-href'))
}
render() {
return (
<div>
<a onClick={this.test} data-href="foo">
<img src="https://vignette.wikia.nocookie.net/mrmen/images/5/52/Small.gif/revision/latest?cb=20100731114437" />
<div>clickMe</div>
</a>
</div>
);
}
}
I would suggest also reading this article about handling events.

Related

Use multiple onClick event listeners in one component

Below is App.js
import logo from './logo.svg';
import './App.css';
import MsgState from './components/MsgState';
function App() {
return (
<div className="App">
<MsgState /> {/* made one event listener */}
</div>
);
}
export default App;
In App.js file I make one component MsgState and that JS file as shown below. In MsgState.js file I made 2 state msg and btn and 2 setState functions, I wanted that if I click subscribe button then both state change but only 1 event listener works.
Below is MsgState.js
import React, { Component } from 'react';
class MsgState extends Component {
constructor() {
super();
this.state = {
msg: 'Welcome to all Visitors',
btn: 'Subscribe',
};
}
changeMsg() {
this.setState({
msg: 'Thanks',
});
}
changeBtn() {
this.setState({
btn: 'Subscribed..!!!',
});
}
render() {
return (
<div>
<h1>{this.state.msg}</h1>
<button onClick={(() => this.changeMsg(), () => this.changeBtn())}>{this.state.btn}</button>
</div>
);
}
}
export default MsgState;
If I click subscribe button then only one event listener works that is changeBtn. I wanted that both event listener works while I click subscribe button please help...
onClick={function(event){ func1(); func2()}}
Please change your code as
onClick={
() =>{
this.changeMsg()
this.changeBtn()
}
}
Why you need to call them separately. There can be way to handle this inside one function only. Further extra conditions can be checked. For Example:
render() {
return (
<div>
<h1>{this.state.msg}</h1>
<button
onClick={
() => this.clickListener(),
}>
{this.state.btn}
</button>
</div>
)
}
Now you can write clickListener to call both functions
clickListener(){
this.changeMsg();
this.changeBtn(); // Extra conditions if call both or only one function.
}
Or setting state in a single function (Will save ONE render cycle as well)
clickListener(){
this.setState(
{
msg: "Thanks",
btn: "Subscribed..!!!"
}
)
}

Attaching event to document, that depends on conditionally rendered element in React

I have an input element, that is rendered depending on condition.
render() {
const {
isNameInputVisible,
name
} = this.state;
return (
<div>
{isNameInputVisible ? (
<input
onChange={this.handleNameChange}
ref={this.nameInput}
type="text"
value={name}
/>
) : (
<h1
className="list-header__heading"
onClick={this.handleNameInputVisibility}
>
{name}
</h1>
)}
</div>
)
Basically, I want to listen for a click on the document, to hide the input whenever user click's outside this input element.
Right now I'm doing this:
componentDidMount() {
document.addEventListener('mousedown', this.handleClick);
}
handleClick = event => {
//do some logic
};
But I've been wondering if this is the correct way, because the event exists and fires,
even when the element is not rendered.
So I've tried this:
componentDidUpdate () {
const {isNameInputVisible} = this.state;
isNameInputVisible && document.addEventListener('mousedown', this.handleClick);
}
But it doesn't work.
Question 1:
What is the right way of attaching events to document when it depends on other conditionally rendered elements??
Question 2:
What is the correct way of attaching events, for example, like escape press, for closing dialogs that o etc??
You need to add an event listener in the componentDidMount method only if the conditionally rendered element's ref exists. You can tell if a ref has been attached to an element by using this.refName.current.
The most important thing here is that the input element gets its own lifecycle methods instead of sharing them with a larger component. By moving the input element to its own component with its own lifecycle methods, those methods will only fire around the creation and removal of the input.
// App.jsx
import React from "react"
import ReactDOM from "react-dom"
import CustomInput from "./CustomInput"
class App extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef()
this.toggleInput = this.toggleInput.bind(this)
this.state = {
inputVisible: false
}
}
toggleInput(e) {
this.setState(prevState => ({
inputVisible: !prevState.inputVisible
}))
}
render() {
const { inputVisible } = this.state
return (
<div>
<input type="button" value="toggle input" onClick={this.toggleInput} />
{ inputVisible
? <CustomInput />
: <p>Input is not visible</p>
}
</div>
)
}
}
const rootElement = document.getElementById("root")
ReactDOM.render(<App />, rootElement)
// CustomInput.jsx
import React from "react"
export default class CustomInput extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef()
}
componentDidMount() {
this.inputRef.current &&
document.addEventListener("mousedown", this.handleClick)
}
componentWillUnmount() {
document.removeEventListener("mousedown", this.handleClick)
}
handleClick(e) {
console.log("clicked")
}
render() {
return (
<input type="text" ref={this.inputRef} />
)
}
}
Try it here

How to Target DOM Elements in ReactJS?

Within my React app, I have a sidebar which needs to have a CSS class added to it when the sidebar close button is clicked. I'm using React.createRef() to create a reference to the element, however, I'm receiving the following error:
Here's my code:
import React from 'react';
import './css/Dashboard.css';
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.sidebar = React.createRef();
}
sidebarClose() {
console.log('test');
this.sidebar.className += "hidden";
}
render() {
return (
<div id="dashboard">
<div ref={this.sidebar} id="sidebar">
<img width="191px" height="41px" src="logo.png"/>
<div onClick={this.sidebarClose} className="sidebar-close">X</div>
</div>
</div>
);
}
}
export default Dashboard;
The console.log('test') is just so that I can confirm the function is being executed (which it is).
Thank you.
Instead of manually trying to add a class to a DOM node, you can keep a variable in your state indicating if the sidebar is open and change the value of that when the button is clicked.
You can then use this state variable to decide if the sidebar should be given the hidden class or not.
Example
class Dashboard extends React.Component {
state = { isSidebarOpen: true };
sidebarClose = () => {
this.setState({ isSidebarOpen: false });
};
render() {
const { isSidebarOpen } = this.state;
return (
<div id="dashboard">
<div
ref={this.sidebar}
id="sidebar"
className={isSidebarOpen ? "" : "hidden"}
>
<img
width="191px"
height="41px"
src="logo.png"
alt="craftingly-logo"
/>
<div onClick={this.sidebarClose} className="sidebar-close">
X
</div>
</div>
</div>
);
}
}
I think you forget to bind sidebarClose method to your class in constructor.
constructor(props) {
super(props);
this.sidebar = React.createRef();
this.sidebarClose = this.sidebarClose.bind(this); // here
}

React JS add component to specific clicked li

Can't figure out the way to create the following function. I currently load data (local json) into a li. Upon click I want to add a new component to the clicked li element with data.
Example:
<ul>
<li> 1 </li>
<li> 2 </li>
<li>
3
<div id="data">
<!-- appended data -->
</div>
</li>
</ul>
Upon clicking another li the previous appended element should be removed and added to the newly clicked li. (Row toggle)
If anyone could kick me in the right React direction.
import React, { Component } from 'react';
import Flowers from 'flowers.json';
class Flowers extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
};
this.onClick = this.handleClick.bind(this);
}
handleClick = (e) => {
console.log(this);
console.log(e);
console.log('li item clicked! ' + e.currentTarget);
// appends to clicked div
e.currentTarget.style.backgroundColor = '#ccc';
}
render() {
const List = Flowers.map((flower) =>
<li onClick={this.handleClick.bind(this)} key={flower.id} id={flower.id}>
{flower.name}
</li>
);
return(
<ul>{List}</ul>
);
}
}
class App extends Component {
render() {
return (
<Flowers />
);
}
}
export default App;
You could just add to your state the ID of the flower that is active.
E.g.
this.state = { ActiveFlowerID: null }
So when no ActiveFlower none is open.
On handle click method make the change on the state, and render your flowers additional data with an if matching the flower id and the state.
Hope this helps.

Rendering a component onClick in React

I'm rank new to React and to Javascript in general. Thus the question.
I've a list of images being displayed in a React component. What I'm trying to achieve is to define a method to handle the onClick method on any of these images and render a new component as an overlay. This is my code:
class Viewer extends React.Component{
constructor(props){
super(props);
this.state = {
images : ImageList
};
}
componentDidMount(){
}
handleClick(mediaId, event){
event.preventDefault();
render(){
<MyNewComponent mediaId=mediaId />
}
}
render(){
return (
<div className="row">
<div className="image-viewer">
<ul className="list-inline">
{this.state.images.map(function (image) {
return (<li key={image.mediaId}><a href="#" onClick={this.handleClick.bind(image.mediaId, event)}><img src={image.src}
className="img-responsive img-rounded img-thumbnail"
alt={image.mediaId}/></a></li>);
})}
</ul>
</div>
</div>
);
}
}
export default Viewer;
This clearly is wrong and throws up a bunch of errors. Can someone point me in the right direction?
Here how to handle the click event and show the overlay
class Viewer extends React.Component{
constructor(props){
super(props);
this.state = {
images : ImageList,
mediaId : 0
};
// Bind `this`
// See "Binding to methods of React class" link below
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){ }
handleClick(event){
event.preventDefault();
// Get the value of the `data-id` attribute <a data-id="XX">
var mediaId = event.currentTarget.attributes['data-id'].value;
// Set the state to re render
this.setState({ mediaId }); // ES6 shortcut
// this.setState({ mediaId: mediaId }); // Without shortcut
}
render(){
// Set a variable which contains your newComponent or null
// See "Ternary operator" link below
// See "Why use null expression" link below
var overlay = this.state.mediaId ? <MyNewComponent mediaId={this.state.mediaId} /> : null;
return (
<div className="row">
<div className="image-viewer">
{ overlay }
<ul className="list-inline">
{this.state.images.map(image => {
return (<li key={image.mediaId}><a href="#" onClick={this.handleClick} data-id={image.mediaId}><img src={image.src}
className="img-responsive img-rounded img-thumbnail"
alt={image.mediaId}/></a></li>);
}).bind(this)}
</ul>
</div>
</div>
);
}
}
export default Viewer;
Documentation
React and ES6 - Binding to methods of React class
Ternary operator
Why use null expression

Categories

Resources