I have an input field that can be reset/cleared by a button click:
https://codesandbox.io/s/basictextfields-material-demo-forked-m1z5l?file=/demo.js:127-482
const searchInput = useRef(null);
const clearInput = () => {
searchInput.current.value = '';
searchInput.current.blur();
}
return (
<div>
<Input
inputRef={searchInput}
id="outlined-basic"
label="Outlined"
variant="outlined" />
<button onClick={clearInput}>Clear</button>
</div>
);
The issue is that the label does not reset to it's original position after clearing the input. I'm guessing because the input field still thinks it has the focus. I tried adding a blur() but that doesn't seem to do anything.
You can use inputProps for catch onBlur event.
<Input
id="outlined-basic"
label="Outlined"
variant="outlined"
inputProps={{
onBlur: () => {
console.log('foo bar')
}
}}/>
<button onClick={clearInput}>Clear</button>
This is in no way an elegant solution. But it's what I got to make it work as you wanted
import React, {useRef, useState, } from 'react';
import Input from '#mui/material/TextField';
export default function BasicTextFields() {
const searchInput = useRef(null);
const [isFocused, setFocus] = useState(false);
const clearInput = () => {
searchInput.current.value = '';
searchInput.current.blur();
setInputFocus(false)
}
const setInputFocus = (state)=>{
const notEmpty = !!searchInput.current?.value;
if(notEmpty) return setFocus(true)
setFocus(state)
}
return (
<div>
<Input
inputRef={searchInput}
id="outlined-basic"
label="Outlined"
InputLabelProps={{shrink: isFocused}}
onFocus={()=>setInputFocus(true)}
onBlur={()=>setInputFocus(false)}
variant="outlined" />
<button onClick={clearInput}>Clear</button>
</div>
);
}
If what you really want is clearing the TextField programmatically, then just use controlled mode and reset the value state when needed. See uncontrolled-vs-controlled:
export default function BasicTextFields() {
const [value, setValue] = useState("");
const clearInput = () => setValue("");
return (
<>
<Input
value={value}
onChange={(e) => setValue(e.target.value)}
label="Outlined"
variant="outlined"
/>
<button onClick={clearInput}>Clear</button>
</>
);
}
Live Demo
If you are using TextField, then you just have to use onBlur property:
<TextField
onBlur={your_method_handleOnBlur}
...other props
/>
Related
I have a single input element from react-bootstrap that will allow the user to change the field value and 2 buttons will appear, one to accept the changes and the other will cancel the changes leaving the original value in.
I manage to control the focus and blur events by delegating the listeners to the wrapping component, my thinking is that since the focus is still within the wrapping component, I won't lose its focus, but pressing the inner buttons seems to blur the focus, therefore the Accept and Cancel buttons don't fire any events...
Here is my code example:
import { useState, useEffect, useRef } from "react";
import { InputGroup, Button, FormControl } from "react-bootstrap";
import "./styles.css";
const InputField = ({ title }) => {
const formRef = useRef(null);
const [value, setValue] = useState(title);
const [toggleButtons, setToggleButtons] = useState(false);
const onChange = (e) => {
setValue(e.target.value);
};
const onFocus = () => {
setToggleButtons(true);
};
const onBlur = () => {
setToggleButtons(false);
};
const acceptChange = () => {
console.log("Accept");
setToggleButtons(false);
};
const cancelChange = () => {
console.log("Cancel");
setToggleButtons(false);
};
useEffect(() => {
const form = formRef.current;
form.addEventListener("focus", onFocus);
form.addEventListener("blur", onBlur);
return () => {
form.removeEventListener("focus", onFocus);
form.removeEventListener("blur", onBlur);
};
}, []);
return (
<div className="App">
<InputGroup className="m-3" style={{ width: "400px" }}>
<FormControl
ref={formRef}
value={value}
onChange={onChange}
// onFocus={onFocus}
// onBlur={onBlur}
/>
{toggleButtons ? (
<InputGroup.Append>
<Button variant="outline-secondary" onClick={() => acceptChange()}>
Accept
</Button>
<Button variant="outline-secondary" onClick={() => cancelChange()}>
Cancel
</Button>
</InputGroup.Append>
) : null}
</InputGroup>
</div>
);
};
export default function App() {
return (
<>
<InputField title={"Input 1"} />
<InputField title={"Input 2"} />
<InputField title={"Input 3"} />
<InputField title={"Input 4"} />
</>
);
}
A couple of changes are needed to make this work:
The toggle buttons need to always be in the DOM, so hide them rather than only rendering if the focus is there.
To avoid hiding the buttons when the blur occurs from the input to one of the buttons you can check if the newly focused element is a sibling of the input by using the event's relatedTarget and the currentTarget.parentNode.
For example:
import { useState } from "react";
import { InputGroup, Button, FormControl } from "react-bootstrap";
import "./styles.css";
const InputField = ({ title }) => {
const [value, setValue] = useState(title);
const [toggleButtons, setToggleButtons] = useState(false);
const onChange = (e) => {
setValue(e.target.value);
};
const onFocus = () => {
setToggleButtons(true);
};
const onBlur = (e) => {
if (!e.currentTarget.parentNode.contains(e.relatedTarget)) {
setToggleButtons(false);
}
};
const acceptChange = () => {
console.log("Accept");
setToggleButtons(false);
};
const cancelChange = () => {
console.log("Cancel");
setToggleButtons(false);
};
return (
<div className="App">
<InputGroup className="m-3" style={{ width: "400px" }}>
<FormControl
value={value}
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
/>
<InputGroup.Append className={toggleButtons ? "d-flex" : "d-none"}>
<Button
onBlur={onBlur}
variant="outline-secondary"
onClick={() => acceptChange()}
>
Accept
</Button>
<Button
onBlur={onBlur}
variant="outline-secondary"
onClick={() => cancelChange()}
>
Cancel
</Button>
</InputGroup.Append>
</InputGroup>
</div>
);
};
export default function App() {
return (
<>
<InputField title={"Input 1"} />
<InputField title={"Input 2"} />
<InputField title={"Input 3"} />
<InputField title={"Input 4"} />
</>
);
}
https://codesandbox.io/s/input-group-focus-slwoh
I want to know how to set placeholder in input date.
this is my code
<input type="text" onChange={(e) => { setbirth(moment(e.target.value).format('YYYYMMDD')) }} placeholder="생년월일" onFocus="(this.type = 'date')" onBlur="(this.type='text')" />
I've looked through the question, but using this code
throws an error.
onfocus="(this.type='date')" onblur="(this.type='text')"
Warning: Expected `onFocus` listener to be a function, instead got a value of `string` type.
Warning: Expected `onBlur` listener to be a function, instead got a value of `string` type.
Is there any way to change it without error?
useRef is not recommended here as it doesn't helps in re-rendering the component and hence it will not change the type of input. The better approach is to initialize it as type "text" and change the types based on the events on onFocus and onBlur. For your use case, please go through the code below.
export default function App() {
return (
<div>
<input
type="text"
onChange={(e) => console.log(e.target.value)}
onFocus={(e) => (e.target.type = "date")}
onBlur={(e) => (e.target.type = "text")}
/>
</div>
);
}
import React , { useRef } from "react";
const App= () => {
const ref = useRef();
return (
<div>
<input
type="date"
ref={ref}
onChange={(e) => console.log(e.target.value)}
onFocus={() => (ref.current.type = "date")}
onBlur={() => (ref.current.type = "date")}
/>
</div>
);
}
export default App
The error is because onFocus and onBlur are event emitters that expect to fire a callback. In your code, onFocus and onBlur is a statement instead of a callback and thus the error.
You can use react RefObject to do it easily.
Reference:
https://reactjs.org/docs/refs-and-the-dom.html
https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event
https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event
import { useRef } from "react";
export default function App() {
const ref = useRef();
return (
<div>
<input
type="text"
ref={ref}
onChange={(e) => console.log(e.target.value)}
onFocus={() => (ref.current.type = "date")}
onBlur={() => (ref.current.type = "text")}
/>
</div>
);
}
import { useRef } from "react";
export default function App() {
const ref = useRef();
return (
<div>
<input
type="date"
ref={ref}
onChange={(e) => console.log(e.target.value)}
onFocus={() => (ref.current.type = "date")}
onBlur={() => (ref.current.type = "date")}
/>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Working on a tutorial atm that involves react material-ui tables that also has a search input textfield. What I am trying to add to it, is a button that will reset the table report but also clear the search input textfield.
It is the clearing of the search textfield that I am having trouble with.
They are using this code as a separate component library called Controls.input:
import React from 'react'
import { TextField } from '#material-ui/core';
export default function Input(props) {
const { name, label, value,error=null, onChange, ...other } = props;
return (
<TextField
variant="outlined"
label={label}
name={name}
value={value}
onChange={onChange}
{...other}
{...(error && {error:true,helperText:error})}
/>
)
}
The main search code is as follows where I have also added a button
<Controls.Input
id="name"
label="Search Name"
className={classes.searchInput}
InputProps={{
startAdornment: (<InputAdornment position="start">
<Search />
</InputAdornment>)
}}
onChange={handleSearch}
/>
<Button
onClick={handleClear}
className="materialBtn"
>
Clear
</Button>
At this point, I am not sure how to reference/target the search input field as part of the handleClear function, in-order to clear it's contents?
const handleClear = () => {
????
}
Do I need to use useState()?
You are right with having to put the value into state. Based on what you have supplied it seems that your state needs to be in your parent component. So something like this should work
import { useState } from 'react'
const ParentComponent = () => {
const [value, setValue] = useState('')
const handleClear = () => {
setValue('')
}
const handleSearch = (event) => {
setValue(event.target.value)
}
return (
<>
<Controls.Input
id="name"
label="Search Name"
className={classes.searchInput}
value={value}
onChange={handleSearch}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
}}
/>
<Button onClick={handleClear} className="materialBtn">
Clear
</Button>
</>
)
}
Guys I was using the Material-UI text-field and made some custom theme options by by making it as a custom component .
Like Below :-
const CustomTextField = ({props}) =>{
return (
<TexField
label={props.label}
name={props.label}
value={props.row[props.label]}
onChange={(e)=>props.handleChangeFunction(e)}
/>
)
}
In My Component I inject like below :-
const App = () =>{
const [value,setValue] = useState('');
//Some methods here
const handleChange = () =>{
//Logic here.......
}
return(
<>
<CustomTextField {...{label:'name',row:emp,handleChangeFun:handleChange}}/> //Custom Comps...
</>
)
}
So What happens on each input my component renders and the cursor jumps out off the text field so for the next value to input I have to click the text field again.
But when I use the normal text field , directly in the component it works fine , I understand it is because of the onChange Event and using text field in a custom component .
On each value change the parent compoenent re-renders .
So what will be the solution can anyone give me a solution ?
I'm not sure about your way of passing props. This should work:
const CustomTextField = (props) => {
return (
<TextField
label={props.label}
name={props.label}
value={props.value}
onChange={props.onChange}
/>
);
};
export default function App() {
const [value, setValue] = useState("");
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<>
<CustomTextField label="name" value={value} onChange={handleChange} />
</>
);
}
Optionally you can create your component like this, without grouping props:
const CustomTextField = ({label, value, onChange}) => {
return <TextField label={label} name={label} value={value} onChange={onChange}/>
};
I am trying to enable or disable a button based on whether or not there is text in my input but cant seem to achieve it. When I manually set {true OR false} in the disabled property of Button function it works fine but I am really confused on how to set that dynamically based on the content of the input.
Any guidance is super welcome!
This is my app code
import { useState } from "react";
function Input (props){
const { onChange, value } = props
return (<input value={value} onChange={onChange} type="text" placeholder="Add a ToDo" maxLength="50"/>)
}
function Button (props) {
const {onChange, state, text} = props
return (<button disabled={false} onChange={onChange}>{text}</button>)
}
function App() {
const [text, setText] = useState("");
const [state, setSate] = useState(true);
const handleChange = (event) => {
if (!setText(event.target.value)) {
setSate(false);
} else {
setSate(true);
}
};
return (
<div className="App">
<div className="container">
<Input value={text} onChange={handleChange} />
<Button onChange={() => handleChange(state)} text="Add" />
<Button onChange={() => handleChange(state)} text="Clean" />
</div>
);
}
export default App;
Button element should change to:
function Button (props) {
const {disabled, onChange, state, text} = props
return (<button disabled={disabled} onChange={onChange}>{text}</button>)
}
Rendering of it should change to:
...
<Button disabled={!text} onChange={() => handleBtn()} text="Add" />
...
Sandbox: https://codesandbox.io/s/zen-hawking-qqzkw?file=/src/App.js
The idea is to send down disabled prop which would be true if the there is no text in the field.
ALSO, handleChange should look like this:
const handleChange = (event) => {
setText(event.target.value);
};
because the rest of your code in that function does not do anything.
Buttons should have their own handler functions .e.g. const handleBtn = () => {};
So you should pass the state value you are using to store whatever the users write in the input to the button so that the button knows when the input has text on it or not. And then your second state value can be used to store your todo list, so something like this
import { useState } from "react";
function Input({ handleChange, value }) {
return (
<input
value={value}
onChange={handleChange}
type="text"
placeholder="Add a Todo"
maxLength="50"
/>
);
}
function Button({ handleClick, text, disabled }) {
return (
<button disabled={disabled} onClick={handleClick}>
{text}
</button>
);
}
function App() {
const [value, setValue] = useState("");
const [todoList, setTodoList] = useState([]);
const handleChange = (event) => {
setValue(event.target.value);
};
const handleAdd = () => {
setTodoList([...todoList, value]);
handleClear();
};
const handleClear = () => {
setValue("");
};
return (
<div className="App">
<div className="container">
<Input value={value} handleChange={handleChange} />
<Button handleClick={handleAdd} disabled={!value} text="Add" />
<Button handleClick={handleClear} disabled={!value} text="Clear" />
</div>
</div>
);
}
export default App;