I'm currently using the react-dropzone plugin and encountered a use case that's not exactly described in the documentations.
Basically, I have the following elements:
An outer dropzone that should allow both
Drag-and-drop, and
Native file picker on click
An inner button that does not show the native file picker on click
The problem I'm having right now is to stop the native file picker from showing up when the inner button is clicked.
To illustrate my example, you can paste this code into the View Code section.
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Dropzone(props) {
const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({
// Disable click and keydown behavior
noKeyboard: true
});
const files = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here</p>
<InnerButton />
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
);
}
function InnerButton(props) {
const { getRootProps } = useDropzone({ noClick: true }); // doesn't stop the parent's input file picker
return (
<button
{...getRootProps({
onClick: (event) => event.stopPropagation(), // this is bad for many reasons
})}
type="button">
This button should not open the file picker
</button>
);
}
<Dropzone />
I figured that using event.stopPropagation() is one way but I've read that it should be avoided for many reasons (source 1, source 2). I tried using noClick: true in the inner button but it doesn't work - most likely because it can't stop the parent's <input> tag.
Is there another approach that I should try, other than using stopPropagation?
I got an answer on GitHub, posting it here in case anyone else encounters the same question.
There's no way around it. You have to use stopPropagation() to stop the event from bubbling up the DOM. Your other option is to use noClick on the parent as well.
noClick only disables opening the file selector when clicking on the dropzone node. It does not prevent the event from bubbling.
The only thing we can do is to provide a noClickBubbling option that calls stopPropagation(), but the user can already do that.
Related
I am using react-select for value input in my web app. I have to override dropdown indicator icon according to design and some dynamic modifications has to be performed depending on the opened/closed status of the menu. I am using styled-components and I have simplified the code a bit to be presented here:
const DropdownIndicator = (props) => {
return (
<components.DropdownIndicator {...props}>
</components.DropdownIndicator>
);
};
<S.InputField
disabled={disabled}
labelLeft={labelLeft}
noLabel={!label}
className={className}
fieldType={state?.type}
inputType={state?.inputType}
notClearable={notClearable}
extendOnOpen={extendOnOpen && menuIsOpen}
menuIsOpen={menuIsOpen}
menuHeight={menuHeight}
>
<label htmlFor={id}>{label}</label>
<div className="input-wrapper">
<Select
id={id}
placeholder={'test'}
components={{ DropdownIndicator }}
// menuIsOpen={menuIsOpen}
onMenuOpen={() => {
console.log('menu opened');
setMenuIsOpen(() => true);
}}
onMenuClose={() => {
console.log('menu closed');
setTimeout(() => setMenuIsOpen(() => false), 3000);
}}
/>
</div>
</S.InputField>
Now, when I try to open the menu clicking on the Select's control field, the menu opens as expected, but when I want to open it clicking on the dropdown icon, the operation becomes hardly predictable - basically I get the 'menu opened' and 'menu closed' directly after, so menu doesn't stay open. Why could that be? Is it a bug in the react-select design?
The funny thing is, if I comment either components={{ DropdownIndicator }} or one of onMenuOpen or onMenuClosed, the menu opens/closes as expected, but never when both ("overridden" DropdownIndicator and onMenuOpen/onMenuClosed) are employed. And I put "overridden" in quotes as it is not technically overridden (I've removed the icon change from <components.DropdownIndicator {...props}></components.DropdownIndicator> as it doesn't impact the outcome), it's actually mimicking (as I understand) the default behaviour of react-select's menu flow.
Important! It only happens in mobile resolutions. Desktop res works just fine. So my understanding this has got something to do with onFocus/onBlur and the way they are treated in touch devices.
Any thoughts?
I am pretty new to ReactJS. I have created a Form that accepts a two inputs and has a button that invokes the onclick() function. I want to display a simple message like "Action complete" below the Form when the onclick() function is complete. I don't want to use alert, but display the message on the webpage itself.
What is the best way to do this?
You can either create an element with JS and append it to the form body or you can have an invisible div/p/whatever element that gets his text modified inside the onclick function and its css class/css style too so that it appears. If you need specific code solutions you can paste your code here :)
You may want to look into state hooks with react, they are found here
https://reactjs.org/docs/hooks-state.html
It sounds like you may want something similar to the following;
const Search = () => {
const [showResults, setShowResults] = React.useState(false)
const onClick = () => setShowResults(true)
return (
<div>
<input type="submit" value="Search" onClick={onClick} />
{ showResults ? <Results /> : null }
</div>
)
}
const Results = () => (
<div id="results">
Some Results
</div>
)
So I have this modal that is wrapped with an entire background div. My issue is that I added a close function on the background, so no matter where I click, it will close the modal.
I would like to have the useRef only target the background div and not work if I click any of the children inside of it
Here is the code
const modalRef = useRef();
const closeModal = e => {
if (modalRef.current.contains(e.target)) {
setShowModal(false);
}
}
return (
<>
{showModal ? (
<Background onClick={closeModal} ref={modalRef}>
<animated.div style={animation}>
<ModalWrapper showModal={showModal}>
<div>hi</div>
<CloseModalButton
aria-label='Close modal'
// onClick={() => setShowModal(!showModal)}
/>
</ModalWrapper>
</animated.div>
</Background>
) : null}
</>
);
};
So right now the ref is attached to the background, but if I console.log(modalRef.current) it will show me the entire jsx with all my children divs inside, but I only want to target the outside div aka the background
So whenever I click outside of my modal it will close
Note: when I console.log(modalRef.current) this is what shows up in the console aka my entire JSX
I'm also using styled-components, so I only want to target the top div which I called Background but shows sc-bdnylx iEsAwc so I have no idea how to target it since it doesn't have any ids or classNames
I tried to add this console.log(modalRef.current.children[0]) but when I implemented it into my function, it didn't work properly. This console.log does only show the divs below my Background div, but I haven't found anything about targeting just the Background only
You don't actually need to use refs here. Just add onClick={e => e.stopPropagation()} to one of the divs inside Background, like on ModalWrapper. It'll stop the click from passing through.
How come the following React Button Emitter is not working? It should display the word Apple, with button click.
function App() {
return (
<div>
<button onClick={handleClick('apple')}>
Test Button
</button>
</div>
)
}
function handleClick(props) {
console.log(props)
}
In order for it to get called on click you need to pass a function. Right now your code is invoking the function:
function App() {
return (
<div>
{/* Creates an anonymous function that calls `handleClick` */}
<button onClick={() => { handleClick('apple'); }}>
Test Button
</button>
</div>
)
}
By doing onClick={handleClick('apple')} what you are doint is to put the result of handleClick('apple') at rendering time, not onClick time.
onClick={() => handleClick('apple')} will work because you are creating a function and assign it to onClick, without executing it yet.
This is how React works, because what you are writing is actually just javascript (not html, even if it looks like so).
Your way would instead be perfectly ok if you were using Vue, for example, because in that case you are working in an html template (unless you don't want to use jsx..)
I haven't managed to wrap my head around how to make use of existing modal libraries with React. For reference, I'm using the awesome remodal.
component.js.jsx
openNewModal: function () {
// The OpenModal component is wrapped around the modal's class which keeps it hidden until we show it here.
var modal = $(ReactDOM.findDOMNode(this.refs.modal)).remodal();
modal.open();
}
render: function () {
return(
<div onClick={this.openNewModal}>
<OpenModal ref="modal" />
</div>
);
}
modal.js.jsx
handleSubmit: function(e) {
e.preventDefault();
},
render: function () {
return(
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
The modal opens up just fine. However, the binding on the onSubmit doesn't work. At first, I thought I'm doing something wrong. I added an onClick handler somewhere in the modal.js.jsx but still nothing would fire.
The interesting part is that, if I executed the binding immediately it worked, as in <form onSubmit={this.handleSubmit()}>. Which means that React fine up to the point that I actually open() the modal.
Is there a simple solution/example for modals and React?
The interesting part is that, if I executed the binding immediately it worked, as in . Which means that React fine up to the point that I actually open() the modal.
This happened because you explicitly added the click event once the modal actually placed in DOM. I think the solution could be in this way. Please add your modal mockup in the component.js.jsx like -
render: function () {
return(
<div onClick={this.openNewModal}>
<div class="remodal" data-remodal-id="modal">
<button data-remodal-action="close" class="remodal-close"></button>
<h1>Remodal</h1>
<p>
Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking.
</p>
<br>
<button data-remodal-action="cancel" class="remodal-cancel">Cancel</button>
<button data-remodal-action="confirm" class="remodal-confirm" onSubmit={this.handleSubmit}>OK</button>
</div>
</div>
);
}
Then hide the modal mockup with CSS(if it is not hided bydefault by remodaljs). Then add your event(handleSubmit) to the ok button or to the form if modal is opened in form.
In openNewModal function instead of remodal the modal just open the modal.
Modal mockup is copied from remodal example. Do replace with your requirement.To be true i didn't have knowledge on remodal but i do faced the same problem while using the bootstrap modal.