Can't use onChange property on input file by using useRef - reactjs

I want customize the input file button so I decided to use useRef, but in this situation I cant read file properties. How can I do this?
const inputFile = useRef(null);
const onButtonClick = () => {
inputFile.current.click();
};
return (
<>
<ToggleButtonGroup size="small" className={classes.marginEnd}>
<input
style={{ display: "none" }}
// accept=".xls,.xlsx"
ref={inputFile}
onChange={(e) => handleInput(e)}
type="file"
// onClick={handleInput}
/>
<Tooltip title="upload">
<ToggleButton size={"small"}>
<CloudUpload onClick={onButtonClick}></CloudUpload>
</ToggleButton>
</Tooltip>
</ToggleButtonGroup>
</>
);

I tried to create a codepen to test the same, it worked well for me. Can you try commenting out the onChange handler if it works for you? Rest of my code is quite similar to yours. The other issue might be that the onButtonClick function is not triggered, if you can put a console log or a debugger to test it out once.
Link to Codepen - Click on File Upload button, the popup comes up.
Also, some notes from React JS Documentation about the same - React docs for File Upload Input

Related

How to display a text message after onClick in react JS?

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>
)

react-dropzone: prevent inner element from showing file picker

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.

How to I keep a Material-ui Select open when I click on only one of the items in it

I have been writing a custom Material-UI Select dropdown which has an optional text field at the top to allow the user to search / filter items in the Select if there were many entries.
I am struggling with how to keep the Select open when I click on the text field (rendered as an InputBase) and just have the normal behavior (of closing the Select when a regular MenuItem is selected.
CodeSandbox here : https://codesandbox.io/s/inspiring-newton-9qsyf
const searchField: TextField = props.searchable ? (
<InputBase
className={styles.searchBar}
onClick={(event: Event) => {
event.stopPropagation();
event.preventDefault();
}}
endAdornment={
<InputAdornment position="end">
<Search />
</InputAdornment>
}
/>
) : null;
return (
<FormControl>
<Select
className={styles.root}
input={<InputBase onClick={(): void => setIconOpen(!iconOpen)} />}
onBlur={(): void => setIconOpen(false)}
IconComponent={iconOpen ? ExpandMore : ExpandLess}
{...props}
>
{searchField}
{dropdownElements.map(
(currEntry: string): HTMLOptionElement => (
<MenuItem key={currEntry} value={currEntry}>
{currEntry}
</MenuItem>
)
)}
</Select>
</FormControl>
);
As you can see above I've tried using stopPropagation and preventDefault but to no avail.
check out this codesandbox link: https://codesandbox.io/s/busy-paper-9pdnu
You can use open prop of Select API
I was able to make a controlled open select by providing open prop as a react state variable and implementing correct event handlers. To make it controlled you must provide onOpen and onClose props of the Select and make sure the open prop stays true when the custom textfield is clicked.
One more important thing I had to do was override the default keyDown behavior of the Select component. If you open up a Select and start typing into it, it shifts focus to the select option that matches what you are typing. For example, if you Select had an option with the text Foobar and if you start typing Food and water, it would cause focus to shift from your custom text input onto the Foobar option. This behavior is overridden in the onKeyDown handler of the custom textfield
Working sandbox here
Edit: even though this worked in the codepen, I had to add onChange={handleOpen} to the Select as well to get it working on a real browser with React and Next.
You can still use stopPropagation to make it work
// open state
const [isSelectorOpen, setisSelectorOpen] = useState(false)
// handle change
const handleChange = event => {
const { value } = event.target
event.stopPropagation()
// set your value
}
// selector
<Select
multiple
open={isSelectorOpen}
onChange={handleChange}
input={(
<Input
onClick={() => setisSelectorOpen(!isSelectorOpen)}
/>
)}
// other attribute
>
<MenuItem>a</MenuItem>
<MenuItem>b</MenuItem>
<MenuItem>c</MenuItem>
</Select>
In my case, none of the above worked, but this did the trick to stop closing the Select:
<MenuItem
onClickCapture={(e) => {
e.stopPropagation();
}}>
You can also change onMouseEnter to change some default styling that comes with using MenuItem (like pointer cursor when mouse enters its layout)
onMouseEnter={(e) => {
e.target.style.backgroundColor = "#ffffff";
e.target.style.cursor = "default";
}}
In my case i also needed to remove the grey clicking effect that MenuItem makes on click, which is a new object generated in MuiTouchRipple-root, so changing display to none did the trick.
sx={{
"& .MuiTouchRipple-root": {
display: "none",
},
}}

How to set or clear value of material-ui Input in ReactJS

I am unable to clear the value of a material-ui Input using refs, not state.
I've tried both types of refs that I know about:
ref={this.input}
- and -
ref={el => (this.input = el)}
but neither seems to work w/ a material-ui Input
the following similar questions did not help:
How to get input value of TextField from Material UI?
Clear and reset form input fields
Clear an input field with Reactjs?
how to set Input value in formField ReactJs
Here's a snippet of my React JSX for the input & button:
<Input
type="text"
id="name"
inputComponent="input"
ref={el => (this.name = el)}
/>
<Button
variant="contained"
onClick={this.handleClear}
className="materialBtn"
>
Clear
</Button>
And the event handler that I expect should clear the input value:
handleClear() {
this.name.value = "";
}
I can make the code work fine using a standard HTML5 input, but not with a material-ui input, which is a requirement of this project. Additionally, this element's value is NOT in react state and I am not looking for a solution that requires using state -- I need to keep this piece as an uncontrolled component.
What am I missing w/ regard to material-ui? I have combed their docs/api but haven't found anything that suggests it needs to be handled differently from a standard input. thanks
Here's an example on CodeSandbox showing the failure w/ a material-ui input and success w/ an HTML5 input:
https://codesandbox.io/s/fancy-frost-joe03
I figured it out, you are using the wrong prop for the ref.
You should be using inputRef prop.
Here is the correct version,
<Input
type="text"
id="name"
inputComponent="input"
inputRef={el => this.name = el}
/>
<Button
variant="contained"
onClick={this.handleClear}
className="materialBtn"
>
Clear
</Button>
handleClear() {
this.name.value = "";
}
The reason is that the Material Input component creates an element with the following structure,
<div class="MuiInputBase-root MuiInput-root MuiInput-underline">
<input class="MuiInputBase-input MuiInput-input" id="name" type="text" value=""></input>
</div>
So, using ref would reference the root element which is <div>. So, they created a separate prop called inputRef to reference the child element <input>.
I updated your codesandbox.io code and saved it. Check out the full working code here,
https://codesandbox.io/s/elastic-dhawan-l4dtf
import React, { useState } from 'react'
import { Button, Container, InputBase } from '#material-ui/core'
const ClearText = ()=> {
const [text , setText] = useState("")
}
const clearTextField = () => setText("")
return (
<Container>
<InputBase
value={text ? text : ""}
onChange={(e)=>setText(e.target.value)}
/>
<Button onClick={clearTextField} > Clear </Button>
</Container>
)
};
export default ClearText;

React disable and enable button based on parent input function and click of button

So basically i have a parent component which uses a child button component. Basically currently when the input validation is not correct it will keep the button disabled. However now I have tried to disable the button on click. The button is currently a pure component and i started to use hooks but not sure how i can still get the validation running.
Code is below
<ChildButton
onClick={() => {
this.checkSomething= this.checkCreds();
}}
enabled={this.validateInput()}
/>
My pure component currently looks like this:
export function AButton({ onClick, enabled, text }) {
const [disabled, setDisabled] = useState(!enabled);
function handleClick() {
setDisabled(!disabled);
//onClick();
}
return (
<Button
style={{ display: "block", margin: "auto" }}
id="next-button"
onClick={handleClick}
disabled={disabled}
>
{text}
</Button>
);
}
So i can get the disable button to work in both scenairos. As the enabled is always being passed down into this pure component so need to keep setting state of it.
I ended up using useEffect from react hooks
useEffect(() => setDisabled(!enabled), [enabled]);
This will check every time the enabled props is updated from the parent. Similar to how componentDidUpdate would work

Resources