im currently trying to create an dynamic form that uses a sub component to render out some input fields as createing them static would exceed a limit. I pass the states for the the form i made aswell as some date and the change form. But when i render the form as it does so fine! as soon as i change the state its sends a warning about uncontrolled components that you can read about it here
I have tried pre-setting all the fields with empty defaults but it doesnt help.
Am i doing it somewhat correct or totally wrong? or what is the correct way, or what am i doing wrong. thanks in adbance.
As for the code looks like this:
Edit.js
export default function InventoryItemEdit() {
const [form, setForm] = useState({});
function handleFormChange(e) {
setForm({ ...form, [e.target.name]: e.target.value });
}
const variants = (
<ItemVariants
form={form}
onChange={handleFormChange}
fields={form.sizes}
/* ^^ fields data fetched from the server i set via use effect hook when loading the page */
/>
);
const updateItem = async (event) => {
event.preventDefault();
/* Do form submit post */
};
return (
<form onSubmit={updateItem}>
<div>
<p htmlFor="name">Name</p>
<input
id="name"
name="name"
onChange={handleFormChange}
value={form.name}
type="text"
required
placeholder="Name of Item"
/>
</div>
{variants}
</form>
);
}
As for the sub component ItemVariants it looke like this
ItemVariants.js
export default function ItemVariants({
form = {},
onChange = '',
fields = [],
}) {
return (
<>
{fields.map((row, index) => (
<div>
<span>
{row.name}
</span>
<input
type="text"
id={`variant${index}`}
name={`variant${index}`}
onChange={onChange}
value={form[`variant${index}`]}
required
min="0"
placeholder="0"
defaultValue="0"
/>
</div>
))}
</>
);
}
Controlled component: is when your input value is coming from a single source of truth a state(meaning for an input value to change, you need to update the state holding the value)
Uncontrolled component: is when your input value is not coming from a state but rather it's coming from something we call useRef/createRef which helps to reference to react syntactic DOMNODE thus giving us the ability to interact with input in a false-actual-DOM(as if we were query an actual DOM input-node in pure-JS). Meaning if your input value is not coming from a state, it must come from a ref(ref.current.value which is equivalent to document.querySelector(theInputLocator).value).
Answer to your bug: I see that everything is okay, but the only issue is that you didn't set the initial state with the name key. Thats an analysing issue because in your code you doing [e.target.name]:e.target.value and the way inputs works generally is that event is triggered but must recent value is emitted when the event happens next. it works like a++.
Do this:
const [form, setForm] = React.useState({ name: '' });
The general rule of thumb is that always describe your form input keys when your inputs are predefine and not dynamic(in this case your name input is predefined).
So After digging reading this post i have to thank! #juliomalves for solution with value={form[`variant${index}`] ?? ''} or value={form[`variant${index}`] || ''}
I also found out i was accidently adding defaultvalue aswell as value which cannot be set at the same time i found out. After i tested it removed the default value and set it correctly to be a string! it now works like a charm!
Also thanks to #Emmanuel Onah for the explaination it really helped me understand more about the react states!
Thanks again for everyone that helped
Related
When I type something in my custom field it loses focus and needs to be clicked again in order to add data, Below is the sandbox link to code. It only happens when I pass error and touched props to check for validation, If I remove that it works perfectly.
https://codesandbox.io/s/formik-example-forked-w0bub?file=/index.js
Browser I'm using is Chrom for windows
The input inside the functional component is recreated every time when there is state changes.
Read this blog post to know more the behaviour of nested functional components
To avoid recreation problem, you can follow the Formik example to create other functional component and use it inside your component.
const MyInput = ({ field, form, ...props }) => {
return <input {...field} {...form} {...props} />;
};
...
<Field
id="email"
name="email"
placeholder="Email"
component={MyInput}
/>
Here is the codesandbox for demo.
Good afternoon devs !!
I have a little problem, I'm using reactJS, and I'm using react-form-hook, in which I use an TextField from Material-UI. When I fill an input with name cep, it calls a function that sends the content from this to an api, which returns, content to fill the input there, The problem happens that when I try to set my inputs with the returned content. The Input label remains on the field, as in the case shown in the photo, in the last input. That came the answer.
the fields first with the setValues of the react-form-hook, but the error persisted after I tried to set this way document.querySelector ('# inputStreet'). value = street;
Finally the way I decided was to create in this inputs that receive update a useState for each
const [srua, setSrua] = useState ('');
const [sbairros, setSbairros] = useState ('');
const [scity, setScidade] = useState ('');
const [sUF, setSUF] = useState ('');
Only in this way was he able to solve the problem of the label not being on the content of the input, as already exemplified in the photo in the last input, I believe this is not the most correct way, so I would like a help on how to solve this in a more efficient. just like that
Repository link.
The problem with your TextField is that the InputLabel does not shrink. the label shrinks when the TextField is in focus or when the value of the TextField is not empty.
But since you are setting the value programmatically, the input is not focused, the value is also not registered because it's uncontrolled which means the TextField keeps track and updates the value internally for you and ignore external value if provided.
In order to make the TextField controllable, as suggested from the name, you must take control of the TextField's value from the beginning which is what you did in the question.
You can also simplify this process by using react-hook-form's Controller. It's a wrapper component to help you pass common props to the controlled components
<Controller
render={({ onChange, onBlur, value, name }) => (
<TextField
onBlur={onBlur}
onChange={(e) => onChange(e.target.value)}
value={value}
name={name}
label="Email"
/>
)}
name="email"
control={control}
// need to initialize value to register the TextField as controlled
// if we don't provide, the default value is undefined, which means uncontrolled
defaultValue=""
/>
Which can be shortened to this
<Controller
render={(props) => <TextField {...props} label="Email" />}
name="email"
control={control}
defaultValue=""
/>
Live Demo
I have a very interesting situation.
This is my component:
export const checkbox = ({ data }) => {
const inputRef = useRef(null);
console.log(inputRef); // Does not appear
return (
<FormGroup>
{data.data.map(item => (
<FormGroup>
<Input
ref={inputRef}
/>
</FormGroup>
</FormGroup>
);
};
It is a child of other component which basically get values and submits to form.
but to no avail..
How can I fix this? I just need it to keep the focus, so I can continue typing. Should I use React.memo() for this?
Also, please note that I have checked all the references I could find on SO and nothing helped. I have spent 3 days on this.
Based on our chat, the root cause was elements having a dynamic key prop which changed on each render. This was causing a new textarea element to be rendered with each change event and the new element didn't have focus.
The solution is to use a deterministic key which doesn't change between renders.
I have implemented in React a webpage with 3 input fields, each one with the properties of
onChange={this.handleChange} and disabled={this.isDisabled()}
The desired behavior is that when an input field contains 2 digits, the focus will be moved to the next input field.
As long as the field doesn't contain 2 digits, the fields next to must be disabled.
What actually happens, when I type the second digit in the first field, it runs the handleChange function, that function checks whether the field contains 2 digits, find out that yes, and moves the focus to the next input field.
But the next field is disabled! (because the isDisabled function didn't run yet!)
So the cursor doesn't move..
I want to change the order of the happenings, or any other way to solve it.
Do you have any suggestions?
The problem is that this.isDisabled() runs immediately in render but this.handleChange runs on click and most possibly doesn't change the state thus no rerender.
You sholdn't run function on next input, you should pass true or false to its disabled prop. Just make handleChange update the state which defines which fields are disabled. And pass that state to your inputs accordingly.
I had faced the same issue a few days back. My approach was however using react states and focusing the input by its id, that was fetched from state;
So first we make a input - id map for our convenience. And use document.getElementById(this.state.active).focus() function. We change our state via our change handler.
render() {
this.setInputFocus();
return (
<div className="App">
<input id="1" onChange={this.onChange} />
<input id="2" onChange={this.onChange} />
<input id="3" onChange={this.onChange} />
</div>
);
}
setInputFocus = () => {
if (document.getElementById(this.state.active)) {
document.getElementById(this.state.active).focus();
}
};
onChange = e => {
if (e.target.value.length === 2) {
this.setState({ active: this.state.active + 1 });
}
};
Here is a full code that somewhat solves the issue
I have used TSX file for loading text box in React JS as below :
<input type={'text'} value={employees.length > 0 ? employees[0].name : ""} id=
{'Name'} label={'Name'} name={'Name'} htmlFor={'Name'} />
Now when I load this file then we cant write any thing in text box.
So anyone can please help me?
You cannot write anything because you have created a controlled component which means that the value of the input will always be whatever the value prop evaluates to - in this case employees[0].name or "".
With some minor adjustments you can make this work, either by making it a controlled component with an event listener which updates the value, or by making it an uncontrolled component.
In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.
More info here and here.
Option 1 - Controlled component (recommended)
To make a controlled component, you need to add the onChange event listener and have that update the variable that you pass into value. It's not clear from your question what employees is; if it's a state or prop variable or something else.
Assuming it's a state variable, you can do:
handleChange = (e) => {
let arr = this.state.employees.slice();
arr[0].name = e.target.value;
this.setState({employees: arr});
}
render() {
let {employees} = this.state;
return (
<input type={'text'} onChange={this.handleChange} value={employees.length > 0 ? employees[0].name : ""} id={'Name'} label={'Name'} name={'Name'} htmlFor={'Name'} />
);
}
Option 2 - Uncontrolled component
You only need a slight modification to make your input an uncontrolled component. Simply replace value with defaultValue:
<input type={'text'} defaultValue={employees.length > 0 ? employees[0].name : ""} id={'Name'} label={'Name'} name={'Name'} htmlFor={'Name'} />
As a side note, you don't need to wrap string literals in brackets. So instead of <input type={'text'} ... you can just do <input type='text' ..., if you prefer.