Let's say that i have an array of 5 integers
[1,2,3,4,5]
I iterate the array and i create 5 divs. On each div i want to have a onClick handler. So when i click on a div i want to retrieve the number.
What is the best way to store the info, so later i can retrive it from the handler function?
I have tried the following and it's working, but i am not sure if this is the best solution because in my app i will have a large amount of these divs.
const array = [1,2,3,4,5];
// make a closure
function onDivClick(id) {
return () => {
console.log(id);
}
}
return (
<div>
{array.map( id => {
return <div onClick={onDivClick(id)}> ... </div>
})
</div>
)
I have seen also the the html data attribute:
So i can store and i can retrive it after with event.target.getAttributes('data-id').
Is there other ways, and which is the best?
What you have right now is not okay. {onDivClick(id)} executes on every render.
Instead, add the id attribute directly to the div, and access it in the click handler.
Rewrite your click handler like so:
function handleClick(event) {
let id = event.target.id;
console.log(id);
}
and attach it to the div like so:
<div id={id} onClick={handleClick} />
Related
Small question here.
I have 3 div classes that contain images that I want to toggle the state property for each picture every time a user presses on one of the images (div actually).
The states are as follows:
constructor(props) {
super(props);
this.state = {
img_1: 0,
img_2: 0,
img_3: 0
};
}
And I want to have a single handle function that can toggle for all the different images that I have.
Here is the div code (its the same for every image):
<div className="pics" onClick={(e) => this.handlePic(e)}>
<h2>First picture</h2>
<img alt="" src={pic1} className="tier2"/>
</div>
And The handle function is empty at the moment, because I have no idea how to pass into it the name of this.state.img_1. The value of course should toggle between 0 and 1, but I want to be able to use a single function for the toggle of all 3 images.
I am not sure if my question makes a lot of sense, please let me know if you want me to explain a little bit more of my situation.
Thank you!
You have 3 different div, in each of your dives define an onClick={()=>this.myhandle(nameofpic)} (you can hardcode nameofpic here,for example in each div put a name like "img1","img2" ... instead of nameofpic)
Then in your myhandle() put your ifs ,like this :
myhandle(nameofpic){
if(nameofpic==="img_1")
{this.setState({img_1:"valuechange"})
}
if(nameofpic==="img_2")
{this.setState({img_2:"valuechange"})
}
if(nameofpic==="img_3")
{this.setState({img_3:"valuechange"})
}}
I hope you get the idea and works for you
You should create a separate component for Image, which will handle it's own state. This way you can manage any number of Images not just 3.
function Image({url}) {
const [overlay, setOverlay] = useState(false);
const toggleState = () => {
setOverlay(!overlay)
}
return <img src={url} alt={url} onClick={toggleState} />
}
Here overlay can be anything.
Now you can use this component any number of time and it will have it's own state management.
Here is the solution if you are not using hooks then,
In render method(passing static string in parameter for handler method):
<div className="pics" onClick={e => this.handlePic('img1')}>
<h2>First picture</h2>
<img alt="" src={pic1} className="tier2" />
</div>
Your handler(Setting dynamic key):
handlePic = key => {
this.setState(prevState => ({ [key]: !prevState[key] })); // This will toggle 0 or 1 simultaneously.
};
This is a little long winded.
I would like advice on how to go about making the functionality of a remove button. I'm relatively new to react and should have planned this project before I embarked. Either way I am here now and looking for some advice before I remove functionality tomorrow.
In this component I create a method called "displayFood" in which I take an array from props that has in it string values of the names of foods that the user wanted to add to the refrigerator. For example: [yogurt, milk, egg, yogurt, yogurt]. I then create an object that maps this array to key value pairs based on name and quantity, for example: {"yogurt": 3, "milk": 1, "egg": 1}. After this I create an array that holds what I want to render to the user which is each item that they put in the fridge and the quantity of that item. I also have a remove button. I have been thinking about how to remove Items but do not know how to go about doing so.
If, for example the user deletes yogurt I want the value to decrease by one, and if the user deletes a item with quantity 1 then it should go away.
This is a pretty specific question thank you for your time.
import React from 'react';
import "./style.scss";
function InFrige(props) {
const handleRemove = (e, counts) => {
console.log(e.target.name)
}
const displayFood = () => {
var counts = {};
props.foodAddedByUser.forEach(function(x) { counts[x] = (counts[x] || 0) + 1; });
let foodItems = []
for(var index in Object.entries(counts)){
foodItems.push(
<div className="inFrige-food-item" key={index}>
<h3>{Object.keys(counts)[index]} x{Object.values(counts)[index]}</h3>
<button onClick={handleRemove} name={Object.keys(counts)[index]}>Remove</button>
</div>
)
}
return foodItems
}
return (
<div>
<div className="inFrige-food-container">
{displayFood()}
</div>
</div>
)
}
export default InFrige;
Your problem is you are trying to alter your props from within the component. You could either handle this inside the component with state or give a callback via props from the parent component and handle the deletion there, something like this:
<button onClick={()=>{this.props.handleRemove(Object.keys(counts)[index])}} name={Object.keys(counts)[index]}>Remove</button>
in parents render:
<InFridge handleRemove={(item)=>{foodAddedByUser.delete(item)} foodAddedByUser={foodAddedByUser} />
i cound't see right way how to find and modify React DOM nodes before render.
For example i need to make all founded images inside component as lazy load. But the problem is that I don't know which nodes will be inserted by the user. Therefore, I need to look for them after the user places nodes in the component, by using componentWillMount or constructor i think...
Actually in jQuery, to replace real src after user insereted it in. I will do something like this:
jQuery('.wrapper img').each(element => {
let realSrc = element.attr('src')
element
.attr('data-real-src', realSrc)
.attr('src', './fakeimage.png')
})
So in the React, i have to do something like this i think...
const modifiedChildren = children.reduce((arr, next) => {
let modified = {
...next,
src: './fakeimage.png',
'data-real-src': next.src
}
return [
...arr,
modified
]
}, [])
render {
<div className="wrapper">{ modifiedChildren }</div>
}
Can you navigate me some please?
Stop thinking in DOM nodes. Start thinking in components.
You want a new component LazyLoadedImage which gets the real URL and displays it after some trigger. Something like that:
import fakeImage from 'assets/fakeimage.png';
const LazyLoadedImage = ({url, displayActualImage, ...props}) => {
if(displayActualImage) return <img {...props} src={url}>;
else return <img {...props} src={fakeImage}>;
}
Usage example:
<Slider>
{images.map((obj, index) =>
<LazyLoadedImage className='slider__img' url={obj.largeImageURL}
alt={obj.tags || 'cap'} key={index}/>)}
</Slider>
I am trying to build a simple dynamically updated, interactive list that styles each <li></li> according to the css rules of a .clicked class, when you click on them.
The app is composed of two components, a parent and a child and the code in question is the following (taken from the child):
handleClick(e) {
document.getElementById(e.currentTarget.id).setAttribute("class","clicked");
}
render() {
let ar = this.props.sentences;
let pro = ar.map((x,i)=>{ return (<li id={i} key={i} className={i%2==0 ? "white" : "grey"}
onClick={this.handleClick}>{x}</li>); })
return (
<div>
<ul id="ul">{ pro }</ul>
</div>
What is happening here is basically that the parent is passing to the child a sentences prop (an array of sentences that will form the basis for the formation of a dynamic list).
The controversial part is me using DOM manipulation in the form of document.getElementById(e.currentTarget.id).setAttribute("class","two");
in order to change the class of the dynamically created html from jsx.
The code above works, however it does not feel as best practice. The whole advantage in using react is to use virtual dom and optimize the way the DOM is updated.
My questions are the following:
1) Am I right to feel this way? (that my solution is not best practice?)
2) (If so, ) How can I structure my code in order to use the virtual dom machinery react offers?
If you know this question to be a duplicate, please leave a comment and I ll remove it.
1) Am I right to feel this way? (that my solution is not best practice?)
It is correct to assume that this is not an ideal approach, manipulating the DOM via vanilla js in React has its place (Example Use Cases) but should not be done unless absolutely necessary. Also, it is not ideal to use the index from Array.prototype.map as the key on your components as if they change order it can cause confusion for React as the keys would map differently in that case.
2) (If so, ) How can I structure my code in order to use the virtual dom machinery react offers?
You should make use of the component state. If you want each clicked element to maintain the clicked class then make a piece of state that caches the elements that have already recieved the clicked class. if only the most recently clicked element gets the clicked class then simply cache an identifier to the appropriate element in the state. You could also use refs for this purpose though the overusage of them is somewhat discouraged by facebook.
Here is a quick snipped that will toggle the click class on each <li>
class Test extends Component {
constructor() {
super();
this.state = {
clicked: {}
};
}
render() {
let ar = this.props.sentences;
let pro = ar.map((x, i) => {
const color_class = i % 2 === 0 ? "white" : "grey";
const clicked_class = this.state.clicked[i] === true ? "clicked" : "";
let clicked = Object.assign({}, this.state.clicked); // Dont mutate state!!!
return (
<li
id={i}
key={i}
className={`${color_class} ${clicked_class}`}
onClick={e => {
if (clicked.hasOwnProperty(i)) {
delete clicked[i];
} else {
clicked[i] = true;
}
this.setState({ clicked });
}}
>
{x}
</li>
);
});
return (
<div>
<ul id="ul">
{pro}
</ul>
</div>
);
}
}
I am quiet new to React and I wasn't able to figure out how to manipulate DOM.
I have a set of components with checkboxes, and I have a delete button, I want to delete the checked elements when delete button is clicked.
here is a snippet of code I am using :
...
deleteMessage: function(event) {
this.refs.select_message.getDOMNode(); // I get only the last element
},
...
...
render: function() {
var Messages = this.props.messages;
return (
<div>
<button onClick={this.deleteMessage}>Delete</button>
{Messages.map(function(message) {
return (
<div>
<input type='checkbox' className='select_message' ref='select_message'/>
</div>
);
})}
</div>
);
Am I doing it the right way?
What you should do is, in your deleteMessage, call a handler which you have passed from the parent. That, in turn, modifies the messages array from the outside. Then the new messages array will be passed in as props. The main insight you need is props are not only data passed for rendering, but also functions to be called when user interaction happens inside the component.