AutoFocus doesn't work in React - reactjs

I have a problem with autoFocus. It doesn't work for me, but using it:
<input onChange={this.handleName} value={this.state.name} placeholder="Name..." type="text" autoFocus />

None of these worked for me:
<input type="text" autoFocus .../>
<input type="text" autoFocus={true} .../>
<input type="text" autoFocus="true" .../>
<input type="text" autoFocus="autofocus" .../>
…even though in each case, the web inspector showed that <input type="text" autofocus .../> was rendered 🤔
Perhaps it's because of this phenomenon, I'm not sure:
If you render your React component into a detached element, React will call focus() too soon. This will result in the input not focusing when your React tree gets added to the DOM.
This did work for me:
import React, { useRef, useEffect } from "react";
export default function SignIn() {
const inputElement = useRef(null);
useEffect(() => {
if (inputElement.current) {
inputElement.current.focus();
}
}, []);
return (
<div>
<form action="...">
<input
ref={inputElement} // yes, this is working in Chrome on Mac
type="text" // allows username also; type 'email' woud insist on '#' sign
name="email"
placeholder="Email address"
autoComplete="email"
required
...
/>
</form>
</div>
);
}
That's strategy #2 from Daniel Johnson

Likely something else you are doing that is causing it to fail. It works fine in this simple example:
const App = React.createClass({
render() {
return (
<input
placeholder = "Name..."
type = "text"
autoFocus
/ >
);
}
});
ReactDOM.render( <
App / > ,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

For React function components when you get the error
Warning: Function components cannot be given refs. Attempts to access
this ref will fail. Did you mean to use React.forwardRef()?
import React, { useEffect, useRef } from 'react';
const InputField = () => {
const inputElement = useRef(null);
useEffect(() => {
inputElement.current?.focus();
}, []);
return (
<form action="...">
<input type="text" innerRef={inputElement} />
</form>
);
};
export default InputField;
If you use typescript and the compiler complains
Property 'focus' does not exist on type 'never'.ts(2339)
initialize inputElement with
const inputElement = useRef<HTMLInputElement>(null);

I had two Input's which I applied autoFocus to, one worked and the other didn't. They were both reactstrap Input fields:
import { Button, Input } from 'reactstrap';
I discovered that one had a type and no name, which was the one that worked. So I changed the other to be the same and it started working:
<Input
autoFocus="autofocus"
id="code"
type="text"
value={props.codeValue}
onChange={props.onCodeChange}
onKeyPress={event => {
if (event.key === 'Enter') {
confirmCode();
}
}}
/>

place that "autoFocus" infront of "input"
<input autoFocus type='text' ...others />
idk but this worked for me !

This is what worked for me on the functional react component as the autoFocus attribute did not work out of the box:
import React, { useCallback } from 'react';
const FeebackForm = () => {
const autoFocusFn = useCallback(element => (element ? element.focus() : null), []);
return (
<form action="...">
<input type="text" name="customerName" ref={autoFocusFn} />
</form>
);
};
export default FeebackForm;

If you're working on a conditional input component that appears post page is loaded on some user interaction, use react cleanup design to focus the input element.
jsx:
{ this.state.showInput && <input ref={ref => {this.inputRef = ref}} /> }
Class components:
this.setState({showInput: true},
() => {
this.inputRef && this.inputRef.focus()
}
)
Functional components:
useEffect(() => {
return () => {
this.inputRef && this.inputRef.focus()
}
}, [showInput])

If auto focus doesn't work for you then you can try using the .focus() function of javascript, which will always work :)
componentDidMount() {
document.getElementById("name").focus();
}
<input id="name" onChange={this.handleName} value={this.state.name} placeholder="Name..." type="text" autoFocus />

Related

The react-hook-form library and React Ref

Having such a simple react-hook-form example form:
import React from 'react';
import { useForm } from 'react-hook-form';
export default function ReactForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = (data) => console.log(data);
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="firstName">First Name:</label>
<input
id="firstName"
ref={(input) => {
console.log("firstName ref...")
textInput = input
}
}
{...register('firstName')} />
<br/>
<label htmlFor="lastName">Last Name:</label>
<input id="lastName" { ...register('lastName', { required: true })} /><br/>
<label htmlFor="age">Age:</label>
<input id="age" {...register('age', { pattern: /\d+/ })} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/><br/>
<input type="submit" />
{errors.firstName && <p>First name is required!</p>}
{errors.lastName && <p>Last name is required!</p>}
{errors.age && <p>Please enter number for age!</p>}
</form>
);
}
I'm getting :
Cannot read properties of null (reading 'focus')
error. The reason for this is that the ref seems not to be called at all (NOT giving the the firstName ref... in the console). Why doesn't the ref NOT being called & working!?
P.S.
I'he rewritten the above WITHOUT using the react-hook-form and the ref do work as expected so the problem lies somewhere in the react-hook-form library!
Your ref prop is being overridden by react-hook-form's register. The result of register includes it's own ref property that you're spreading onto the input. The docs indicate this is used for focusing the field on validation errors.
Your exception occurs in your click handler because your ref callback was never executed (and textInput is still null).
Try swapping the order so you override the ref provided by register('firstName') like so:
<input
id="firstName"
{...register('firstName')} // spread result of register including ref prop
ref={(input) => { // override ref prop
console.log("firstName ref...")
textInput = input
}}
/>

React - Create multiple useRefs in one line using object or array

I want to declare multiple useRefs and have them all in 1 object or an array. If that's not possible, then any means of declaring multiple refs in one line, like array destructuring
I tried these so far, neither worked.
attempt 1
const inputRefs = useRef({ input1: null, input2: null, input3: null, input4: null })
function focusInput() {
inputRefs.input2.current.focus()
}
return (
<div>
<input type="text" ref={inputRefs.input1} />
<input type="text" ref={inputRefs.input2} />
<input type="text" ref={inputRefs.input3} />
<input type="text" ref={inputRefs.input4} />
<button onClick={focusInput}>focus</button>
</div>
)
attempt 2
const [input1, input2, input3, input4] = Array(4).fill(useRef(null))
function focusInput() {
input2.current.focus()
}
return (
<div>
<input type="text" ref={input1} />
<input type="text" ref={input2} />
<input type="text" ref={input3} />
<input type="text" ref={input4} />
<button onClick={focusInput}>focus</button>
</div>
)
note I only have 4 inputs in the example, but in reality I have much more so hence I want to find a way. Thanks
You can set ref on your inputs wrapper and work with inputs through DOM API.
const containerRef = useRef(null);
function focusInput() {
const containerNode = containerRef.current;
containerNode.childNodes[2].focus();
}
return (
<div ref={containerRef}>
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
<button onClick={focusInput}>focus</button>
</div>
)
Few things to correct.
The useRef is a react hook (which should be defined in the top level of the function). Therefore, to create refs and store them in an array, you should use createRef.
Array.fill does not create 4 new refs instead of that share a single ref. That's why even though you clicked on the button always last input is always referred to. To get rid of that, use Array.from like below.
import { createRef } from "react";
export default function App() {
const [input1, input2, input3, input4] = Array.from({ length: 4 }, () =>
createRef(null)
);
function focusInput() {
input2.current.focus();
}
return (
<div>
<input type="text" ref={input1} />
<input type="text" ref={input2} />
<input type="text" ref={input3} />
<input type="text" ref={input4} />
<button onClick={focusInput}>focus</button>
</div>
);
}
kk I found a similar way to my first one but it works. Further suggestions are also welcome
import { useRef } from "react";
export default function App() {
const inputRefs = {input1: useRef(null), input2: useRef(null), input3: useRef(null), input4: useRef(null)}
function focusInput() {
inputRefs.input2.current.focus();
}
return (
<div>
<input type="text" ref={inputRefs.input1} />
<input type="text" ref={inputRefs.input2} />
<input type="text" ref={inputRefs.input3} />
<input type="text" ref={inputRefs.input4} />
<button onClick={focusInput}>focus</button>
</div>
);
}
You can simply use react-use-refs it used a clever generator to create how many useRef you need.
import useRefs from "react-use-refs";
function Demo() {
const [someRef, anotherRef] = useRefs();
return (
<div>
<div ref={someRef}>someRef</div>
<div ref={anotherRef}>anotherRef</div>
</div>
)
}
Or you can implement one yourself
import {useRef} from "react";
const MAX_ITERATIONS_COUNT = 50;
function iterator() {
return this;
}
export default function useRefs(...args) {
let count = 0;
return {
next() {
if (++count > MAX_ITERATIONS_COUNT) {
throw new Error(
"useMultipleRefs: reached more than 50 refs. This hook can be used exclusively with the array destructuring syntax."
);
}
return {
done: false,
value: useRef(args[count - 1] ?? args.at(-1)),
};
},
[Symbol.iterator]: iterator,
};
}
Or using my version from that collection here npm i #perymimon/react-hooks
import {useRefs} from #perymimon/react-hooks
// or
import useRefs from #perymimon/react-hooks/src/useRefs

I wrote code in React Js, but in localhost it shows error, IDK where is the problem

I wrote this code, and when I want to use that in browser, it shows me parsing error.
import OnChange from 'react'
export default function OnChange() {
let formData = {};
let change = (e) => {
const { value, name } = e.target;
formData = { ...formData, [name]: value }
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
alert(`${formData.lastName} ${formData.fName}`)
}}
>
<label htmlFor="lastName">Last name</label>
<input
type="text"
onChange={change}
id="lastName"
name="lastName"
value={formData.lastName}
/>
<input
type="text"
onChange={change}
id="lastName"
name="fName"
value={formData.fName}
/>
<button type='submit'>efgrf</button>
</form>
)
}
error:
Parsing error: Identifier 'OnChange' has already been declared
You imported OnChange as the default react import and then named your component the same. Just change the import of react to be correct.
import React from 'react';

Setting the default value of an input field after data is retrieved causes the content to overlap and the "onChange" event not to be triggered

I have an "edit category" component in my React application.
The ID is passed through the URL.
When the component is mounted, the action "fetchCategory" is called, which updates the props on the component with the current category.
I have a form which I want to be pre-populated, which I'm currently doing using the defaultValue on the input.
However, this isn't reflected on the state and the label for the text field overlaps the input field.
Any help would be greatly appreciated. I'll leave snippets of my code below which could help with understanding what I'm trying to do.
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchCategory } from "../../store/actions/categoryActions";
class AddOrEditCategory extends Component {
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id);
if (this.props.match.params.id) {
this.setState({
_id: this.props.match.params.id
});
}
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
render() {
const addingNew = this.props.match.params.id === undefined;
return (
<div className="container">
<h4>{addingNew ? "Add category" : "Edit category"}</h4>
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
defaultValue={this.props.category.name}
onChange={this.handleChange}
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
defaultValue={this.props.category.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
category: state.categoryReducer.category
};
};
export default connect(
mapStateToProps,
{ fetchCategory }
)(AddOrEditCategory);
EDIT: Included whole component as requested
You need to replace the 'defaultValue' attribute with 'value' in the inputs.
You are using a controlled vs uncontrolled component. You dont need to use defaultValue.
You can set the initial values on the promise success for fetchCategory
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id).then(response => {
// Set the initial state here
}
}
OR in
componentWillReceiveProps(nextProps) {
// Compare current props with next props to see if there is a change
// in category data received from action fetchCategory and set the initial state
}
React docs
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
onChange={this.handleChange}
value={this.state.name} //<---
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
value={this.state.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>

React Modifying Textarea Values

I am working on a project which is basically notepad. I am having problems though updating the <textarea>'s value when an ajax call is made. I tried setting the textarea's value property but then no changes to its value can be made. How can I make it so on a state change the textarea's value changes and can be edited.
The code I have is as follows.
In the parent class
<Editor name={this.state.fileData} />
In the Editor class
var Editor = React.createClass({
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.props.name}></textarea>
<input type="submit" value="Save" />
</form>
);
}
});
I can't use defaultValue because the value of the textarea is not known on page load and when I try and put the data between the textareas nothing happens. I would like it to take the state value whenever the state changes but have it editable in between.
Thanks
Edit
I managed to get it working using jQuery but would like to do it in React instead, I called this before render:
$('#noter-text-area').val(this.props.name);
I think you want something along the line of:
Parent:
<Editor name={this.state.fileData} />
Editor:
var Editor = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
return {
value: this.props.name
};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Save" />
</form>
);
}
});
This is basically a direct copy of the example provided on https://facebook.github.io/react/docs/forms.html
Update for React 16.8:
import React, { useState } from 'react';
const Editor = (props) => {
const [value, setValue] = useState(props.name);
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={value} onChange={handleChange} />
<input type="submit" value="Save" />
</form>
);
}
Editor.propTypes = {
name: PropTypes.string.isRequired
};
As a newbie in React world, I came across a similar issues where I could not edit the textarea and struggled with binding. It's worth knowing about controlled and uncontrolled elements when it comes to react.
The value of the following uncontrolled textarea cannot be changed because of value
<textarea type="text" value="some value"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following uncontrolled textarea can be changed because of use of defaultValue or no value attribute
<textarea type="text" defaultValue="sample"
onChange={(event) => this.handleOnChange(event)}></textarea>
<textarea type="text"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following controlled textarea can be changed because of how
value is mapped to a state as well as the onChange event listener
<textarea value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
Here is my solution using different syntax. I prefer the auto-bind than manual binding however, if I were to not use {(event) => this.onXXXX(event)} then that would cause the content of textarea to be not editable OR the event.preventDefault() does not work as expected. Still a lot to learn I suppose.
class Editor extends React.Component {
constructor(props) {
super(props)
this.state = {
textareaValue: ''
}
}
handleOnChange(event) {
this.setState({
textareaValue: event.target.value
})
}
handleOnSubmit(event) {
event.preventDefault();
this.setState({
textareaValue: this.state.textareaValue + ' [Saved on ' + (new Date()).toLocaleString() + ']'
})
}
render() {
return <div>
<form onSubmit={(event) => this.handleOnSubmit(event)}>
<textarea rows={10} cols={30} value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
<br/>
<input type="submit" value="Save"/>
</form>
</div>
}
}
ReactDOM.render(<Editor />, document.getElementById("content"));
The versions of libraries are
"babel-cli": "6.24.1",
"babel-preset-react": "6.24.1"
"React & ReactDOM v15.5.4"

Resources