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.
};
Related
enter image description here
Hi I am trying to change an image onWheel event and so far the code works in a bad way, can anyone suggest ways to improve. I am trying to recreate the zoom out/in effect as seen on the mrpops.ua/en website. When the wheel event triggers the count increases or decrease on direction. Is there maybe a way to use the number count to do some fancy math instead of using it to change animation name.
Edit
If as you say in the comments you have 6 different animations, one way of implementing that would be:
const Contact = () => {
const [count, setCount] = useState(0)
/* Code for updating the counter */
const handleWheel = ({ deltaY }) => {
setCount((currentCount) =>
deltaY > 0 ? currentCount + 1 : currentCount - 1
);
};
useEffect(() => {
console.log(count);
}, [count]);
// I suggest to place all the possible animations in an object, and then retrieve them with object[1], etc. You could also use switch, but this option is cleaner IMO.
const style = {
1: "animation 1"
2: "animation 2"
...
6: "animation 6"
}
return (
<header onWheel={handleWheel}>
<img className="header-img" style={{animation: style[count]}}>
</img>
</header>
)}
Original answer
I'll suggest the following to "translate" the screenshot code into actual React code:
const Contact = ({ counter }) => (
<header>
<img className="header-img" style={{animation: counter === 1 ? "scaleUp 2s ease forwards" : "scaleUp2 2s ease forwards"}}>
</img>
</header>
)
Mind that with React you access style as an object, this is why I'm using {} first to let React know I'll insert JS in HTML, and second because it's a object like: {animation: ""}
Another option, without editing the node style directly, is with conditional rendering. Personally, I prefer this one more:
const Contact = ({ counter }) => (
<header>
// We use classes with BEM to apply the specific animations
{counter === 1 && <img className="header-img header-img--one"></img>}
{counter === 2 && <img className="header-img header-img--two"></img>}
</header>
)
About the wheel effect, since you are using a console.logs I'm not sure how do you intend to implement that. However, I'll share a SO link that might help you with that:
Mouse wheel events in Reactjs
It is preferable to do as suggested in this post (use onWheel or onScroll) rather than inserting event listeners directly.
The same way, it is also preferable to completely avoid query selectors and any other way of imperative coding (if you are unfamiliar with the terms, Google differences between imperative and declarative programming).
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} />
I have looked around for an answer to this - the closest I found being this question - but there is I think a significant difference in my case (the fact that it starts to get into the parent holding the state of its children's... children) which has finally lead to me asking for some clarification.
A very simple example of what I mean is below (and will hopefully better illustrate what I'm asking):
Suppose we have a bunch of book documents like
bookList = [
{
title: "book 1",
author: "bob",
isbn: 1,
chapters: [
{ chapterNum: 1, chapterTitle: "intro", chapterDesc: "very first chapter", startPg: 2, endPg: 23 },
{ chapterNum: 2, chapterTitle: "getting started", chapterDesc: "the basics", startPg: 24, endPg: 45 }
]},
{
title: "book 2" ... }
]
So main point being these embedded objects within documents that could be very long and as such may be collapsed / expanded.
And then here is a rough sample of code showing the components
class App extends Component {
constructor(props) {
super(props);
this.state = {
books: bookList,
focusBook: null
}
this.updateDetailDiv = this.updateDetailDiv.bind(this);
}
updateDetailDiv(book) {
this.setState(
{ focusBook: book}
);
}
render() {
return(
<BookList
bookList = {this.state.books}
updateDetailDiv = { this.updateDetailDiv }
/>
<BookDetail
focusBook = { this.state.focusBook }
/>
);
}
}
const BookList = props => {
return (
props.bookList.map(item=>
<li onClick={()=> props.updateDetailDiv(item)}> {item.title} </li>
)
);
}
const BookDetail = props => {
return (
<div className="bookDetails">
{ props.focusBook != null
? <div>
{props.focusBook.title},
{props.focusBook.author},
{props.focusBook.isbn}
Chapters:
<div className="chapterList">
{ props.focusBook.chapters.map(item=>
<span onClick={()=>someFunction(item)}>{item.chapterNum} - {item.chapterName}</span>
)}
</div>
<div id="chapterDetails">
This text will be replaced with the last clicked chapter's expanded details
</div>
</div>
: <div>
Select A Book
</div>
})
}
someFunction(item) {
document.getElementById('chapterDetails').innerHTML = `<p>${item.chapterDesc}</p><p>${item.startPg}</p><p>${item.endPg}</p>`;
}
So my problem is that i'm not sure what the best approach is for handling simple cosmetic / visual changes to data in functional stateless components without passing it up to the parent component - which is fine and makes sense for the first child - but what happens when many children will have their own children (who may have their own children) --> all requiring their own rendering options?
For example - here the App component will re-render the DetailDiv component (since the state has changed) - but I don't want the App also handling the DetailDiv's detailed div. In my example here its all very simple but the actual application I'm working on has 2 or 3 layers of embedded items that - once rendered by App - could realisticially just be modified visually by normal JS.
SO in my example you'll see I have a someFunction() in each Chapter listing - I can make this work by writing a separate simple 'traditional JS DOM function' (ie: target.getElementById or closest() -- but i don't think i'm supposed to be using normal JS to manipulate the DOM while using React.
So again to summarize - what is the best way to handle simple DOM manipulation to the rendered output of stateless components? Making these into their own class seems like overkill - and having the 'parent' App handle its 'grandchildren' and 'great-grandchildren's state is going to be unwieldy as the Application grows. I must be missing an obvious example out there because I haven't seen much in the way of handling this without layers of stateful components.
EDIT for clarity:
BookDetail is a stateless component.
It is handed an object as a prop by a parent stateful component (App)
When App's state is changed, it will render again, reflecting the changes.
Assume BookDetail is responsible for displaying a lot of data.
I want it so each of the span in BookDetail, when clicked, will display its relevant item in the chapterDetail div.
If another span is clicked, then the chapterDetail div would fill with that item's details. (this is just a simple example - it can be any other pure appearance change to some stateless component - where it seems like overkill for a parent to have to keep track of it)
I don't know how to change the UI/appearance of the stateless component after it is rendered without giving it state OR making the parent keep track of what is essentially a 'substate' (since the only way to update the appearance of a component is to change its state, triggering a render).
Is there a way to do this without making BookDetail a stateful component?
You can add a little bit of simple state to functional components to track the selected index. In this case I would store a "selected chapter index" in state and then render in the div the "chapters[index].details", all without manipulating the DOM which is a React anti-pattern.
The use-case here is that the selected chapter is an internal detail that only BookDetail cares about, so don't lift this "state" to a parent component and since it is also only relevant during the lifetime of BookDetail it is rather unnecessary to store this selected index in an app-wide state management system, like redux.
const BookDetail = ({ focusBook }) => {
// use a state hook to store a selected chapter index
const [selectedChapter, setSelectedChapter] = useState();
useEffect(() => setSelectedChapter(-1), [focusBook]);
if (!focusBook) {
return <div>Select A Book</div>;
}
const { author, chapters, isbn, title } = focusBook;
return (
<div className="bookDetails">
<div>
<div>Title: {title},</div>
<div>Author: {author},</div>
<div>ISBN: {isbn}</div>
Chapters:
<div className="chapterList">
{chapters.map(({chapterName, chapterNum}, index) => (
<button
key={chapterName}
onClick={() => setSelectedChapter(selectedChapter >= 0 ? -1 : index)} // set the selected index
>
{chapterNum} - {chapterName}
</button>
))}
</div>
// if a valid index is selected then render details div with
// chapter details by index
{chapters[selectedChapter] && (
<div id="chapterDetails">
{chapters[selectedChapter].details}
</div>
)}
</div>
</div>
);
};
DEMO
There is some approaches you can do to solve this problem.
First, you don't need to create some class components for your functional components, instead, you can use react hooks, like useState so the component can control it's own content.
Now, if you don't want to use React Hooks, you can use React Redux store to manage all your states: you can only change the state values using the Redux actions.
Happy coding! :D
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 have an alert box to confirm that the user has successfully subscribed:
<div className="alert alert-success">
<strong>Success!</strong> Thank you for subscribing!
</div>
When a user sends an email, I'm changing the "subscribed" state to true.
What I want is to:
Show the alert box when the subscribed state is true
Wait for 2 seconds
Make it fade out
How can I do this?
May 2021 update: as tolga and Alexey Nikonov correctly noted in their answers, it’s possible to give away control over how long the alert is being shown (in the original question, 2 seconds) to the transition-delay property and a smart component state management based on the transitionend DOM event. Also, hooks are these days recommended to handle component’s internal state, not setState. So I updated my answer a bit:
function App(props) {
const [isShowingAlert, setShowingAlert] = React.useState(false);
return (
<div>
<div
className={`alert alert-success ${isShowingAlert ? 'alert-shown' : 'alert-hidden'}`}
onTransitionEnd={() => setShowingAlert(false)}
>
<strong>Success!</strong> Thank you for subscribing!
</div>
<button onClick={() => setShowingAlert(true)}>
Show alert
</button>
(and other children)
</div>
);
}
The delay is then specified in the alert-hidden class in CSS:
.alert-hidden {
opacity: 0;
transition: all 250ms linear 2s; // <- the last value defines transition-delay
}
The actual change of isShowingAlert is, in fact, near-instant: from false to true, then immediately from true to false. But because the transition to opacity: 0 is delayed by 2 seconds, the user sees the message for this duration.
Feel free to play around with Codepen with this example.
Since React renders data into DOM, you need to keep a variable that first has one value, and then another, so that the message is first shown and then hidden. You could remove the DOM element directly with jQuery's fadeOut, but manipulating DOM can cause problems.
So, the idea is, you have a certain property that can have one of two values. The closest implementation is a boolean. Since a message box is always in DOM, it's a child of some element. In React, an element is result of rendering a component, and so when you render a component, it can have as many children as you want. So you could add a message box to it.
Next, this component has to have a certain property that you can easily change and be completely sure that, as soon as you change it, the component gets re-rendered with new data. It's component state!
class App extends React.Component {
constructor() {
super();
this.state = {
showingAlert: false
};
}
handleClickShowAlert() {
this.setState({
showingAlert: true
});
setTimeout(() => {
this.setState({
showingAlert: false
});
}, 2000);
}
render() {
return (
<div>
<div className={`alert alert-success ${this.state.showingAlert ? 'alert-shown' : 'alert-hidden'}`}>
<strong>Success!</strong> Thank you for subscribing!
</div>
<button onClick={this.handleClickShowAlert.bind(this)}>
Show alert
</button>
(and other children)
</div>
);
}
}
Here, you can see that, for message box, either alert-shown or alert-hidden classname is set, depending on the value (truthiness) of showingAlert property of component state. You can then use transition CSS property to make hiding/showing appearance smooth.
So, instead of waiting for the user to click button to show the message box, you need to update component state on a certain event, obviously.
That should be good to start with. Next, try to play around with CSS transitions, display and height CSS properties of the message box, to see how it behaves and if the smooth transition happening in these cases.
Good luck!
PS. See a Codepen for that.
The correct way is to use Transition handler for Fade-in/out
In ReactJS there is synthetic event to wait till fade-out is finished: onTransitionEnd.
NOTE there are different css effects associated with different handlers. Fade is a Transition not an Animation effect.
Here is my example:
const Backdrop = () => {
const {isDropped, hideIt} = useContext(BackdropContext);
const [isShown, setState] = useState(true);
const removeItFromDOM = () => {
debugger
setState(false)
};
return isShown
? <div className={`modal-backdrop ${isDropped ? 'show' : ''} fade` } onClick={hideIt} onTransitionEnd={removeItFromDOM}/>
: null
}
An other way is to solve this with a CSS3 transition.
https://www.tutorialspoint.com/css/css_animation_fade_out.htm
You can add a new class to the alert (like .hidden) and then you can relate .hidden with the class you defined for the alert.
alert.hidden{
// Here you can define a css transition
}
In this solution you don't have to add a setInterval or anything, since css3 transitions already process it on browser render.