React autoFocus attribute is not rendered - reactjs

I'm trying to render an <input> field with autofocus:
<input type="text" autoComplete="off" autoFocus={true} className="form-control" />
But it's being rendered as:
<input type="text" autocomplete="off" class="form-control">
And the input does not focus.
Why is that? How can I get the autofocus attribute to render?

This is expected, actually. React will manually call focus() on the relevant element, but it will not add the attribute itself.
Here is a quote from Dan Abramov from a ticket response from ~2 years ago:
This is intentional because it works very differently in different browsers. So instead of actually setting a DOM attribute, we polyfill its behavior in JavaScript.
Also, from another thread:
Chrome respects dynamically added elements with autoFocus if there hasn’t been one before. Even creating an input with autoFocus, adding it to the document, and removing it in the next tick is enough to disable autofocusing of any elements added in the future in Chrome.
Firefox just doesn’t care about any dynamically added elements with autoFocus at all.
And Safari always respects them.
That being said, you could still force the attribute with autofocus="true" instead of autoFocus={true} but it might not actually work in a reliable fashion. After all, there is a reason the React team polyfilled this.
In other words, if you want a reliable, consistent cross-browser behavior use autoFocus, if you must have the native attribute you can use autofocus="true", but know that you'll be at the mercy of letting the browser decide when and how said element will be focused.

You can try using a ref, and setting the focus when your component is ready.
const YourComponent = (props) => {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
return <div>
<input autofocus="true" ref={inputEl} />
</div>;
}

Answer by Brandon works perfectly. Additionally, if you want to autofocus on an input on a Modal for example, just keep track of the modal state and use that to focus. For example:
const refModal = useRef(null);
useEffect(() => {
if(modalOpen) {
refModal.current.focus();
}
}, [modalOpen]);

Related

Not getting any console.log data from first name field box

import React, { useState } from "react";
import "../styles.css";
const FormBox = () => {
const [value, setValue] = useState({
fName: "",
lName: "",
});
const [textArea, setTextArea] = useState("");
const handleSumbit = (e) => {
e.preventDefault();
console.log(value);
console.log(textArea);
};
return (
<div className="center">
<form onSubmit={handleSumbit}>
<div>
{/*This is how to set a control form box */}
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
{/*This is how to set a control form box */}
<input
placeholder="Last Name"
type="text"
value={value.lName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
<textarea
value={textArea}
onChange={(e) => setTextArea(e.target.value)}
></textarea>
<div>
<input type="submit" />
</div>
</div>
</form>
</div>
);
};
export default FormBox;
I'm working on a basic React project and I'm currently getting the message "Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
input"
Another problem is that I'm not getting any info when typing in first name field box. I do get back last name and textarea.
Thank you so much and happy holiday
You are having two issues:
Not getting info from the first name field
The uncontrolled vs controlled warning
Issue 1.
This actually happens because on both the first name and last name inputs you are doing setValue(e.target.value). setValue() sets the object that is originally initialized as { fName: '', lName: '' }. This means that even though the value is an object at first, when writing on any of the fields, it will become a simple string, either the first or last name.
This problem does not happen with the text area, since you keep track of its value on a separate state, textArea. That's one way to fix your issue, hold both fName and lName on separate useState calls. Another way is to replace the old value object with a new one that works as expected, for example:
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue({ ...value, fName: e.target.value })}
/>
I would also suggest avoiding abbreviations whenever possible. You could've used firstName and lastName instead as variable names. It really goes a long way improving readability.
Issue 2.
This warning is a side effect of the first issue. It appears because you are using controlled inputs but when writing on one of them the object is turned into a string and both value.fName and value.lName are undefined. Controlled inputs are not supposed to have undefined as a value, since that's usually an indicator that you want to use uncontrolled inputs. The empty value should be an empty string, which was intended and will be fixed if you fix the first issue.
There's another stack overflow sharing the differences between controlled and uncontrolled components: What are React controlled components and uncontrolled components?
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a "dumb component".
A Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
The important bit to note here is that an uncontrolled component doesn't receive a value prop and hence its value is undefined.

React Typescript Mui `onBlur` firing every change

I am writing a front-end app in Typescript and React and am using MUI. I have the following code for a form:
<TextField
label="Password"
value={this.state.password}
placeholder="Choose a password"
type="password"
onChange={(event: any) => this.setState({
password: event.target.value
})}
onBlur={console.log('here')}
error={this.state.passwordError}
/>
With this code, every time I click a key, onBlur gets fired. Isn't it only supposed to fire when I leave the form?
Would be helpful to see the onblur function. It doesn't look like you want to pass anything to it, so simply changing the line onBlur={this.onblur()} to onBlur={this.onblur} should do the trick.
However, if your function looks like this
onblur() {
...
}
then you should bind the context to it ('this'). Instead, I'd suggest going for
onblur = event => {
...
}
which will have the context already binded.

A single onChange listener on a <form> tag

I was just playing a bit with a more generic way of dealing with form data, and figured that setting a single onChange listener on the wrapping form tag should work for all changes to input fields.
And indeed, it does - no surprise there. Events get called, I know what changed by inspecting event.target.name and can update state accordingly. Fully as expected.
However, React doesn't seem to like it and spams the well known "You provided a value prop to a form field without an onChange handler" console warning.
Would there be any other reason for not doing this, apart from the console warning? It seems to eliminate a lot of duplication React otherwise gets criticised about.
class App extends Component {
state = {
name: 'Default Name',
number: 12,
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
})
}
render() {
const { crashLogo } = this.props;
return (
<div className="App">
<form onChange={this.handleChange}>
<input type="text" name="name" value={this.state.name} />
<input type="number" name="number" value={this.state.number} />
</form>
</div>
);
}
}
Just for clarity: I'm not asking for alternative solutions, I know I can set the same change listener directly on every input, use redux-form, own wrapper components with context magic and so on...

How to hide/show Field in FieldArray for Redux Form v6

given the fact in the example http://redux-form.com/6.0.5/examples/fieldArrays/. All the renderField.. functions are outside of the React Class. Hence how am i suppose to use react state or props to determine whether i want to hide or show a Field?
What I'm trying to do is to have a button to manipulate a state to display 'block' or 'none' for a Field. Can someone guide me? I tried to put the renderField variable inside the render of the React class, however this result in bugs.
All the props which are passed to Field are accessible to component props.
<Field
name={foo}
type="text"
component={TextField}
displayBlock={displayBlock}
/>
const TextField = props => {
if(props.displayBlock) {
...
}
return (
<div>
<input {...props.input} />
</div>
);
};
Thanks to Runaground who suggested an answer to me. I came to realized that I can pass in state as props to the FieldArray, such as
<FieldArray name="histories" component={renderHistories} openClose={this.state.openClose}/>
This allow me to utilize an array of state from this.state.openClose, to control which field i would like to hide or show.
<Field
name={`${histories}.details`}
type="text"
component={renderField}
style={{display: openClose[index] ? 'block' : 'none'}}
/>
However, even though I am able to control the display of the Field, the field array does not get rerendered.
Hence, I have to add on two functions
fields.push({});
setTimeout(()=>{
fields.pop();
}, 1);
that is taken from http://redux-form.com/6.0.5/docs/api/FieldArray.md/, to actually rerender the fieldarray whenever i hide or show the field. Hopefully there is a more elegant way to do this as the fieldarray does flicker.

Set focus on a react-bootstrap.Input field using refs.x.getDOMNode.focus

The JSX:
var Button = require("react-bootstrap").Button;
var Input = require("react-bootstrap").Input;
var MyClass = React.createClass({
onclick: function () {
this.refs.email.getDOMNode().focus();
console.log('DOM element', this.refs.email.getDOMNode());
}
render: function () {
return (
<div>
<Button onClick={this.onlick}>Login</Button>
<Input ref='email' type='email' />
</div>
);
},
});
Notes:
The input field is a react-bootstrap.Input class.
The getDOMNode().focus() concept is from Facebook react docs
When the button onclick handler runs, the email input field should be getting the focus, but it's not happening. I found that the react-bootstrap Input field class is rendering the real DOM input field inside a wrapping div. This is the output of the console.log:
<div class="form-group" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0">
<input class="form-control" type="email" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0.1:$input">
</div>
So looks like the input isn't getting the focus because the focus is applied on the wrapping div, which rejects it (I use document.activeElement in the console to check).
Question is, how to focus on the real input element?
Note:
A bit unrelated but React respects the autoFocus property. That's useful in case the focus is needed to be set immediately after rendering. Still, it's not a substitute for the programmatic way.
Try getInputDOMNode() instead of getDOMNode().
in render
<FormControl
type='text'
ref={(c)=>this.formControlRef=c}
/>
in event handler
someHandler() {
ReactDOM.findDOMNode(this.formControlRef).focus();
}
Since findDOMNode is no longer the suggested method and may be deprecated by Facebook, you should use the autoFocus property (which is provided by the React framework, not react-bootstrap). This is a cross-browser polyfill for the HTML autofocus attribute.
<FormControl
autoFocus
...
/>
Sources:
Focusing to the FormControl
Support needed for focusing inputs in modals?
FormControl provides inputRef attribute
try it,
<FormControl inputRef={ref => { this.input = ref; }} />
https://react-bootstrap.github.io/components/forms/
or you may use vanilla javascript and select the element and set focus
document.getElementById("myInput").focus()

Resources