Controlled hooks input is not clearing after parent button press - javascript

I have a search bar in a header component like the picture below. Once you click the search icon the search bar will appear and you can start searching. The idea here, is that once you click the icon again the text inside the search bar should be reset to an empty state. Once the icon in the header component is pressed the cleanSearch useEffect will be triggered. inside both console.logs we find that the state of input is set to ''. However the problem I currently have is that once I show the search bar again the old value is still residing in it. It seems that only the clearing state is not working here.
I also tried to give <StyledSearch> a value={input.value} and tried different ways to set and clean the state. What is going wrong here?
edit: I recreated the problem in this sandbox
https://codesandbox.io/s/adoring-mahavira-o1hyu
controlled input in SearchBar component
/** Search bar in header */
const SearchBar = props => {
const { isActive, cleanSearch } = props;
const [input, setInput] = useState({});
useEffect(() => {
if (cleanSearch) {
setInput({});
props.cleanSearchCallback(false);
}
console.log('cleanSearch', input);
}, [cleanSearch]);
console.log('cleanSearch outer', input);
/** controlled search input */
const handleInputChange = e => {
setInput({
...input,
[e.currentTarget.name]: e.currentTarget.value,
});
props.appbarSearch(...input, e.currentTarget.value);
};
return (
<StyledSearch
type="search"
placeholder="Type between 3 ~ 50 characters to find anything"
isActive={isActive ? 'visible' : 'hidden'}
name="searchbar"
onChange={handleInputChange}
/>
);
};

Well the answer was to add a ref inside the Search bar as seen in the code below (this is the workable example in codesandbox).
import React, { useRef } from "react";
const Search = () => {
const ref = useRef();
const clearValue = () => {
if (ref.current) ref.current.value = "";
};
return (
<div>
<input ref={ref} name="search" />
<button style={{ width: "50px", height: "50px" }} onClick={clearValue} />
</div>
);
};
export default Search;

Related

Implement Edit feature in a Note Application using react

I am trying to create a notes application wherein each note object contains a title and content. The user can add, deleted and update a note.
What I was able to achieve so far:
I am able to create a new note , push it into an array and also delete a note from the array . I am finding it a bit hard to edit an existing note.
This is how I want to implement the edit feature:
When the user clicks on the note, the data has to automatically fill into the input box, and the user can modify the data which is then saved into an object and pushed inside an array and then displayed onto the respective note.
When the user clicks on the Edit button, the note id is sent to the App component, the note is searched within the notes array and an object returned to the Create Area component. This object is then displayed on the input field. I'm using UseEffect() hook to display the object data on the input box, but I'm not able to edit the contents on the input box. Here's my code below:
App.jsx:
If the user clicked the edit button, it sets the IsDone state to true in the Edit function. The edit function gets an object from the Notes component
import Header from "./Header";
import CreateArea from "./CreateArea";
import Note from "./Note";
import Footer from "./Footer";
import { useState } from "react";
function App() {
const [noteArray, setArray] = useState([]);
const [isDone,setDone] = useState(false);
const [editNote,setEditNote] = useState({
title:"",
content:""});
function AddOnClick(note) {
setArray((prevNote) => {
return [...prevNote, note];
});
}
function DeleteOnClick(id) {
setArray((prevNote) => {
return prevNote.filter((note, index) => {
return index !== id;
});
});
}
function EditNote(obj)
{
setDone(true);
setEditNote(prevState=>{
return { ...prevState,...obj}});
}
return (
<div>
<Header />
<CreateArea AddOnClick={AddOnClick} noteEdit = {editNote} editFunction = {EditNote}btnClicked = {isDone}/>
{noteArray.map((note, index) => (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={DeleteOnClick}
EditNote = {EditNote}
/>
))}
<Footer />
</div>
);
}
export default App;
Notes.jsx: The id of the note is also included in the object that's passed to App component through the EditNote() function
function Note(props) {
const obj = {title : props.title,
content: props.content,
id:props.id}
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button
onClick={() => {
props.deleteNote(props.id);
}}
>
DELETE
</button>
<button onClick={()=>{props.EditNote(obj)}}>EDIT</button>
</div>
);
}
export default Note;
CreateArea: If the buttonClicked value is true, I'm calling the handleEdit() that takes the object sent from the EditNote() in App.jsx to saves it to to note object using useState() which automatically updates the input and text area field using event.target.value with the help of useEffect().
import { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({
title: "",
content: ""
});
function handleChange(event) {
console.log(event.target);
const { name, value } = event.target;
setNote((prevNote) => {
return { ...prevNote, [name]: value };
});
}
function addNote(event) {
setNote({ title: "", content: "" });
props.AddOnClick(note,note.id);
event.preventDefault();
}
function handleEdit()
{
setNote(prevValue=>{
return {...prevValue,...props.noteEdit}
})
}
useEffect (()=>{
if(props.btnClicked){handleEdit();
}
});
return (
<div>
<form>
<input name="title" id="title" value={note.title}onChange={handleChange}placeholder="Title"/>
<textarea name="content" id="content" value={note.content}onChange={handleChange} placeholder="Take a note..." rows="3"/>
<button onClick={addNote}>Add</button>
</form>
</div>
);
}
export default CreateArea;
The code runs well but now I can't add any more text on the input box,it just blocks me from doing it.I tried calling HandleChange() inside UseEffect(), that throws an error saying: Cannot read properties of target:undefined at HandleChange() I really need help how to implement edit.
I tried directly populating the input box and the text area field using document.getElementById.value = myValue even that does not seem to work

Why in react input field loses focus? [duplicate]

In my component below, the input field loses focus after typing a character. While using Chrome's Inspector, it looks like the whole form is being re-rendered instead of just the value attribute of the input field when typing.
I get no errors from either eslint nor Chrome Inspector.
Submitting the form itself works as does the actual input field when it is located either in the render's return or while being imported as a separate component but not in how I have it coded below.
Why is this so?
Main Page Component
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionPost from '../redux/action/actionPost';
import InputText from './form/InputText';
import InputSubmit from './form/InputSubmit';
class _PostSingle extends Component {
constructor(props, context) {
super(props, context);
this.state = {
post: {
title: '',
},
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(event) {
this.setState({
post: {
title: event.target.value,
},
});
}
onSubmit(event) {
event.preventDefault();
this.props.actions.postCreate(this.state.post);
this.setState({
post: {
title: '',
},
});
}
render() {
const onChange = this.onChange;
const onSubmit = this.onSubmit;
const valueTitle = this.state.post.title;
const FormPostSingle = () => (
<form onSubmit={onSubmit}>
<InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
<InputSubmit name="Save" />
</form>
);
return (
<main id="main" role="main">
<div className="container-fluid">
<FormPostSingle />
</div>
</main>
);
}
}
_PostSingle.propTypes = {
actions: PropTypes.objectOf(PropTypes.func).isRequired,
};
function mapStateToProps(state) {
return {
posts: state.posts,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actionPost, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(_PostSingle);
Text Input Component
import React, { PropTypes } from 'react';
const InputText = ({ name, label, placeholder, onChange, value, error }) => {
const fieldClass = 'form-control input-lg';
let wrapperClass = 'form-group';
if (error && error.length > 0) {
wrapperClass += ' has-error';
}
return (
<div className={wrapperClass}>
<label htmlFor={name} className="sr-only">{label}</label>
<input type="text" id={name} name={name} placeholder={placeholder} onChange={onChange} value={value} className={fieldClass} />
{error &&
<div className="alert alert-danger">{error}</div>
}
</div>
);
};
InputText.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
placeholder: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
value: PropTypes.string,
error: PropTypes.string,
};
InputText.defaultProps = {
value: null,
error: null,
};
export default InputText;
Submit Button Component
import React, { PropTypes } from 'react';
const InputSubmit = ({ name }) => {
const fieldClass = 'btn btn-primary btn-lg';
return (
<input type="submit" value={name} className={fieldClass} />
);
};
InputSubmit.propTypes = {
name: PropTypes.string,
};
InputSubmit.defaultProps = {
name: 'Submit',
};
export default InputSubmit;
it is because you are rendering the form in a function inside render().
Every time your state/prop change, the function returns a new form. it caused you to lose focus.
Try putting what's inside the function into your render directly.
<main id="main" role="main">
<div className="container-fluid">
<FormPostSingle />
</div>
</main>
===>
<main id="main" role="main">
<div className="container-fluid">
<form onSubmit={onSubmit}>
<InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
<InputSubmit name="Save" />
</form>
</div>
</main>
This happened to me although I had keys set!
Here's why:
I was using a key from a text field. Inside the same block; I had an input field to update the value of the same text field. Now, since component keys are changing, react re-renders the UI. Hence loosing focus.
What to take from this:
Don't use keys which are constantly changing.
What's happening is this:
When your onChange event fires, the callback calls setState with the new title value, which gets passed to your text field as a prop. At that point, React renders a new component, which is why you lose focus.
My first suggestion would be to provide your components keys, particularly the form and the input itself. Keys allow React to retain the identity of components through renders.
Edit:
See this documentation on keys: https://reactjs.org/docs/lists-and-keys.html#keys
Example:
<TextField
key="password" // <= this is the solution to prevent re-render
label="eMail"
value={email}
variant="outlined"
onChange={(e) => setEmail(e.target.value)}
/>
Had the same issue and solved it in a quick & easy manner: just calling the component with {compName()} instead of <compName />
For instance, if we had:
const foo = ({param1}) => {
// do your stuff
return (
<input type='text' onChange={onChange} value={value} />
);
};
const main = () => (
<foo param1={true} />
);
Then, we just need to change the way we call the foo() component:
const main = () => (
{foo({param1: true})}
);
By adding
autoFocus="autoFocus"
in the input worked for me
<input
type="text"
autoFocus="autoFocus"
value = {searchString}
onChange = {handleChange}
/>
You have to use a unique key for the input component.
<input key="random1" type="text" name="displayName" />
The key="random1" cannot be randomly generated.
For example,
<div key={uuid()} className='scp-ren-row'>
uuid() will generate a new set of string for each rerender. This will cause the input to lose focus.
If the elements are generated within a .map() function, use the index to be part of the key.
{rens.map((ren,i)=>{
return(
<div key={`ren${i+1}`} className='scp-ren-row'>
{ren}{i}
</div>)
}
This will solve the issue.
I also had this problem, my problem was related to using another component to wrap the textarea.
// example with this problem
import React from 'react'
const InputMulti = (props) => {
const Label = ({ label, children }) => (
<div>
<label>{label}</label>
{ children }
</div>
)
return (
<Label label={props.label}>
<textarea
value={props.value}
onChange={e => props.onChange(e.target.value)}
/>
</Label>
)
}
export default InputMulti
when the state changed, react would render the InputMulti component which would redefine the Label component every time, meaning the output would be structurally the same, but because of JS, the function would be considered !=.
My solution was to move the Label component outside of the InputMulti component so that it would be static.
// fixed example
import React from 'react'
const Label = ({ label, children }) => (
<div>
<label>{label}</label>
{ children }
</div>
)
const InputMulti = (props) => {
return (
<Label label={props.label}>
<textarea
value={props.value}
onChange={e => props.onChange(e.target.value)}
/>
</Label>
)
}
export default InputMulti
I've noticed that people often place locally used components inside the component that wants to use it. Usually to take advantage of function scope and gain access to the parent component props.
const ParentComp = ({ children, scopedValue }) => {
const ScopedComp = () => (<div>{ scopedValue }</div>)
return <ScopedComp />
}
I never really thought of why that would be needed, since you could just prop-drill the props to the internal function and externalise it from the parent comp.
This problem is a perfect example of why you should always externalise your components from each other, even if they are used in one module. Plus you can always use smart folder structures to keep things close by.
src/
components/
ParentComp/
ParentComp.js
components/
ScopedComp.js
I had a similar issue when using styled-components inside a functional component. The custom input element was losing focus every time I typed a character.
After much searching and experimenting with the code, I found that the styled-components inside the functional component was making the input field re-render every time I typed a character as the template literal syntax made the form a function although it looks like an expression inside Devtools. The comment from #HenryMueller was instrumental in making me think in the right direction.
I moved the styled components outside my functional component, and everything now works fine.
import React, { useState } from "react";
import styled from "styled-components";
const StyledDiv = styled.div`
margin: 0 auto;
padding-left: 15px;
padding-right: 15px;
width: 100%;
`;
const StyledForm = styled.form`
margin: 20px 0 10px;
`;
const FormInput = styled.input`
outline: none;
border: 0;
padding: 0 0 15px 0;
width: 100%;
height: 50px;
font-family: inherit;
font-size: 1.5rem;
font-weight: 300;
color: #fff;
background: transparent;
-webkit-font-smoothing: antialiased;
`;
const MyForm = () => {
const [value, setValue] = useState<string>("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if(value.trim() === '') {
return;
}
localStorage.setItem(new Date().getTime().toString(), JSON.stringify(value));
setValue('');
}
return (
<StyledDiv>
<StyledForm onSubmit={handleSubmit}>
<FormInput type="text"
id="inputText"
name="inputText"
placeholder="What Do You Want To Do Next?"
value={value}
onChange={handleChange}/>
</StyledForm>
</StyledDiv>
)
}
export default MyForm;
The best way to use styled-components in cases like this would be to save them in separate files and import them.
My issue was it was rerendering in a stateless component in the same file. So once I got rid of that unecessary stateless component and just put the code in directly, I didn't have unecessary rerenders
render(){
const NewSocialPost = () =>
<div className='new-post'>
<input
onChange={(e) => this.setState({ newSocialPost: e.target.value })}
value={this.state.newSocialPost}/>
<button onClick={() => this._handleNewSocialPost()}>Submit</button>
</div>
return (
<div id='social-post-page'>
<div className='post-column'>
<div className='posts'>
<Stuff />
</div>
<NewSocialPost />
</div>
<MoreStuff />
</div>
I'm new to React, and have been running into this issue.
Here's what I did to solve:
First move all of your components into your components folder and then import them where you want to use them
Make sure all of your form elements get a name and id property
Make sure all components as you walk up the tree get a unique key
Someone smarter than me can probably tell us why we can skip step one and keep everything inline so to speak, but this just helped me organize the code.
I think the real issue is React is rerendering everything (as already stated) and sometimes that rerender is happening on a parent component that doesn't have a key but needs one.
My problem was with ExpansionPanel components wrapping my custom components for form inputs. The panels needed key as well!
Hope this helps someone else out there, this was driving me crazy!
Basically create a ref and assign it to the input element
const inputRef = useRef(null); // Javascript
const inputRef = useRef<HTMLInputElement>(null); // Typescript
// In your Input Element use ref and add autofocus
<input ref={inputRef} autoFocus={inputRef.current === document.activeElement} {...restProps} />
This will keep the input element in focus when typing.
The problem is with dynamic render() caused by useState() function so you can do this for example.
in this code you should use onChange() to get just the new updated data and onMouseLeave() to handle the update but with condition that data is changed to get better performance
example child
export default function Child(){
const [dataC,setDataC]=useState()
return(<Grid>
<TextField
.
.
onChange={(r)=> setDataC(r.target.value) }
onMouseLeave={(e)=> {
if(dataC!=props.data) { // to avoid call handleupdate each time you leave the textfield
props.handlechange(e.target.value) // update partent.data
}
}
/>
</Grid>)
}
exmple parent
export default function Parent(){
const [data,setData]=useState()
return(
<Grid>
<Child handlechange={handlechanges} data={data}/>
</Grid>)
}
I was facing the same issue, as soon as I type any character, it was losing focus. adding autoFocus props helped me to resolve this issue.
TypeScript Code Snippet
Solution -
Add a unique key to the input element because it helps React to identify which item has changed(Reconciliation). Ensure that your key should not change, it has to be constant as well as unique.
If you are defining a styled component inside a react component. If your input element is inside that styled component then define that styled component outside the input's component. Otherwise, on each state change of the main component, it will re-render your styled component and input as well and it will lose focus.
import React, { useState } from "react";
import styled from "styled-components";
const Container = styled.div`
padding: 1rem 0.5rem;
border: 1px solid #000;
`;
function ExampleComponent() {
// Container styled component should not be inside this ExampleComponent
const [userName, setUserName] = useState("");
const handleInputChange = event => {
setUserName(event.target.value);
};
return (
<React.Fragment>
<Container> {/* Styled component */}
<input
key="user_name_key" // Unique and constant key
type="text"
value={userName}
onChange={handleInputChange}
/>
</Container>
</React.Fragment>
);
}
export default ExampleComponent;
In my case, I had this on a child,
//in fact is a constant
const RenderOnDelete=()=>(
<> .
.
<InputText/>
.
.
</>
)
//is a function that return a constant
const RenderOnRadioSelected=()=>{
switch (selectedRadio) {
case RADIO_VAL_EXIST:
return <RenderOnExist/>
case RADIO_VAL_NEW:
return <RenderOnNew/>
case RADIO_VAL_DELETE:
return <RenderOnDelete/>
default:
return <div>Error</div>
}
}
and this in the parent
return(
<>
.
<RenderOnRadioSelected/>
.
</>
)
Y solved it by not calling a component but a function() or a constant, depending on the case.
.
.
.
//in fact is a constant
const RenderOnDelete=(
<> .
.
<InputText/>
.
.
</>
)
//is a function that return a constant
const RenderOnRadioSelected=()=>{
switch (selectedRadio) {
case RADIO_VAL_EXIST:
return {RenderOnExist}
case RADIO_VAL_NEW:
return {RenderOnNew}
case RADIO_VAL_DELETE:
return {RenderOnDelete}//Calling the constant
default:
return <div>Error</div>
}
}
and this in the parent
return(
<>
.
{RenderOnRadioSelected()}//Calling the function but not as a component
.
</>
)
Adding yet another answer: This happened to me when returning a higher order component inside another component. Eg instead of:
/* A function that makes a higher order component */
const makeMyAwesomeHocComponent = <P, >(Component: React.FC<P>) => {
const AwesomeComponent: React.FC<P & AwesomeProp> = (props) => {
const { awesomeThing, ...passThroughProps } = props;
return (
<strong>Look at: {awesomeThing}!</strong>
<Component {...passThroughProps} />
);
}
return AwesomeComponent;
}
/* The form we want to render */
const MyForm: React.FC<{}> = (props) => {
const MyAwesomeComponent: React.FC<TextInputProps & AwesomeProp> =
makeMyAwesomeHocComponent(TextInput);
return <MyAwesomeComponent awesomeThing={"cat"} onChange={() => { /* whatever */ }} />
}
Move the call to create the higher order component out of the thing you're rendering.
const makeMyAwesomeHocComponent = <P, >(Component: React.FC<P>) => {
const AwesomeComponent: React.FC<P & AwesomeProp> = (props) => {
const { awesomeThing, ...passThroughProps } = props;
return (
<strong>Look at: {awesomeThing}!</strong>
<Component {...passThroughProps} />
);
}
return AwesomeComponent;
}
/* We moved this declaration */
const MyAwesomeComponent: React.FC<TextInputProps & AwesomeProp> =
makeMyAwesomeHocComponent(TextInput);
/* The form we want to render */
const MyForm: React.FC<{}> = (props) => {
return <MyAwesomeComponent awesomeThing={"cat"} onChange={() => { /* whatever */ }} />
}
Solution for this problem is to use useCallback It is used to memoize functions which means it caches the return value of a function given a set of input parameters.
const InputForm = useCallback(({ label, lablevalue, placeholder, type, value,setValue }) => {
return (
<input
key={label}
type={type}
value={value}
onChange={(e) => setIpValue(e.target.value)}
placeholder={placeholder}
/>
);
},[]);
Hope it will solve your problem
If you happen to be developing atomic components for your app's design system, you may run into this issue.
Consider the following Input component:
export const Input = forwardRef(function Input(
props: InputProps,
ref: ForwardedRef<HTMLInputElement>,
) {
const InputElement = () => (
<input ref={ref} {...props} />
);
if (props.icon) {
return (
<span className="relative">
<span className="absolute inset-y-0 left-0 flex items-center pl-2">
<label htmlFor={props.id} className="p-1 cursor-pointer">
{icon}
</label>
</span>
<InputElement />
</span>
);
} else {
return <InputElement />;
}
});
It might seem like a simple optimization at first to reuse your input element across both branches of your conditional render. However, anytime the parent of this component re-renders, this component re-renders, and when react sees <InputElement /> in the tree, it's going to render a new <input> element too, and thus, the existing one will lose focus.
Your options are
memoize the component using useMemo
duplicate some code and define the <input> element in both branches of the conditional render. in this case, it's okay since the <input> element is relatively simple. more complex components may need option 1
so your code then becomes:
export const Input = forwardRef(function Input(
props: InputProps,
ref: ForwardedRef<HTMLInputElement>,
) {
if (props.icon) {
return (
<span className="relative">
<span className="absolute inset-y-0 left-0 flex items-center pl-2">
<label htmlFor={props.id} className="p-1 cursor-pointer">
{icon}
</label>
</span>
<input ref={ref} {...props} />
</span>
);
} else {
return <input ref={ref} {...props} />;
}
});
I did the following steps:
Move dynamic component outside a function
Wrap with useMemo function
const getComponent = (step) =>
dynamic(() => import(`#/components/Forms/Register/Step-${step}`), {
ssr: false,
});
And call this function inside the component by wrapping useMemo:
const CurrentStep = useMemo(() => getComponent(currentStep), currentStep]);
I'm very late but I have been tracking down this issue for days now and finally fixed it. I hope it helps someone.
I'm using Material-ui's Dialog component, and I wanted the dialog to show when a menu item was clicked. Something like so:
import React, { useState } from "react";
import {
Menu,
MenuItem,
Dialog,
DialogContent,
TextField,
} from "#mui/material";
const MyMenu = () => {
const [open, setOpen] = useState(false);
return (
<Menu>
<MenuItem>option 1</MenuItem>
<MenuItem onClick={() => setOpen(!open)}>
option 2
<Dialog open={open}>
<DialogContent>
<TextField />
</DialogContent>
</Dialog>
</MenuItem>
</Menu>
);
};
I was having issues with the TextField losing focus, but only when hitting the a, s, d, c and v keys. If I hit any one of those keys, it would not type anything in the textfield and just lose focus. My assumption upon fixing the issue was that some of the menu options contained those characters, and it would try to switch focus to one of those options.
The solution I found was to move the dialog outside of the Menu component:
const MyMenu = () => {
const [open, setOpen] = useState(false);
return (
<>
<Menu>
<MenuItem>option 1</MenuItem>
<MenuItem onClick={() => setOpen(!open)}>
option 2
</MenuItem>
</Menu>
<Dialog open={open}>
<DialogContent>
<TextField />
</DialogContent>
</Dialog>
</>
);
};
I am unable to find anyone with my specific issue online, and this was the post that came up at the top in my searches so I wanted to leave this here. Cheers
I am not authorised to comment then it must be an answer. I had similar issue and Answer from Alex Yan was corect.
Namely I had that function
const DisplaySearchArea =()=>{return (arrayOfSearchFieldNames.map((element, index)=>{return(<div key ={index} className = {inputFieldStyle}><input placeholder= {arrayOfPlaceholders[index]} type="text" className='border-0'
value={this.state[element]}
onChange={e => {this.setState({ [element]: e.target.value }); console.log(e.target)}}
onMouseEnter={e=>e.target.focus()}/></div>)}))}
that behaves OK with FF and not with Chrome when rendered as <DisplaySearchArea />
When render as {...} it's OK with both. That is not so 'beaty' looking code but working, I have already been told to have tendency to overuse lambdas.
Thanks, Alex. This way I solved my issue:
constructor(props, context) {
...
this.FormPostSingle = this.FormPostSingle.bind(this);
}
FormPostSingle() {
const onChange = this.onChange;
const onSubmit = this.onSubmit;
const valueTitle = this.state.post.title;
return (
<form onSubmit={onSubmit}>
<InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
<InputSubmit name="Save" />
</form> );
}
render() {
let FormPostSingle = this.FormPostSingle
return...
}
set the correct id, make sure no other component has same id, set it unique, and it should not change on state update, most common mistake is updating the id with changed value on state update
I had this issue, it was being cause by react-bootstrap/Container, once I got rid of it, included a unique key for every form element, everything worked fine.
For the ones on React Native facing the issue where the text input goes out of focus after typing in single character.
try to pass your onChangeText to your TextInput component.
eg:
const [value, setValue] = useState("")
const onChangeText = (text) => {
setValue(text)
}
return <TextInput value={value} onChangeText={onChangeText} />
This is a great question, and I had the same problem which was 3 parts.
RandomGenerated keys.
Wrong event type.
wrong react JSX attribute.
Keys: when you use random keys each rerender causes react to lose focus (key={Math.random()*36.4621596072}).
EventTypes: onChange cause a rerender with each key stroke, but this can also cause problems. onBlur is better because it updates after you click outside the input. An input, unless you want to "bind" it to something on the screen (visual builders), should use the onBlur event.
Attributes: JSX is not HTML and has it's own attributes (className,...).
Instead of using value, it is better to use defaultValue={foo} in an input.
once I changes these 3 things it worked great. Example below.
Parent:
const [near, setNear] = useState( "" );
const [location, setLocation] = useState( "" );
<ExperienceFormWhere
slug={slug}
questionWhere={question_where}
setLocation={handleChangeSetLocation}
locationState={location}
setNear={setNear}
nearState={near}
key={36.4621596072}/>
Child:
<input
defaultValue={locationState}
className={slug+"_question_where_select search_a_location"}
onBlur={event => setLocation(event.target.value)}/>
I did it with a useRef on input and useEffect
For me this was happening inside Material UI Tabs. I had a search input filter which filtered the table records below it. The search input and table were inside the Tab and whenever a character was typed the input would lose focus (for the obvious reason of re render, the whole stuff inside a tab).
I used the useRef hook for input field ref and then inside my useEffect I triggered the input's focuswhenever the datalist changed. See the code below
const searchInput = useRef();
useEffect(() => {
searchInput.current.focus();
}, [successfulorderReport]);
If working with multiple fields – and they have to be added and removed dynamically for whatever reason – you can use autofocus. You have to keep track of the focus yourself, though. More or less like this:
focusedElement = document.activeElement.id;
[…]
const id = 'dynamicField123'; // dynamically created.
<Input id={id} key={id} {...(focusedElement === id ? { autoFocus: true } : {})} />
This issue got me for a second. Since I was using Material UI, I tried to customize one of the wrapper components of my form using the styled() API from material UI. The issue was caused due to defining the DOM customization function inside my render function body. When I removed it from the function body, it worked like a charm. So my inspection is, whenever I updated the state, it obviously tried to refresh the DOM tree and redeclare the styled() function which is inside the render body, which gave us a whole new reference to the DOM element for that wrapper, resulting in a loss of focus on that element. This is just my speculation, please enlighten me if I am wrong.
So removing the styled() implementation away from the render function body solved the issue for me.
This is silly, but... are you (reader, not OP) setting disabled={true} ever?
This is a silly contribution, but I had a problem very much like the one this page is talking about. I had a <textarea> element inside a component that would lose focus when a debounce function concluded.
Well, I realized I was on the wrong track. I was setting the <textarea> to disabled={true} whenever an auto-save function was firing because I didn't want to let the user edit the input while their work was being saved.
When a <textarea> is set to be disabled it will lose focus no matter what trick you try shared here.
I realized there was zero harm in letting the user continue to edit their input while the save was occurring, so I removed it.
Just in case anyone else is doing this same thing, well, that might be your problem. 😅 Even a senior engineer with 5 years of React experience can do things that dumb.

How do I add the ability to edit text within a react component?

So here's the user function I'm trying to create:
1.) User double clicks on text
2.) Text turns into input field where user can edit text
3.) User hits enter, and upon submission, text is updated to be edited text.
Basically, it's just an edit function where the user can change certain blocks of text.
So here's my problem - I can turn the text into an input field upon a double click, but how do I get the edited text submitted and rendered?
My parent component, App.js, stores the function to update the App state (updateHandler). The updated information needs to be passed from the Tasks.jsx component, which is where the text input is being handled. I should also point out that some props are being sent to Tasks via TaskList. Code as follows:
App.js
import React, {useState} from 'react';
import Header from './Header'
import Card from './Card'
import cardData from './cardData'
import Dates from './Dates'
import Tasks from './Tasks'
import Footer from './Footer'
import TaskList from './TaskList'
const jobItems= [
{
id:8,
chore: 'wash dishes'
},
{
id:9,
chore: 'do laundry'
},
{
id:10,
chore: 'clean bathroom'
}
]
function App() {
const [listOfTasks, setTasks] = useState(jobItems)
const updateHandler = (task) => {
setTasks(listOfTasks.map(item => {
if(item.id === task.id) {
return {
...item,
chore: task.chore
}
} else {
return task
}
}))
}
const cardComponents = cardData.map(card => {
return <Card key = {card.id} name = {card.name}/>
})
return (
<div>
<Header/>
<Dates/>
<div className = 'card-container'>
{cardComponents}
</div>
<TaskList jobItems = {listOfTasks} setTasks = {setTasks} updateHandler = {updateHandler}/>
<div>
<Footer/>
</div>
</div>
)
}
export default App;
Tasks.jsx
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
TaskList.jsx
import React from 'react'
import Tasks from './Tasks'
function TaskList (props) {
const settingTasks = props.setTasks //might need 'this'
return (
<div>
{
props.jobItems.map(item => {
return <Tasks key = {item.id} item = {item} setTasks = {settingTasks} jobItems ={props.jobItems} updateHandler = {props.updateHandler}/>
})
}
</div>
)
}
export default TaskList
You forgot onChange handler on input element to set item's chore value.
Tasks.jsx must be like below
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
const handleInputChange = (e)=>{
// console.log( e.target.value );
// your awesome stuffs goes here
}
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' onChange={handleInputChange} defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
So, first of all, I would encourage you not to switch between input fields and divs but rather to use a contenteditable div. Then you just use the onInput attribute to call a setState function, like this:
function Tasks ({item}) {
return(
<div className = 'tasks-container'>
<div contenteditable="true" onInput={e => editTask(item.id, e.currentTarget.textContent)} >
{item.chore}
</div>
</div>
)
}
Then, in the parent component, you can define editTask to be a function that find an item by its id and replaces it with the new content (in a copy of the original tasks array, not the original array itself.
Additionally, you should avoid renaming the variable between components. (listOfTasks -> jobItems). This adds needless overhead, and you'll inevitably get confused at some point which variable is connected to which. Instead say, <MyComponent jobItems={jobItems} > or if you want to allow for greater abstraction <MyComponent items={jobItems} > and then you can reuse the component for listable items other than jobs.
See sandbox for working example:
https://codesandbox.io/s/practical-lewin-sxoys?file=/src/App.js
Your Task component needs a keyPress handler to set isEditing to false when enter is pressed:
const handleKeyPress = (e) => {
if (e.key === "Enter") {
setIsEditing(false);
}
};
Your updateHandler should also be passed to the input's onChange attribute, and instead of defaultValue, use value. It also needs to be reconfigured to take in the onChange event, and you can map tasks with an index to find them in state:
const updateHandler = (e, index) => {
const value = e.target.value;
setTasks(state => [
...state.slice(0, index),
{ ...state[index], chore: value },
...state.slice(index + 1)
]);
};
Finally, TaskList seems like an unnecessary middleman since all the functionality is between App and Task; you can just render the tasks directly into a div with a className of your choosing.
react-edit-text is a package I created which does exactly what you described.
It provides a lightweight editable text component in React.
A live demo is also available.

Print TextField on button click - printing incorrectly

I'm attempting to incorporate a function in my react app, where a user will input a search term, and when the button is pressed it will go search it. (text input and buttons are using material-ui components) For now I just want it to print the term to the console log to check its okay. When running the below code, the output is: "[object Object]. Does anyone know why that is?
import React from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
class searchText extends React.Component {
state = {
searchTerm: null,
}
handleClick = (searchTerm) => {
this.setState({ searchTerm });
}
render() {
const { searchTerm } = this.state;
return (
<div className ="Search">
<TextField
id = "outlined-search"
label="Enter Query"
type = "search"
className ="Search"
variant="filled"
value = {searchTerm}
/>
<Button
onClick={() =>{console.log("search: " +{searchTerm}); }}
varient ="contained"
classname="goButton" >
Enter Query Here!
<Button/>
</div>
);
}
}
export default Search
update: fixed typos
your have not any function to get the input value and set that value in state.
Try this.
// function for TextField Value Change
getvalue=(event)=>{
this.setState({
searchTerm:event.target.value
})
}
// function for button click
handleClick = () => {
console.log(this.state.searchTerm)
}
<TextField
id = "outlined-search"
label="Enter Query"
type = "search"
className ="Search"
variant="filled"
value = {this.state.searchTerm}
onChange={this.getvalue}
/>
The + searchTem coerces the object into a string, which in your case is [object Object]
Just change it to:
console.log("search: ", searchTerm)
You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/API/console#Outputting_text_to_the_console
Right now your'e not calling handleClick as you just console log it. That means that you're not changing/setting the state.searchTerm to anything. It's the handleClick that will update the state. So the value will always be null. Also you have a typo in your handleClick:
Your parameter says "serchTerm" ... it should say "searchTerm". You missed an a there ;)
Classes use PascalCase as they are components, not simple functions. The Buttons component were badly terminated and maybe you can just simply call your function handler when the user clicks on the button and then show the state on the console
class SearchText extends React.Component {
state = {
searchTerm: null,
}
handleClick = (term) => {
this.setState({ searchTerm: term });
console.log(this.state.searchTerm)
}
render() {
const { searchTerm } = this.state;
return (
<div className ="Search">
<TextField
id = "outlined-search"
label="Enter Query"
type = "search"
className ="Search"
variant="filled"
value = {searchTerm}
/>
<Button
onClick={this.handleClick}
varient ="contained"
classname="goButton" >
Enter Query Here!
</Button>
</div>
);
}
}
Hope helps :)

React.js - input losing focus when rerendering

I am just writing to text input and in onChange event I call setState, so React re-renders my UI. The problem is that the text input always loses focus, so I need to focus it again for each letter :D.
var EditorContainer = React.createClass({
componentDidMount: function () {
$(this.getDOMNode()).slimScroll({height: this.props.height, distance: '4px', size: '8px'});
},
componentDidUpdate: function () {
console.log("zde");
$(this.getDOMNode()).slimScroll({destroy: true}).slimScroll({height: 'auto', distance: '4px', size: '8px'});
},
changeSelectedComponentName: function (e) {
//this.props.editor.selectedComponent.name = $(e.target).val();
this.props.editor.forceUpdate();
},
render: function () {
var style = {
height: this.props.height + 'px'
};
return (
<div className="container" style={style}>
<div className="row">
<div className="col-xs-6">
{this.props.selected ? <h3>{this.props.selected.name}</h3> : ''}
{this.props.selected ? <input type="text" value={this.props.selected.name} onChange={this.changeSelectedComponentName} /> : ''}
</div>
<div className="col-xs-6">
<ComponentTree editor={this.props.editor} components={this.props.components}/>
</div>
</div>
</div>
);
}
});
Without seeing the rest of your code, this is a guess.
When you create a EditorContainer, specify a unique key for the component:
<EditorContainer key="editor1"/>
When a re-rendering occurs, if the same key is seen, this will tell React don't clobber and regenerate the view, instead reuse. Then the focused item should retain focus.
I keep coming back here again and again and always find the solution to my elsewhere at the end.
So, I'll document it here because I know I will forget this again!
The reason input was losing focus in my case was due to the fact that I was re-rendering the input on state change.
Buggy Code:
import React from 'react';
import styled from 'styled-components';
class SuperAwesomeComp extends React.Component {
state = {
email: ''
};
updateEmail = e => {
e.preventDefault();
this.setState({ email: e.target.value });
};
render() {
const Container = styled.div``;
const Input = styled.input``;
return (
<Container>
<Input
type="text"
placeholder="Gimme your email!"
onChange={this.updateEmail}
value={this.state.email}
/>
</Container>
)
}
}
So, the problem is that I always start coding everything at one place to quickly test and later break it all into separate modules.
But, here this strategy backfires because updating the state on input change triggers render function and the focus is lost.
Fix is simple, do the modularization from the beginning, in other words, "Move the Input component out of render function"
Fixed Code
import React from 'react';
import styled from 'styled-components';
const Container = styled.div``;
const Input = styled.input``;
class SuperAwesomeComp extends React.Component {
state = {
email: ''
};
updateEmail = e => {
e.preventDefault();
this.setState({ email: e.target.value });
};
render() {
return (
<Container>
<Input
type="text"
placeholder="Gimme your email!"
onChange={this.updateEmail}
value={this.state.email}
/>
</Container>
)
}
}
Ref. to the solution: https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947
If it's a problem within a react router <Route/> use the render prop instead of component.
<Route path="/user" render={() => <UserPage/>} />
The loss of focus happens because the component prop uses React.createElement each time instead of just re-rendering the changes.
Details here: https://reacttraining.com/react-router/web/api/Route/component
I had the same symptoms with hooks. Yet my problem was defining a component inside the parent.
Wrong:
const Parent =() => {
const Child = () => <p>Child!</p>
return <Child />
}
Right:
const Child = () => <p>Child!</p>
const Parent = () => <Child />
My answer is similar to what #z5h said.
In my case, I used Math.random() to generate a unique key for the component.
I thought the key is only used for triggering a rerender for that particular component rather than re-rendering all the components in that array (I return an array of components in my code). I didn't know it is used for restoring the state after rerendering.
Removing that did the job for me.
Applying the autoFocus attribute to the input element can perform as a workaround in situations where there's only one input that needs to be focused. In that case a key attribute would be unnecessary because it's just one element and furthermore you wouldn't have to worry about breaking the input element into its own component to avoid losing focus on re-render of main component.
What I did was just change the value prop to defaultValue and second change was onChange event to onBlur.
I got the same behavior.
The problem in my code was that i created a nested Array of jsx elements like this:
const example = [
[
<input value={'Test 1'}/>,
<div>Test 2</div>,
<div>Test 3</div>,
]
]
...
render = () => {
return <div>{ example }</div>
}
Every element in this nested Array re-renders each time I updated the parent element. And so the inputs lose there "ref" prop every time
I fixed the Problem with transform the inner array to a react component
(a function with a render function)
const example = [
<myComponentArray />
]
...
render = () => {
return <div>{ example }</div>
}
EDIT:
The same issue appears when i build a nested React.Fragment
const SomeComponent = (props) => (
<React.Fragment>
<label ... />
<input ... />
</React.Fragment>
);
const ParentComponent = (props) => (
<React.Fragment>
<SomeComponent ... />
<div />
</React.Fragment>
);
I solved the same issue deleting the key attribute in the input and his parent elements
// Before
<input
className='invoice_table-input invoice_table-input-sm'
type='number'
key={ Math.random }
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
gotoPage(page)
}}
/>
// After
<input
className='invoice_table-input invoice_table-input-sm'
type='number'
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
gotoPage(page)
}}
/>
The answers supplied didn't help me, here was what I did but I had a unique situation.
To clean up the code I tend to use this format until I'm ready to pull the component into another file.
render(){
const MyInput = () => {
return <input onChange={(e)=>this.setState({text: e.target.value}) />
}
return(
<div>
<MyInput />
</div>
)
But this caused it to lose focus, when I put the code directly in the div it worked.
return(
<div>
<input onChange={(e)=>this.setState({text: e.target.value}) />
</div>
)
I don't know why this is, this is the only issue I've had with writing it this way and I do it in most files I have, but if anyone does a similar thing this is why it loses focus.
If the input field is inside another element (i.e., a container element like <div key={"bart"}...><input key={"lisa"}...> ... </input></div>-- the ellipses here indicating omitted code), there must be a unique and constant key on the container element (as well as on the input field). Elsewise, React renders up a brand new container element when child's state is updated rather than merely re-rendering the old container. Logically, only the child element should be updated, but...
I had this problem while trying to write a component that took a bunch of address information. The working code looks like this
// import react, components
import React, { Component } from 'react'
// import various functions
import uuid from "uuid";
// import styles
import "../styles/signUp.css";
export default class Address extends Component {
constructor(props) {
super(props);
this.state = {
address1: "",
address2: "",
address1Key: uuid.v4(),
address2Key: uuid.v4(),
address1HolderKey: uuid.v4(),
address2HolderKey: uuid.v4(),
// omitting state information for additional address fields for brevity
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
event.preventDefault();
this.setState({ [`${event.target.id}`]: event.target.value })
}
render() {
return (
<fieldset>
<div className="labelAndField" key={this.state.address1HolderKey} >
<label className="labelStyle" for="address1">{"Address"}</label>
<input className="inputStyle"
id="address1"
name="address1"
type="text"
label="address1"
placeholder=""
value={this.state.address1}
onChange={this.handleChange}
key={this.state.address1Key} ></input >
</div>
<div className="labelAndField" key={this.state.address2HolderKey} >
<label className="labelStyle" for="address2">{"Address (Cont.)"}</label>
<input className="inputStyle"
id="address2"
name="address2"
type="text"
label="address2"
placeholder=""
key={this.state.address2Key} ></input >
</div>
{/* omitting rest of address fields for brevity */}
</fieldset>
)
}
}
Sharp-eyed readers will note that <fieldset> is a containing element, yet it doesn't require a key. The same holds for <> and <React.Fragment> or even <div> Why? Maybe only the immediate container needs a key. I dunno. As math textbooks say, the explanation is left to the reader as an exercise.
I had this issue and the problem turned out to be that I was using a functional component and linking up with a parent component's state. If I switched to using a class component, the problem went away. Hopefully there is a way around this when using functional components as it's a lot more convenient for simple item renderers et al.
I just ran into this issue and came here for help. Check your CSS! The input field cannot have user-select: none; or it won't work on an iPad.
The core reason is: When React re-render, your previous DOM ref will be invalid. It mean react has change the DOM tree, and you this.refs.input.focus won't work, because the input here doesn't exist anymore.
For me, this was being caused by the search input box being rendered in the same component (called UserList) as the list of search results. So whenever the search results changed, the whole UserList component rerendered, including the input box.
My solution was to create a whole new component called UserListSearch which is separate from UserList. I did not need to set keys on the input fields in UserListSearch for this to work. The render function of my UsersContainer now looks like this:
class UserContainer extends React.Component {
render() {
return (
<div>
<Route
exact
path={this.props.match.url}
render={() => (
<div>
<UserListSearch
handleSearchChange={this.handleSearchChange}
searchTerm={this.state.searchTerm}
/>
<UserList
isLoading={this.state.isLoading}
users={this.props.users}
user={this.state.user}
handleNewUserClick={this.handleNewUserClick}
/>
</div>
)}
/>
</div>
)
}
}
Hopefully this helps someone too.
I switched value prop to defaultValue. That works for me.
...
// before
<input value={myVar} />
// after
<input defaultValue={myVar} />
My problem was that I named my key dynamically with a value of the item, in my case "name" so the key was key={${item.name}-${index}}. So when I wanted to change the input with item.name as the value, they key would also change and therefore react would not recognize that element
included the next code in tag input:
ref={(input) => {
if (input) {
input.focus();
}
}}
Before:
<input
defaultValue={email}
className="form-control"
type="email"
id="email"
name="email"
placeholder={"mail#mail.com"}
maxLength="15"
onChange={(e) => validEmail(e.target.value)}
/>
After:
<input
ref={(input) => {
if (input) {
input.focus();
}
}}
defaultValue={email}
className="form-control"
type="email"
id="email"
name="email"
placeholder={"mail#mail.com"}
maxLength="15"
onChange={(e) => validEmail(e.target.value)}
/>
I had a similar issue, this is fixed it.
const component = () => {
return <input onChange={({target})=>{
setValue(target.vlaue)
}
} />
}
const ThisComponentKeptRefreshingContainer = () => {
return(
<component />
)
}
const ThisContainerDidNot= () => {
return(
<> {component()} </>
)
}
As the code illustrate calling the component child like an element gave that re-rendering effect, however, calling it like a function did not.
hope it helps someone
I had the same problem with an html table in which I have input text lines in a column. inside a loop I read a json object and I create rows in particular I have a column with inputtext.
http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/
I managed to solve it in the following way
import { InputTextComponent } from './InputTextComponent';
//import my inputTextComponent
...
var trElementList = (function (list, tableComponent) {
var trList = [],
trElement = undefined,
trElementCreator = trElementCreator,
employeeElement = undefined;
// iterating through employee list and
// creating row for each employee
for (var x = 0; x < list.length; x++) {
employeeElement = list[x];
var trNomeImpatto = React.createElement('tr', null, <td rowSpan="4"><strong>{employeeElement['NomeTipologiaImpatto'].toUpperCase()}</strong></td>);
trList.push(trNomeImpatto);
trList.push(trElementCreator(employeeElement, 0, x));
trList.push(trElementCreator(employeeElement, 1, x));
trList.push(trElementCreator(employeeElement, 2, x));
} // end of for
return trList; // returns row list
function trElementCreator(obj, field, index) {
var tdList = [],
tdElement = undefined;
//my input text
var inputTextarea = <InputTextComponent
idImpatto={obj['TipologiaImpattoId']}//index
value={obj[columns[field].nota]}//initial value of the input I read from my json data source
noteType={columns[field].nota}
impattiComposite={tableComponent.state.impattiComposite}
//updateImpactCompositeNote={tableComponent.updateImpactCompositeNote}
/>
tdElement = React.createElement('td', { style: null }, inputTextarea);
tdList.push(tdElement);
var trComponent = createClass({
render: function () {
return React.createElement('tr', null, tdList);
}
});
return React.createElement(trComponent);
} // end of trElementCreator
});
...
//my tableComponent
var tableComponent = createClass({
// initial component states will be here
// initialize values
getInitialState: function () {
return {
impattiComposite: [],
serviceId: window.sessionStorage.getItem('serviceId'),
serviceName: window.sessionStorage.getItem('serviceName'),
form_data: [],
successCreation: null,
};
},
//read a json data soure of the web api url
componentDidMount: function () {
this.serverRequest =
$.ajax({
url: Url,
type: 'GET',
contentType: 'application/json',
data: JSON.stringify({ id: this.state.serviceId }),
cache: false,
success: function (response) {
this.setState({ impattiComposite: response.data });
}.bind(this),
error: function (xhr, resp, text) {
// show error to console
console.error('Error', xhr, resp, text)
alert(xhr, resp, text);
}
});
},
render: function () {
...
React.createElement('table', {style:null}, React.createElement('tbody', null,trElementList(this.state.impattiComposite, this),))
...
}
//my input text
var inputTextarea = <InputTextComponent
idImpatto={obj['TipologiaImpattoId']}//index
value={obj[columns[field].nota]}//initial value of the input I read //from my json data source
noteType={columns[field].nota}
impattiComposite={tableComponent.state.impattiComposite}//impattiComposite = my json data source
/>//end my input text
tdElement = React.createElement('td', { style: null }, inputTextarea);
tdList.push(tdElement);//add a component
//./InputTextComponent.js
import React from 'react';
export class InputTextComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
idImpatto: props.idImpatto,
value: props.value,
noteType: props.noteType,
_impattiComposite: props.impattiComposite,
};
this.updateNote = this.updateNote.bind(this);
}
//Update a inpute text with new value insert of the user
updateNote(event) {
this.setState({ value: event.target.value });//update a state of the local componet inputText
var impattiComposite = this.state._impattiComposite;
var index = this.state.idImpatto - 1;
var impatto = impattiComposite[index];
impatto[this.state.noteType] = event.target.value;
this.setState({ _impattiComposite: impattiComposite });//update of the state of the father component (tableComponet)
}
render() {
return (
<input
className="Form-input"
type='text'
value={this.state.value}
onChange={this.updateNote}>
</input>
);
}
}
Simple solution in my case:
<input ref={ref => ref && ref.focus()}
onFocus={(e)=>e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length)}
/>
ref triggers focus, and that triggers onFocus to calculate the end and set the cursor accordingly.
The issue in my case was that the key prop values I was setting on the InputContainer component and the input fields themselves were generated using Math.random(). The non-constant nature of the values made it hard for track to be kept of the input field being edited.
For me I had a text area inside a portal. This text area was loosing focus. My buggy portal implementation was like this:
export const Modal = ({children, onClose}: modelProps) => {
const modalDOM = document.getElementById("modal");
const divRef = useRef(document.createElement('div'));
useEffect(()=>{
const ref = divRef.current;
modalDOM?.appendChild(ref);
return ()=>{
modalDOM?.removeChild(ref);
}
});
const close = (e: React.MouseEvent) => {
e.stopPropagation();
onClose();
};
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation()
}
return (
createPortal(
<div className="modal" onClick={close}>
<div className="modal__close-modal" onClick={close}>x</div>
{children}
</div>,
divRef.current)
)
}
const Parent = ({content: string}: ParentProps) => {
const [content, setContent] = useState<string>(content);
const onChangeFile = (e: React.MouseEvent) => {
setContent(e.currentTarget.value);
}
return (
<Modal>
<textarea
value={content}
onChange={onChangeFile}>
</textarea>
</Modal>
)
}
Turned out following implementation worked correctly, here I am directly attaching modal component to the DOM element.
export const Modal = ({children, onClose}: modelProps) => {
const modalDOM = document.getElementById("modal");
const close = (e: React.MouseEvent) => {
e.stopPropagation();
onClose();
};
return (
createPortal(
<div className="modal" onClick={close}>
<div className="modal__close-modal" onClick={close}>x</div>
{children}
</div>,
modalDOM || document.body)
)
}
Turns out I was binding this to the component which was causing it to rerender.
I figured I'd post it here in case anyone else had this issue.
I had to change
<Field
label="Post Content"
name="text"
component={this.renderField.bind(this)}
/>
To
<Field
label="Post Content"
name="text"
component={this.renderField}
/>
Simple fix since in my case, I didn't actually need this in renderField, but hopefully me posting this will help someone else.
Changing text in the input of some control can cause parent control rerendering in some cases (according to binding to props).
In this case focus will be lost. Editing should not has effect to parent container in DOM.

Categories

Resources