How to access a DOM element in React? What is the equilvalent of document.getElementById() in React - javascript

How do I select certain bars in react.js?
This is my code:
var Progressbar = React.createClass({
getInitialState: function () {
return { completed: this.props.completed };
},
addPrecent: function (value) {
this.props.completed += value;
this.setState({ completed: this.props.completed });
},
render: function () {
var completed = this.props.completed;
if (completed < 0) { completed = 0 };
return (...);
}
I want to use this React component:
var App = React.createClass({
getInitialState: function () {
return { baction: 'Progress1' };
},
handleChange: function (e) {
var value = e.target.value;
console.log(value);
this.setState({ baction: value });
},
handleClick10: function (e) {
console.log('You clicked: ', this.state.baction);
document.getElementById(this.state.baction).addPrecent(10);
},
render: function () {
return (
<div class="center">Progress Bars Demo
<Progressbar completed={25} id="Progress1" />
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" />
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" />
<h2 class="center"></h2>
<span>
<select name='selectbar' id='selectbar' value={this.state.baction} onChange={this.handleChange}>
<option value="Progress1">#Progress1</option>
<option value="Progress2">#Progress2</option>
<option value="Progress3">#Progress3</option>
</select>
<input type="button" onClick={this.handleClick10} value="+10" />
<button>+25</button>
<button>-10</button>
<button>-25</button>
</span>
</div>
)
}
});
I want to execute the handleClick10 function and perform the operation for my selected progressbar.
But the result I get is:
You clicked: Progress1
TypeError: document.getElementById(...) is null
How do I select the certain Element in react.js?

You can do that by specifying the ref
EDIT: In react v16.8.0 with function component, you can define a ref with useRef. Note that when you specify a ref on a function component, you need to use React.forwardRef on it to forward the ref to the DOM element of use useImperativeHandle to to expose certain functions from within the function component
Ex:
const Child1 = React.forwardRef((props, ref) => {
return <div ref={ref}>Child1</div>
});
const Child2 = React.forwardRef((props, ref) => {
const handleClick= () =>{};
useImperativeHandle(ref,() => ({
handleClick
}))
return <div>Child2</div>
});
const App = () => {
const child1 = useRef(null);
const child2 = useRef(null);
return (
<>
<Child1 ref={child1} />
<Child1 ref={child1} />
</>
)
}
EDIT:
In React 16.3+, use React.createRef() to create your ref:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
In order to access the element, use:
const node = this.myRef.current;
DOC for using React.createRef()
EDIT
However facebook advises against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases.
From the docs:
Legacy API: String Refs
If you worked with React before, you might be
familiar with an older API where the ref attribute is a string, like
"textInput", and the DOM node is accessed as this.refs.textInput. We
advise against it because string refs have some issues, are considered
legacy, and are likely to be removed in one of the future releases. If
you're currently using this.refs.textInput to access refs, we
recommend the callback pattern instead.
A recommended way for React 16.2 and earlier is to use the callback pattern:
<Progressbar completed={25} id="Progress1" ref={(input) => {this.Progress[0] = input }}/>
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" ref={(input) => {this.Progress[1] = input }}/>
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" ref={(input) => {this.Progress[2] = input }}/>
DOC for using callback
Even older versions of react defined refs using string like below
<Progressbar completed={25} id="Progress1" ref="Progress1"/>
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" ref="Progress2"/>
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" ref="Progress3"/>
In order to get the element just do
var object = this.refs.Progress1;
Remember to use this inside an arrow function block like:
print = () => {
var object = this.refs.Progress1;
}
and so on...

For getting the element in react you need to use ref and inside the function you can use the ReactDOM.findDOMNode method.
But what I like to do more is to call the ref right inside the event
<input type="text" ref={ref => this.myTextInput = ref} />
This is some good link to help you figure out.

With newer versions of React you can use and manipulate the DOM via hooks like this:
import React, { useEffect, useRef } from "react";
const MyComponent = () => {
const myContainer = useRef(null);
useEffect(() => {
console.log("myContainer..", myContainer.current);
});
return (
<>
<h1>Ref with react</h1>
<div ref={myContainer}>I can use the DOM with react ref</div>
</>
);
};
export default MyComponent;
Whenever you want to access your DOM element just use myContainer.current

You can replace
document.getElementById(this.state.baction).addPrecent(10);
with
this.refs[this.state.baction].addPrecent(10);
<Progressbar completed={25} ref="Progress1" id="Progress1"/>

Disclaimer: While the top answer is probably a better solution, as a beginner it's a lot to take in when all you want is something very simple. This is intended as a more direct answer to your original question "How can I select certain elements in React"
I think the confusion in your question is because you have React components which you are being passed the id "Progress1", "Progress2" etc. I believe this is not setting the html attribute 'id', but the React component property. e.g.
class ProgressBar extends React.Component {
constructor(props) {
super(props)
this.state = {
id: this.props.id <--- ID set from <ProgressBar id="Progress1"/>
}
}
}
As mentioned in some of the answers above you absolutely can use document.querySelector inside of your React app, but you have to be clear that it is selecting the html output of your components' render methods. So assuming your render output looks like this:
render () {
const id = this.state.id
return (<div id={"progress-bar-" + id}></div>)
}
Then you can elsewhere do a normal javascript querySelector call like this:
let element = document.querySelector('#progress-bar-Progress1')

You have to follow two different ways to do it in Class and Functional components.
For class components
<input type="text" ref={ref => this.myTextInput = ref} />
Look at the above code. Use "ref" attribute to refer to the relevant element. Then you will be able to refer to that element using that reference. In this example, I can use "this.myTextInput" to refer to the above input element.
For functional components
const textInput = useRef(null)
Use the "useRef" hook and set that variable name as the value of the "ref" attribute of the element you want to refer to (like below).
<input type="text" ref={textInput} />
An example for this on functional components.
import React, {useRef} from 'react'
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input type="text" ref={textInput} />
</div>
);
}
Want to learn more? Here you go

Since React uses JSX code to create an HTML we cannot refer dom using regulation methods like documment.querySelector or getElementById.
Instead we can use React ref system to access and manipulate Dom as shown in below example:
constructor(props){
super(props);
this.imageRef = React.createRef(); // create react ref
}
componentDidMount(){
**console.log(this.imageRef)** // acessing the attributes of img tag when dom loads
}
render = (props) => {
const {urls,description} = this.props.image;
return (
<img
**ref = {this.imageRef} // assign the ref of img tag here**
src = {urls.regular}
alt = {description}
/>
);
}
}

In my case, I wasn't able to use ref because elements were somewhere between many child components and I have to access them by class and id instead of ref. So, trying with useEffect hook didn't work as it can't find the element:
useEffect(() => {
const el1 = document.querySelector('.el1')
const el2 = document.querySelector('.el2')
}, [])
The element is undefined because when it is mounted the children components also doesn't mounted before this parent component.
So, what I did is to use timeout:
useEffect(() => {
const timer = setTimeout(() => {
const el1 = document.querySelector('.el1')
const el2 = document.querySelector('.el2')
},500)
return () => {
clearTimeout(timer)
}
}, [])
Now, it worked fine. It found the DOM and I was able to manipulate with them. Hope, this helps someone!

The equivalent of document.getElementById() in React is document.querySelector().

Related

How can I get the value of an input from a button click event?

I would like to concat the value that's on a text input onto a property in the state, but I don't know how to get access to that input's value from the click event.
This is the code i'm working with right now.
onCreateClick = () => {
const valueFromInput =
this.setState((prevState) => {
stateProperty: prevState.stateProperty.concat(`${valueFromInput}`)
})
}
Here is an example of how you can do it, I have used functional component and hooks instead of class-based component, but I hope you get the logic behind implementation:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [stateProperty, setStateProperty] = useState("");
const [valueFromInput, setValueFromInput] = useState("");
const onCreateClick = () => {
let newString = stateProperty;
setStateProperty(newString.concat(valueFromInput));
setValueFromInput("");
console.log(valueFromInput);
};
return (
<div className="App">
{stateProperty ? stateProperty : "Type Something"}
<input
value={valueFromInput}
onChange={(e) => setValueFromInput(e.target.value)}
/>
<button onClick={onCreateClick}>append</button>
</div>
);
}
Codesandbox Example
You can set a reference on the JSX element whose value you want to obtain. Then you can just reference the DOM Element at your event handler. Once you have a reference to that DOM Element, you can just grab the value of the input with yourElement.value like a normal HTML element.
https://reactjs.org/docs/refs-and-the-dom.html
Copied from the React docs
functional component with Hooks
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
class component implementation
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}```

How to focus a specific component on page load in React using hooks? [duplicate]

What's the react way of setting focus on a particular text field after the component is rendered?
Documentation seems to suggest using refs, e.g:
Set ref="nameInput" on my input field in the render function, and then call:
this.refs.nameInput.getInputDOMNode().focus();
But where should I call this? I've tried a few places but I cannot get it to work.
#Dhiraj's answer is correct, and for convenience you can use the autoFocus prop to have an input automatically focus when mounted:
<input autoFocus name=...
Note that in jsx it's autoFocus (capital F) unlike plain old html which is case-insensitive.
You should do it in componentDidMount and refs callback instead. Something like this
componentDidMount(){
this.nameInput.focus();
}
class App extends React.Component{
componentDidMount(){
this.nameInput.focus();
}
render() {
return(
<div>
<input
defaultValue="Won't focus"
/>
<input
ref={(input) => { this.nameInput = input; }}
defaultValue="will focus"
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
<div id="app"></div>
Focus on mount
If you just want to focus an element when it mounts (initially renders) a simple use of the autoFocus attribute will do.
<input type="text" autoFocus />
Dynamic focus
to control focus dynamically use a general function to hide implementation details from your components.
React 16.8 + Functional component - useFocus hook
const FocusDemo = () => {
const [inputRef, setInputFocus] = useFocus()
return (
<>
<button onClick={setInputFocus} >
Focus
</button>
<input ref={inputRef} />
</>
)
}
const useFocus = () => {
const htmlElRef = useRef(null)
const setFocus = () => {htmlElRef.current && htmlElRef.current.focus()}
return [ htmlElRef, setFocus ]
}
Full Demo
React 16.3 + Class Components - utilizeFocus
class App extends Component {
constructor(props){
super(props)
this.inputFocus = utilizeFocus()
}
render(){
return (
<>
<button onClick={this.inputFocus.setFocus}>
Focus
</button>
<input ref={this.inputFocus.ref}/>
</>
)
}
}
const utilizeFocus = () => {
const ref = React.createRef()
const setFocus = () => {ref.current && ref.current.focus()}
return {setFocus, ref}
}
Full Demo
As of React 0.15, the most concise method is:
<input ref={input => input && input.focus()}/>
If you just want to make autofocus in React, it's simple.
<input autoFocus type="text" />
While if you just want to know where to put that code, answer is in componentDidMount().
v014.3
componentDidMount() {
this.refs.linkInput.focus()
}
In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.
Read the API documents here: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode
React 16.3 added a new convenient way to handle this by creating a ref in component's constructor and use it like below:
class MyForm extends Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focus();
}
render() {
return(
<div>
<input ref={this.textInput} />
</div>
);
}
}
For more details about React.createRef, you can check this article in React blog.
Update:
Starting from React 16.8, useRef hook can be used in function components to achieve the same result:
import React, { useEffect, useRef } from 'react';
const MyForm = () => {
const textInput = useRef(null);
useEffect(() => {
textInput.current.focus();
}, []);
return (
<div>
<input ref={textInput} />
</div>
);
};
The React docs now have a section for this. https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute
render: function() {
return (
<TextInput
ref={function(input) {
if (input != null) {
input.focus();
}
}} />
);
},
I just ran into this issue and I'm using react 15.0.1 15.0.2 and I'm using ES6 syntax and didn't quite get what I needed from the other answers since v.15 dropped weeks ago and some of the this.refs properties were deprecated and removed.
In general, what I needed was:
Focus the first input (field) element when the component mounts
Focus the first input (field) element with an error (after submit)
I'm using:
React Container/Presentation Component
Redux
React-Router
Focus the First Input Element
I used autoFocus={true} on the first <input /> on the page so that when the component mounts, it will get focus.
Focus the First Input Element with an Error
This took longer and was more convoluted. I'm keeping out code that isn't relevant to the solution for brevity.
Redux Store / State
I need a global state to know if I should set the focus and to disable it when it was set, so I don't keep re-setting focus when the components re-render (I'll be using componentDidUpdate() to check for setting focus.)
This could be designed as you see fit for you application.
{
form: {
resetFocus: false,
}
}
Container Component
The component will need to have the resetfocus property set and a callBack to clear the property if it ends up setting focus on itself.
Also note, I organized my Action Creators into separate files mostly due to my project is fairly large and I wanted to break them up into more manageable chunks.
import { connect } from 'react-redux';
import MyField from '../presentation/MyField';
import ActionCreator from '../actions/action-creators';
function mapStateToProps(state) {
return {
resetFocus: state.form.resetFocus
}
}
function mapDispatchToProps(dispatch) {
return {
clearResetFocus() {
dispatch(ActionCreator.clearResetFocus());
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyField);
Presentation Component
import React, { PropTypes } form 'react';
export default class MyField extends React.Component {
// don't forget to .bind(this)
constructor(props) {
super(props);
this._handleRef = this._handleRef.bind(this);
}
// This is not called on the initial render so
// this._input will be set before this get called
componentDidUpdate() {
if(!this.props.resetFocus) {
return false;
}
if(this.shouldfocus()) {
this._input.focus();
this.props.clearResetFocus();
}
}
// When the component mounts, it will save a
// reference to itself as _input, which we'll
// be able to call in subsequent componentDidUpdate()
// calls if we need to set focus.
_handleRef(c) {
this._input = c;
}
// Whatever logic you need to determine if this
// component should get focus
shouldFocus() {
// ...
}
// pass the _handleRef callback so we can access
// a reference of this element in other component methods
render() {
return (
<input ref={this._handleRef} type="text" />
);
}
}
Myfield.propTypes = {
clearResetFocus: PropTypes.func,
resetFocus: PropTypes.bool
}
Overview
The general idea is that each form field that could have an error and be focused needs to check itself and if it needs to set focus on itself.
There's business logic that needs to happen to determine if the given field is the right field to set focus to. This isn't shown because it will depend on the individual application.
When a form is submitted, that event needs to set the global focus flag resetFocus to true. Then as each component updates itself, it will see that it should check to see if it gets the focus and if it does, dispatch the event to reset focus so other elements don't have to keep checking.
edit
As a side note, I had my business logic in a "utilities" file and I just exported the method and called it within each shouldfocus() method.
Cheers!
This is not longer the best answer. As of v0.13, this.refs may not available until AFTER componentDidMount() runs, in some odd cases.
Just add the autoFocus tag to your input field, as FakeRainBrigand showed above.
Ref. #Dave's comment on #Dhiraj's answer; an alternative is to use the callback functionality of the ref attribute on the element being rendered (after a component first renders):
<input ref={ function(component){ React.findDOMNode(component).focus();} } />
More info
Using React Hooks / Functional components with Typescript, you can use the useRef hook with HTMLInputElement as the generic parameter of useRef:
import React, { useEffect, useRef } from 'react';
export default function MyComponent(): JSX.Element {
const inputReference = useRef<HTMLInputElement>(null);
useEffect(() => {
inputReference.current?.focus();
}, []);
return (
<div>
<input ref={inputReference} />
</div>
);
}
Or if using reactstrap, supply inputReference to innerRef instead of ref:
import React, { useEffect, useRef } from 'react';
import { Input } from 'reactstrap';
export default function MyComponent(): JSX.Element {
const inputReference = useRef<HTMLInputElement>(null);
useEffect(() => {
inputReference.current?.focus();
}, []);
return (
<div>
<Input innerRef={inputReference} />
</div>
);
}
Note that none of these answers worked for me with a material-ui TextField component. Per How to set focus to a materialUI TextField? I had to jump through some hoops to get this to work:
const focusUsernameInputField = input => {
if (input) {
setTimeout(() => {input.focus()}, 100);
}
};
return (
<TextField
hintText="Username"
floatingLabelText="Username"
ref={focusUsernameInputField}
/>
);
This is the proper way, how to autofocus. When you use callback instead of string as ref value, it is automatically called. You got your ref available than without the need of touching the DOM using getDOMNode
render: function() {
return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
this._input.focus();
},
You don't need getInputDOMNode?? in this case...
Just simply get the ref and focus() it when component gets mounted -- componentDidMount...
import React from 'react';
import { render } from 'react-dom';
class myApp extends React.Component {
componentDidMount() {
this.nameInput.focus();
}
render() {
return(
<div>
<input ref={input => { this.nameInput = input; }} />
</div>
);
}
}
ReactDOM.render(<myApp />, document.getElementById('root'));
You can put that method call inside the render function. Or inside the life cycle method, componentDidUpdate
I have same problem but I have some animation too, so my colleague suggest to use window.requestAnimationFrame
this is ref attribute of my element:
ref={(input) => {input && window.requestAnimationFrame(()=>{input.focus()})}}
AutoFocus worked best for me. I needed to change some text to an input with that text on double click so this is what I ended up with:
<input autoFocus onFocus={this.setCaretToEnd} value={this.state.editTodo.value} onDoubleClick={this.updateTodoItem} />
NOTE: To fix the issue where React places the caret at the beginning of the text use this method:
setCaretToEnd(event) {
var originalText = event.target.value;
event.target.value = '';
event.target.value = originalText;
}
Found here:
https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js
<input type="text" autoFocus />
always try the simple and basic solution first, works for me.
To move focus to a newly created element, you can store the element's ID in the state and use it to set autoFocus. e.g.
export default class DefaultRolesPage extends React.Component {
addRole = ev => {
ev.preventDefault();
const roleKey = this.roleKey++;
this::updateState({
focus: {$set: roleKey},
formData: {
roles: {
$push: [{
id: null,
name: '',
permissions: new Set(),
key: roleKey,
}]
}
}
})
}
render() {
const {formData} = this.state;
return (
<GridForm onSubmit={this.submit}>
{formData.roles.map((role, idx) => (
<GridSection key={role.key}>
<GridRow>
<GridCol>
<label>Role</label>
<TextBox value={role.name} onChange={this.roleName(idx)} autoFocus={role.key === this.state.focus}/>
</GridCol>
</GridRow>
</GridSection>
))}
</GridForm>
)
}
}
This way none of the textboxes get focus on page load (like I want), but when you press the "Add" button to create a new record, then that new record gets focus.
Since autoFocus doesn't "run" again unless the component gets remounted, I don't have to bother unsetting this.state.focus (i.e. it won't keep stealing focus back as I update other states).
Simple solution without autofocus:
<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.
Ben Carp solution in typescript
React 16.8 + Functional component - useFocus hook
export const useFocus = (): [React.MutableRefObject<HTMLInputElement>, VoidFunction] => {
const htmlElRef = React.useRef<HTMLInputElement>(null);
const setFocus = React.useCallback(() => {
if (htmlElRef.current) htmlElRef.current.focus();
}, [htmlElRef]);
return React.useMemo(() => [htmlElRef, setFocus], [htmlElRef, setFocus]);
};
Warning: ReactDOMComponent: Do not access .getDOMNode() of a DOM node; instead, use the node directly. This DOM node was rendered by App.
Should be
componentDidMount: function () {
this.refs.nameInput.focus();
}
The simplest answer is add the ref="some name" in the input text element and call the below function.
componentDidMount(){
this.refs.field_name.focus();
}
// here field_name is ref name.
<input type="text" ref="field_name" />
After trying a lot of options above with no success I've found that It was as I was disabling and then enabling the input which caused the focus to be lost.
I had a prop sendingAnswer which would disable the Input while I was polling the backend.
<Input
autoFocus={question}
placeholder={
gettingQuestion ? 'Loading...' : 'Type your answer here...'
}
value={answer}
onChange={event => dispatch(updateAnswer(event.target.value))}
type="text"
autocomplete="off"
name="answer"
// disabled={sendingAnswer} <-- Causing focus to be lost.
/>
Once I removed the disabled prop everything started working again.
Read almost all the answer but didnt see a getRenderedComponent().props.input
Set your text input refs
this.refs.username.getRenderedComponent().props.input.onChange('');
According to the updated syntax, you can use this.myRref.current.focus()
Focus using createRef for functional components
To developers using Functional Components. This seems to suit. Focus happens on inputfield after clicking on the button. I've attached CodeSandbox link too.
import React from 'react';
export default function App() {
const inputRef = React.createRef();
return <>
<input ref={inputRef} type={'text'} />
<button onClick={() => {if (inputRef.current) { inputRef.current.focus() }}} >
Click Here
</button>
</>
}
https://codesandbox.io/s/blazing-http-hfwp9t
That one worked for me:
<input autoFocus={true} />
Updated version you can check here
componentDidMount() {
// Focus to the input as html5 autofocus
this.inputRef.focus();
}
render() {
return <input type="text" ref={(input) => { this.inputRef = input }} />
})
Since there is a lot of reasons for this error I thought that I would also post the problem I was facing. For me, problem was that I rendered my inputs as content of another component.
export default ({ Content }) => {
return (
<div className="container-fluid main_container">
<div className="row">
<div className="col-sm-12 h-100">
<Content /> // I rendered my inputs here
</div>
</div>
</div>
);
}
This is the way I called the above component:
<Component Content={() => {
return (
<input type="text"/>
);
}} />

How to find focused React component? (like document.activeElement)

If you had 500 components, each with a ref, how would you find which component has the user's focus? All components with a ref are focusable elements like <input />, <textarea />, etc. For simplicity, all of the refs are accessible from a single top-level component.
This is very simple if your React components have classNames - but if you want to find the ref of a document.activeElement, if there some way to achieve that without having to resort to classNames?
To touch on why we don't have classNames, we're using JSS via emotion. To have to manually assign everything a className just for this purpose would be rather absurd. No obvious alternative has occurred to me.
This may be a good use case for a custom hook that hooks into the native DOM methods to track focus events and then returns the active element. This will log the active element every time a new element receives focus:
const useActiveElement = () => {
const [active, setActive] = useState(document.activeElement);
const handleFocusIn = (e) => {
setActive(document.activeElement);
}
useEffect(() => {
document.addEventListener('focusin', handleFocusIn)
return () => {
document.removeEventListener('focusin', handleFocusIn)
};
}, [])
return active;
}
const App = () => {
const focusedElement = useActiveElement();
useEffect(() => {
if (focusedElement) {
focusedElement.value && console.log(focusedElement.value);
}
console.log(focusedElement);
}, [focusedElement])
return (
<div>
<input type="text"/>
<button>Button</button>
</div>
)
}
However, correlating this element with your refs could prove tricky, as you'd need to keep a collection of the refs to search through which would probably involve giving each element its own callback ref to store in an Array or something similar. But depending on what you need to do with the element once it's focused, it may not be necessary. For example, the code I posted above will log the value of the input if it exists. It would help to know more specifically what your use case is for tracking this data.
Do you want something like that, react gives you an option to track/focus using ref
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.button = React.createRef();
this.textarea = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// this.textInput.current.focus();
this.button.current.focus();
// this.textarea.current.focus();
}
render() {
return (
<div>
<input type="text" ref={this.textInput} />
<button ref={this.button}>something</button>
<textarea ref={this.textarea}></textarea>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default App;
You can get a detail here

How to access a child's state in React

I have the following structure:
FormEditor - holds multiple instances of FieldEditor
FieldEditor - edits a field of the form and saving various values about it in its state
When a button is clicked within FormEditor, I want to be able to collect information about the fields from all FieldEditor components, information that's in their state, and have it all within FormEditor.
I considered storing the information about the fields outside of FieldEditor's state and put it in FormEditor's state instead. However, that would require FormEditor to listen to each of its FieldEditor components as they change and store their information in its state.
Can't I just access the children's state instead? Is it ideal?
Just before I go into detail about how you can access the state of a child component, please make sure to read Markus-ipse's answer regarding a better solution to handle this particular scenario.
If you do indeed wish to access the state of a component's children, you can assign a property called ref to each child. There are now two ways to implement references: Using React.createRef() and callback refs.
Using React.createRef()
This is currently the recommended way to use references as of React 16.3 (See the documentation for more information). If you're using an earlier version then see below regarding callback references.
You'll need to create a new reference in the constructor of your parent component and then assign it to a child via the ref attribute.
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.FieldEditor1 = React.createRef();
}
render() {
return <FieldEditor ref={this.FieldEditor1} />;
}
}
In order to access this kind of ref, you'll need to use:
const currentFieldEditor1 = this.FieldEditor1.current;
This will return an instance of the mounted component so you can then use currentFieldEditor1.state to access the state.
Just a quick note to say that if you use these references on a DOM node instead of a component (e.g. <div ref={this.divRef} />) then this.divRef.current will return the underlying DOM element instead of a component instance.
Callback Refs
This property takes a callback function that is passed a reference to the attached component. This callback is executed immediately after the component is mounted or unmounted.
For example:
<FieldEditor
ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
{...props}
/>
In these examples the reference is stored on the parent component. To call this component in your code, you can use:
this.fieldEditor1
and then use this.fieldEditor1.state to get the state.
One thing to note, make sure your child component has rendered before you try to access it ^_^
As above, if you use these references on a DOM node instead of a component (e.g. <div ref={(divRef) => {this.myDiv = divRef;}} />) then this.divRef will return the underlying DOM element instead of a component instance.
Further Information
If you want to read more about React's ref property, check out this page from Facebook.
Make sure you read the "Don't Overuse Refs" section that says that you shouldn't use the child's state to "make things happen".
If you already have an onChange handler for the individual FieldEditors I don't see why you couldn't just move the state up to the FormEditor component and just pass down a callback from there to the FieldEditors that will update the parent state. That seems like a more React-y way to do it, to me.
Something along the line of this perhaps:
const FieldEditor = ({ value, onChange, id }) => {
const handleChange = event => {
const text = event.target.value;
onChange(id, text);
};
return (
<div className="field-editor">
<input onChange={handleChange} value={value} />
</div>
);
};
const FormEditor = props => {
const [values, setValues] = useState({});
const handleFieldChange = (fieldId, value) => {
setValues({ ...values, [fieldId]: value });
};
const fields = props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={handleFieldChange}
value={values[field]}
/>
));
return (
<div>
{fields}
<pre>{JSON.stringify(values, null, 2)}</pre>
</div>
);
};
// To add the ability to dynamically add/remove fields, keep the list in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
Original - pre-hooks version:
class FieldEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="field-editor">
<input onChange={this.handleChange} value={this.props.value} />
</div>
);
}
}
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFieldChange = this.handleFieldChange.bind(this);
}
handleFieldChange(fieldId, value) {
this.setState({ [fieldId]: value });
}
render() {
const fields = this.props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
{fields}
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
}
// Convert to a class component and add the ability to dynamically add/remove fields by having it in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
ReactDOM.render(<App />, document.body);
As the previous answers said, try to move the state to a top component and modify the state through callbacks passed to its children.
In case that you really need to access to a child state that is declared as a functional component (hooks) you can declare a ref in the parent component, and then pass it as a ref attribute to the child, but you need to use React.forwardRef and then the hook useImperativeHandle to declare a function you can call in the parent component.
Take a look at the following example:
const Parent = () => {
const myRef = useRef();
return <Child ref={myRef} />;
}
const Child = React.forwardRef((props, ref) => {
const [myState, setMyState] = useState('This is my state!');
useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})
Then you should be able to get myState in the Parent component by calling:
myRef.current.getMyState();
It's 2020 and lots of you will come here looking for a similar solution but with Hooks (they are great!) and with the latest approaches in terms of code cleanliness and syntax.
So as previous answers had stated, the best approach to this kind of problem is to hold the state outside of child component fieldEditor. You could do that in multiple ways.
The most "complex" is with a global context (state) that both parent and children could access and modify. It's a great solution when components are very deep in the tree hierarchy and so it's costly to send props in each level.
In this case I think it's not worth it, and a more simple approach will bring us the results we want, just using the powerful React.useState().
An approach with a React.useState() hook - way simpler than with Class components
As said, we will deal with changes and store the data of our child component fieldEditor in our parent fieldForm. To do that we will send a reference to the function that will deal and apply the changes to the fieldForm state, you could do that with:
function FieldForm({ fields }) {
const [fieldsValues, setFieldsValues] = React.useState({});
const handleChange = (event, fieldId) => {
let newFields = { ...fieldsValues };
newFields[fieldId] = event.target.value;
setFieldsValues(newFields);
};
return (
<div>
{fields.map(field => (
<FieldEditor
key={field}
id={field}
handleChange={handleChange}
value={fieldsValues[field]}
/>
))}
<div>{JSON.stringify(fieldsValues)}</div>
</div>
);
}
Note that React.useState({}) will return an array with position 0 being the value specified on call (Empty object in this case), and position 1 being the reference to the function
that modifies the value.
Now with the child component, FieldEditor, you don't even need to create a function with a return statement. A lean constant with an arrow function will do!
const FieldEditor = ({ id, value, handleChange }) => (
<div className="field-editor">
<input onChange={event => handleChange(event, id)} value={value} />
</div>
);
Aaaaand we are done, nothing more. With just these two slim functional components we have our end goal "access" our child FieldEditor value and show it off in our parent.
You could check the accepted answer from 5 years ago and see how Hooks made React code leaner (by a lot!).
Hope my answer helps you learn and understand more about Hooks, and if you want to check a working example here it is.
Now you can access the InputField's state which is the child of FormEditor.
Basically, whenever there is a change in the state of the input field (child), we are getting the value from the event object and then passing this value to the Parent where in the state in the Parent is set.
On a button click, we are just printing the state of the input fields.
The key point here is that we are using the props to get the input field's id/value and also to call the functions which are set as attributes on the input field while we generate the reusable child input fields.
class InputField extends React.Component{
handleChange = (event)=> {
const val = event.target.value;
this.props.onChange(this.props.id , val);
}
render() {
return(
<div>
<input type="text" onChange={this.handleChange} value={this.props.value}/>
<br/><br/>
</div>
);
}
}
class FormEditorParent extends React.Component {
state = {};
handleFieldChange = (inputFieldId , inputFieldValue) => {
this.setState({[inputFieldId]:inputFieldValue});
}
// On a button click, simply get the state of the input field
handleClick = ()=>{
console.log(JSON.stringify(this.state));
}
render() {
const fields = this.props.fields.map(field => (
<InputField
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
<div>
{fields}
</div>
</div>
);
}
}
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditorParent fields={fields} />;
};
ReactDOM.render(<App/>, mountNode);
You may access the child state by passing a callback to the child component.
const Parent = () => {
return (
<Child onSubmit={(arg) => {
console.log('accessing child state from parent callback: ', arg)
}}
/>
)
}
const Child = ({onSubmit}) => {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={setText}>
<button onClick={() => onSubmit(text)} />
</>
)
}
Now if you click the button in the child component, you will execute the function passed from the parent and have access to the child component's state variables.

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