I have a component for restaurants list and I have an onClick event for every list-item. Everything is working fine except when I click any child element of the list item. It responds with nothing or undefined (I don't know because nothing is showing up in the console.)
Here is my component:
import React from "react";
class ResturantsList extends React.Component {
constructor(props) {
super(props);
this.state = {
reviews: {}
};
}
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.target.id
);
console.log(reviewsList.reviews);
};
render() {
let list = this.props.resturants.map(resturant => {
return (
<li
key={resturant.id}
id={resturant.id}
className="resturant"
onClick={this.handleReviews}
>
<div className="resturant__hero">
<h1 className="resturant__name">{resturant.name}</h1>
<img src={resturant.icon} alt="" className="resturant__icon" />
</div>
<div className="resturant__row">
<p className="item">Rating</p>
<p className="description">{resturant.rating || "N/A"}</p>
</div>
</li>
);
});
return <div>{list}</div>;
}
}
export default ResturantsList;
So the problem is I can only click on the padding of li to get accurate result otherwise it throws error.
This behavior is new for me and very unexpected.
edit-
Binding in the constructor is not the problem, I had that line but this is not real issue. My event working just fine but only when I click li and not any of its child.
Use e.currentTarget
Try using e.currentTarget instead of e.target in your handleReviews(), like so:
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.currentTarget.id // <--- Here
);
console.log(reviewsList.reviews);
};
I’ve solved it by adding for each li’s a data-id attribute and binding the handleReviews method to this.
<li
key={resturant.id}
data-id={resturant.id}
className="resturant"
onClick={this.handleReviews}
>
And then in handleReviews I’ll get the element with (e.currentTarget.dataset.id):
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.currentTarget.dataset.id
);
};
Related
i am trying to implement an ui requirement. I want to add a active class name to the children div one at a time. 1st it will add the class in first child, and then the class will be removed and to be added in the 2nd child div. And it will infinitly itereate.
Here is my code in next js
$(".softwares_container").each(function () {
(function ($set) {
setInterval(function () {
var $cur = $set
.find(`.${st.active}`)
.removeClass(`${st.active}`);
//store inner html of current item
var $next = $cur.next().length
? $cur.next()
: $set.children().eq(0);
$next.addClass(`${st.active}`);
//store inner element of next item
//set inner html of current item to inner html of next item
var $next_inner = $next.children().eq(0);
setValue({
name: $next_inner.attr('alt'),
description: $next_inner.attr('data-info')
})
// setImage($next_inner.attr('src'))
}, 1000);
})($(this));
});
<div className={`softwares_container ${st.left_container}`}>
<div className={` ${st.img}`} alt="1">
<img src={ae.src} data-info="this is aftereffects" alt="After effects" />
</div>
<div className={st.img} alt="2">
<img src={pr.src} alt="Adobe Premiere pro" />
</div>
<div className={st.img}>
<img src={ps.src} alt="Adobe Photoshop" />
</div>
<div className={st.img}>
<img src={xd.src} alt="Adobe Xd" />
</div>
</div>
But it is not working.it is showing unexpected behaviour. It works fine in react .
Can anyone please give me an alternative solution or tell me how to fix the issue?
Here's the link where you can see the unexpected behaviour.
https://diptnc.ml/about
You can write an effect that sets the classname for elements in an array in a round-robin manner.
// Keep the interval id around so that
// it can be cleared when unsubscribing the effect.
let activeFxId;
/*
Applies active class to an array of HTMLElement in a round-robin manner.
*/
function activeFx(elements) {
activeFxId = setInterval(() => {
const elementsArr = [...elements];
let idx = elementsArr.findIndex((element) => {
return element.classList.contains('active');
});
if (idx === -1) {
idx = 0;
}
elementsArr[idx].classList.remove('active');
elementsArr[(idx + 1) % elementsArr.length].classList.add('active');
}, 2000);
return () => {
clearInterval(activeFxId);
};
}
How you provide this array of elements is left to you. An approach is to store a ref to the parent element containing them and pass that on to the function.
For example,
/* Example component */
import React, {useEffect, useRef} from 'react';
export default () => {
const ref = useRef();
useEffect(() => {
if (ref.current && ref.current.children) {
return activeFx(ref.current.children);
}
});
return (
<div ref={ref}>
<div>One</div>
<div>Two</div>
<div>Three</div>
</div>
);
};
I have a div which is scrollable and i want to refer that div from outside my class. For example
const myDiv = document.getElementById('scrollDiv');
class Test extends React.Component{
listenScrollEvent = (e) => {
console.log("myDiv returns undefined",myDiv);
};
render(){
return (
<div id="scrollDiv" onScroll={this.listenScrollEvent.bind(this)}></div>
)
}
}
Here inside listenScrollEvent i want to access myDiv which is referred to div with id scrollDiv. But i'm getting a undefined value in my console log. I can use const myDiv = document.getElementById('scrollDiv'); inside my listenScrollEvent method but then every time i scroll, referring to div happens.
You can access an element by using refs in React like this -
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.ref = null;
}
listenScrollEvent = () => {
console.log(this.myRef.getBoundingClientRect().top);
};
render() {
return (
<button
ref={my => (this.myRef = my)}
id="scrollDiv"
onScroll={this.listenScrollEvent}
>
Click Me!
</button>
);
}
}
More on refs - https://reactjs.org/docs/refs-and-the-dom.html
I have a parent Component which sends a list of data to a List component which in turn sends it to a Label Component to display each data as a label.
I want to be able to focus on the label element when i click on it so that the appropriate style is applied ..
Below is the gist :-
class ContainerComp extends React.Component {
constructor(props) {
super(props);
this.state = {
group: [1, 2, 3]
};
clickHandler = (name, ref) = > {
// I am able to get the DIV as a html element here but calling .focus() on it dosent change the style where as when i explictly add focus using chrome debugger for the element it works.
ref.focus() // not working
}
render() {
return ( <
ListComp group = {
group
}
onClick = {
clickHandler
} >
)
}
}
function ListComp(props) {
const data = props.group.map(... < label onClick = {} > )
return ( <
Label.. >
)
}
function Label(props) {
let ref = createref();
// on focus style for the component is defined in this component
// i am making use of css modules
return ( <
div ref = {
ref
}
onClick = (name, ref) >
)
}
<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>
How can we achieve such a functionality without having to pass a selected prop to the label component ? By default i would select the first element and keep the focus .. or we can make it configurable.
Usually for this I would use Redux and fire off an action which therefore sets the property of the component that needs change, and make a listener that will listen for that specific prop and change style accordingly.
In this situation, id just pass down the event handler to the child component (remember to not call it when you pass it down, so do:
{() => {eventHandler()}}
and then in the child component do:
onClick={this.props.eventHandler(e)}
You will use the event to identify which element triggered it and then apply the class/style/prop to it.
There was some problem with the Ref , I am not quite sure why but i changed it to use the useRef() hook.
Label Component
const elementRef = useRef(null);
return (
<div className={[externalStyle, styles.container].join(' ')} onClick={() => onClickEvent(itemName, elementRef)} ref = {elementRef} tabIndex={1}> // added tabIndex and also changed to useRef
Container Component
clickHandler = (name: string, ref) => {
ref.current.focus(); // volla it worked
}
I tried using the old form of Ref and also useRef() without null previously (el) => (const = el).
It works if some one has some explanation where i went wrong i will be happy to listen as i am able to wrap my head around. may be a nights sleep helped fix it :P
I am working on a React application where I am trying to render text on the screen when a button is clicked. I have defined a function onButtonClick which gets triggered whenever the button is clicked. However, the HTML that I am returning from the function is not rendered on the screen. I am in the learning stages of React so please excuse me if the question seems silly.
class App extends Component {
constructor() {
super();
this.state = {
blockno:0
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1})
return(
<div>
<h3>Some text</h3>
</div>
);
}
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
</div>
);
}
}
The value is being returned, but the framework/browser/etc. has no reason to do anything with that value.
Try thinking about this a different way, a "more React way". You don't want to return the value to be rendered, you want to update state. Something like this:
constructor() {
super();
this.state = {
blockno:0,
showDiv: false // <-- note the new property in state
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1, showDiv: true})
}
Now you're not returning anything, but rather updating the state of the component. Then in your render method you conditionally render the UI based on the current state:
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
{
this.state.showDiv
?
<div>
<h3>Some text</h3>
</div>
: ''
}
</div>
);
}
The click handler doesn't modify the page, it just modifies the state of the component you're writing. The render method is responsible for rendering the UI based on that state. Any time state changes, render will be called again to re-render the output.
(Note: It's not 100% clear if this is exactly the functionality you're looking for in the UI, since it's not really clear what you're trying to build. But the point here is to illustrate how to update state and render output in React. Your logic can be tweaked as needed from there.)
You have to make a render based on your state. Please check the tutorial at the react docs to learn more about how React works. It's really good
Here is a version of your code that works. Hope it helps
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
};
}
OnButtonClick = () => {
//updates the states
this.setState({ blockno: this.state.blockno + 1 });
};
//remember: every time there is an update to the state the render functions re-runs
render() {
//variable holding the blocks in an array
let blocks = []
//if blockno is greater than 0, it checks everytime that there is a state change
if (this.state.blockno > 0) {
//for every block added
for (let index = 0; index < this.state.blockno; index++) {
//We`re going to add to the array of blocks a new div with the block number
blocks.push(
<div>
<h3>My block number is {index}</h3>
</div>
);
}
}
return (
<div>
<div>
{/**button that updates the state on every click */}
<button onClick={this.OnButtonClick}>
Click me to add a new div!
</button>
</div>
{/**This render the blocks variable that holds the divs */}
{blocks}
</div>
);
}
}
What I see is that you are trying to build a counter. The value that you're returning from the click handler function can't be rendered, instead you need to manage it in the render function as follow:
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
}
}
OnButtonClick = () => {
this.setState(prevState => ({ blockno: prevState.blockno + 1 }));
}
render() {
return(
<div>
{this.state.blockno > 0 && <div>some text {this.state.blockno}</div>}
<Button onButtonClick={this.OnButtonClick} />
</div>
);
}
}
Also note that the setState method is asynchronous, please read the documentation https://reactjs.org/docs/react-component.html#setstate
I am new to ReactJS and I was wondering what is the correct way to target next element with same class in react?
<div className="portfolioGallery">
<img className="portfolioImg activeImg" src="img/1.png"/>
<img className="portfolioImg" src="img/2.png"/>
<img className="portfolioImg" src="img/2.png"/>
<div className="portfolioNext" onClick={this.nextImg.bind(this)}>
Next image
</div>
</div>
What would be the correct way that when I click the portfolioNext div I would be able to give the img2 class of activeImg and remove it from the previous element and so on in ReactJS?
Thank You!
constructor() {
super();
this.state = {
default: "portfolioImg activeImg"
};
}
nextImg() {
this.setState({
default: "portfolioImg"
});
}
That's the kind of imperative technique that you'd generally find in jQuery code, but it doesn't map very well to React's slightly more declarative nature.
Rather than trying to find the next element with a class, use state to maintain a list of those elements alongside an index cursor.
// constructor
this.state = {
images = ['img/1.png', 'img/2.png', 'img/3.png']
cursor: 0
};
Then use these bits of data to render your view.
// render
const { images, cursor } = this.state;
return (
<div>
{images.map((src, index) => {
const activeClass = (index === cursor) ? 'activeImg' : '';
return <img className={`portfolioImg ${activeClass}`} />;
}}
</div>
);
To change the active image, use setState to change the cursor property.
// nextImg
const { cursor, images } = this.state;
const nextCursor = cursor % images.length;
this.setState({ cursor: nextCursor });
I wouldn't suggest you think of it as siblings finding each other, but instead thing of it as the parent storing an index of the current children, and updating that instead.
this.props (or this.state) would have something like this (pseudocode)
this.props.images => ["img/1.png", "img/2.png", "img/2.png"];
Inside render:
<div className="portfolioGallery">
{this.props.images.map((image, i ) => active === i ? (<img className="portfolioImg activeImg" src="image" key={i}/>) : (<img className="portfolioImg " src="image" key={i}/>))}
<button className="portfolioNext" onClick={(e) => this.setState({active: this.state.active + 1}).bind(this)}>Next image</button>
</div>
Of course, accounting for when active >= images.length, but you get the idea