I am using React dropzone for file upload
<DropZone
accept='.pdf,.pptx,.ppt,.docx,.doc,.xls,.xlsx,.xslx,.png,.xsl,.jpg,.jpeg,.gif,.zip'
onDrop={ files => {
this.handleFileDrop(files);
this.dragLeaveHandler();
} }
onDragEnter={ this.dragOverHandler }
onDragLeave={ this.dragLeaveHandler }
multiple={ false }
style={ { height: '100%' } }
>
dragOverHandler = () => {
console.log('enter');
this.setState({
isDragOver: true,
});
};
dragLeaveHandler = () => {
console.log('exit');
this.setState({
isDragOver: false,
});
};
When a file is moving above the drop zone onDragLeave event fires simultaneously.
Should I use some other events?
How can I fix this issue?
You could use pointer-events: none; on the element(s) that are firing the drag leave. That should still allow the dropped event and getting the accepted file though would stop overriding the dropzone events.
The problem you're facing is most likely caused by the DOM events dragEnter and dragLeave getting messed up instead of any flaw in the react-dropzone package. Some elements may cause hovering over them in certain positions not to register as hovering over their parent element. For example, there is a thin sliver at the top edge of any plain string rendered inside a block displayed element. Most commonly this happens inside a <p> tag.
Without seeing the children rendered inside your dropzone, it is impossible to give a specific fix. Generally, you will have to mess with the styling of the children, though. <p> tags for example will not be a problem if their size is set to 0 pixels or if they're replaced with <span> tags. Both options will disrupt the displaying of the children, which is unfortunatley unavoidable.
As for using other events, you're out of luck. The DropZone component relies on the onDragEnter and onDragLeave HTML DOM events. Therefore any fix you might come up with won't fix the component itself.
All in all, it's an unfortunate issue that just has to be dealt with. The simplest way to deal with it is to just have at most one piece of text inside the dropzone and to set its size to 0 pixels with css: height: 0px;. Regular <div> elements won't cause issues, so you can craft an intricate dropzone using them.
Related
The attached graphic shows my issue. If I click outside of the content, but inside the textarea, which you can see a light grey border around, the onBlur event is fired.
I've tried to capture the event and prevent this behaviour using target, but the event looks the same if you click way outside the box - where I want onBlur to fire.
So far using a ref has not worked either. I was hoping that would be the solution. Any ideas on how to allow a user to click anywhere within what they are seeing as the component react-draft-wysiwyg and NOT fire onBlur?
My fix, though feeling a bit hacky because of needing to handle the first onClickAway, was to elevate the onBlur call to a ClickAwayListener wrapping the Editor component like so:
<ClickAwayListener onClickAway={() => {
// Moving onBlur up to support clicking anywhere in component w/o blurring.
// Handle onClickAway firing once on focus of editor textarea.
if (firstClick) {
setFirstClick(false);
} else {
onBlur();
}
}}
>
<Editor
// do not use here: onBlur={onBlur}
// other props
/>
</ClickAwayListener>
I have two images. When I click the image container, a state is changed, and another image is revealed, while the other one disappears - like this:
const Image= ({image1, image2}) => {
const [image, setImageState] = useState(true);
return (
<div className="image-container" onClick={ () => setImageState(!image)}>
<img src={image ? "/images/" + image1 : "/images/" + image2} alt="" />
</div>
)
}
So this works as intended. When I click, the state changes, and so does the image. However, I would like there to be a fade in/out effect when the state changes. How is this achieved ?
I'm not sure if it's possible to animate via CSS the src attribute of an image element (I personally prefer to do it via CSS if possible). However you can animate its opacity property. And you'll need two elements for that - one for the fade-out effect and one for the fade-in. Here's a sandbox. The situation is very similar to this question, there you can see how to put images on top of each other (the one hidden).
Some things to note:
I've pre-set the two images' src attributes and don't set them to
nulls or some falsy values so that we don't see broken image icon while animating (could
occur if you have your state separated among multiple useState
hooks). In my case I batch changes at once to have only a single render and avoid any flashes of broken image icon.
Browser will do the animation upon class change and of course if the
property in this class can be animated. opacity is a CSS property which can be
animated.
For your case you'll probably need to set the src dynamically in your
handler so I'd recommend you make sure you don't cause multiple
renders/paints to avoid visual inconsistencies, so I think you should batch your state updates at once.
In my case the images are next to each other, but if you want to first fade-out the first image and then fade-in the second, you'll need to synchronize animation with transition-delay property.
You could some Animation library like GSAP and accomplish this, as this is difficult to accomplish by css alone.
https://codepen.io/rohinikumar4073/pen/mdVYbrm?editors=1111
function toggleImage() {
let tween = TweenLite.to(".img", 2, {
opacity: 0,
onComplete: function() {
let image = document.querySelector(".img")
if (image.src === "https://via.placeholder.com/350") {
image.src = "https://via.placeholder.com/150";
} else {
image.src = "https://via.placeholder.com/350";
}
let tween2 = TweenLite.to(".img", 2, {
opacity: 2
});
}
});
}
<script src=https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script>
<div onclick="toggleImage()">
<img class="img" id="img1" src="https://via.placeholder.com/150" />
</div>
I'm pretty new to React and I have a table that I hide some columns with CSS media query when the the screen is below certain breakpoint.
#media screen and (max-width: 766px) {
.tdcel {
display: none;
}
It works great, however, in the same table, in a row below I have a cell that's using colspan. Obviously when I hide columns with CSS I need to reduce the colspan as well.
I have an element that's calling a function on click. I was thinking of adding some sort of a check to that function and perhaps count visible cells in the row where the click happens. If I was using jQuery I could do something like this:
$(".clickEm").click(function() {
var ths = $(this),
par = ths.closest("tr"),
col = par.find("td:visible").length;
par.next("tr td").attr("colspan", col);
}
Unfortunately I'm still trying to wrap my mind around React components. Is there a way to accomplish this? Or perhaps I'm overthinking this. Perhaps I can have conditional colspan value based on a viewport size?
<td colSpan={5}>
The simplest solution would be to use css-grid or flexbox instead of tables, since it sounds like you have a layout issue here. Those would be purely css solutions.
However, if you need to set the colspan in response to a viewport change, you can always call react functions from window callbacks.
class MyComponent(props) extends React.Component {
state = {
colspan: 1
};
componentDidMount(){
window.addEventListener('resize', this.resize);
}
componentWillUnmount(){
window.removeEventListener('resize', this.resize);
}
resize(){
// set the state of your component here:
this.setState({
colspan: // whatever
});
}
render(){
return (
<Table>
<tr>
<td colSpan={this.state.colspan}>Blah blah blah</td>
</tr>
</Table>
);
}
}
You may need to change the syntax depending on your variant or flavour of javascript (typescript, classes, whatever.)
React component class functions are just regular functions that can be called from other functions, like window events. Make sure to remove the function when the component is unmounted, or else you could be left with a memory leak.
Additionally, don't forget that window events like resize can be called multiple times per frame, and react can batch up render calls, so make sure your update and render logic is simple.
Alternatively, there are react components on NPM that respond to window size changes for you. Search for npm react resize aware.
I'm trying to make a small sprite-based game with ReactJS. The green dragon (was taken from HMMII) is flying across the hexagonal field and it's behavior depends on mouse clicking. The sprites change each other with speed depending on a specially chosen time constant - 170ms. More precisely: there is a div representing the dragon and it's properties (top, left, width, height and background-image) always are changing.
At the first stage of the development I've faced with irritating blinking and flickering by rerendering the image. How can avoid it?
Below are described multiple ways I've used with some previews made with Surge. The strongest effect is watched in Google Chrome but in Firefox also are troubles.
0) At first I've tried to use CSS-animation based on #keyframes, but it was no good due to fade effect. And I don't need any fade effects at all, I need rapid rerendering.
1) This is the most straightforward attempt. After clicking on a particular field, componentWillReceiveProps is creating the list of steps and then all of this steps are performing consistently. Also I've tried to use requestAnimationFrame instead of setTimeout but with the same troubles.
makeStep() {
const {steps} = this.state;
this.setState((prevState, props) => ({
steps: steps.slice(1),
style: {...}
}));
}
render() {
const {steps, style} = this.state;
steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
this.props.endTurn();
return (<div id="dragon" style={style}></div>);
}
Here is the result: http://streuner.surge.sh/ As you can see, dragon is often disapearing by launching and landing, it fly with skipping some sprites.
2) I've tried to test method describen in article:
https://itnext.io/stable-image-component-with-placeholder-in-react-7c837b1ebee
In this case I've changed my div with background-image to other div containing explicit img. At first, this.state.isLoaded is false and new sprite will not appear. It appears only after the image has been loaded with onLoad method. Also I've tried to use refs with attempt watch for complete-property of the image but it's always true - maybe because size of the image is very small.
setLoaded(){
this.setState((prevState, props) => ({
isLoaded: true
}));
}
render() {
const {isLoaded, steps, style} = this.state;
if(isLoaded) {
steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
this.props.endTurn();
}
return (<div id="wrap" style={{top:style.top, left:style.left}} >
<img id="dragon" alt="" src={style.src} onLoad={this.setLoaded}
style={{width:style.width,
height: style.height,
visibility: isLoaded ? "visible": "hidden"}}/>
</div>);
}
Here is the result: http://streuner2.surge.sh/ There's no more sprite skipping but the flickering effect is much stronger than in first case.
3) Maybe it was my best attempt. I've read this advice: https://github.com/facebook/react-native/issues/981 and decided to render immediately all of the step images but only the one with opacity = 1, the others have opacity = 0.
makeStep(index) {
const {steps} = this.state;
this.setState((prevState, props) => ({
index: index + 1,
steps: steps.map( (s, i) => ({...s, opacity: (i !== index) ? 0: 1}))
}));
}
render() {
const {index, steps} = this.state;
(index < steps.length) ?
setTimeout(() => this.makeStep(index), DRAGON_RENDER_TIME):
this.props.endTurn();
return ([steps.map((s, i) =>
<div className="dragon" key={i} style={s}></div>)]);
}
It's possible to see the result here: http://streuner3.surge.sh/ There's only one flickering by starting new fly with rerendering all sprites. But the code seems to me more artificial.
I would like to emphasize that the behavior always depends on browser, in Firefox it's much better. Also there are differences with variety of flys in the same browser: sometimes there's no flickering effect but in most of cases it unfortunately is. Maybe I don't understand any basic notion of rerendering images in browser.
I think you should shift your attention from animation itself and pay more attention to rerendering in React, each time when you change Image component state or props it is rerendering. Read about lifecycle methods and rerendering in React docs.
You change state very fast(in your case it's almost 6 times per second), so I suppose that some of the browsers are not fast enough with Image component rerendering. Try to move out of Image state variables which updates so fast and everything will be ok
I know the answer is late, but posting my answer here in case someone still wants to find a solution and because I've found this drives some traffic.
A simple workaround is to add a CSS transition property to the image like the below:
transition: all .5s;
it does not prevent the image re-rendering, but at least it does prevent the image flickering.
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.