Reputation: 163
The problem I am facing is really annoying, when I try to edit the default value of my input text, the input field won't type my changes or it would lose focus only after typing the first character and it will return to it's default value. But please note that this only happens when I add the onChange event. If I remove it, the input works normally. What is happening
const AddMaker = () => {
const [make, setMake] = useState();
function ConditionalTableRow(props) {
let { item, index } = props;
if ( index === editModeIndex)
return (<TableRowWithSave item={item} index={index} />)
else
return (<TableRowWithEdit item={item} index={index} />)
}
function TableRowWithSave(props) {
let { item } = props;
return (
<TableRow>
<TableCell>
<input type="text" defaultValue={item.make} onChange={e => setMake(e.target.value)} />
</TableCell>
<TableCell>
<button className="palletSaveButton" onClick={handleSaveClick}> Save </button>
</TableCell>
</TableRow>
)
}
return (
<TableBody>
{
records.map((item, index) => (
<ConditionalTableRow key={index} item={item} index={index} />
))
}
</TableBody>
);
}
Upvotes: 1
Views: 5839
Reputation: 21
The short answer is possible because with each key stroke you are causeing a rerender with the onChange event.
This is a great question, and I had the same problem which was 3 parts.
Keys: when you use random keys each rerender causes react to lose focus (key={Math.random()*36.4621596072})
.
EventTypes: onChange cause a rerender with each key stroke, but this can also cause problems. onBlur is better because it updates after you click outside the input. An input, unless you want to "bind"
it to something on the screen (visual builders), should use the onBlur
event.
Attributes: JSX is not HTML and has it's own attributes (className,...)
. Instead of using value, it is better to use defaultValue={foo}
in an input.
Once I changed these 3 things it worked great. Example below.
Parent:
const [near, setNear] = useState( "" );
const [location, setLocation] = useState( "" );
<ExperienceFormWhere
slug={slug}
questionWhere={question_where}
setLocation={handleChangeSetLocation}
locationState={location}
setNear={setNear}
nearState={near}
key={36.4621596072}/>
Child:
<input
defaultValue={locationState}
className={slug+"_question_where_select search_a_location"}
onBlur={event => setLocation(event.target.value)}/>
a great resource for form building is: https://react-hook-form.com/
Upvotes: 1
Reputation: 266
Move useState
to TableRowWithSave
and use value
instead of defaultValue
function ConditionalTableRow(props) {
let { item, index } = props;
if ( index === editModeIndex)
return (<TableRowWithSave item={item} index={index} />)
else
return (<TableRowWithEdit item={item} index={index} />)
}
function TableRowWithSave(props) {
let { item } = props;
const [make, setMake] = useState(item.make);
return (
<TableRow>
<TableCell>
<input type="text" value={make} onChange={e => setMake(e.target.value)} />
</TableCell>
<TableCell>
<button className="palletSaveButton" onClick={handleSaveClick}> Save </button>
</TableCell>
</TableRow>
)
}
const AddMaker = () => {
return (
<TableBody>
{
records.map((item, index) => (
<ConditionalTableRow key={index} item={item} index={index} />
))
}
</TableBody>
);
}
EDIT: Also it's better to move ConditionalTableRow
and TableRowWithSave
outside AddMaker
Upvotes: 1
Reputation: 3831
Try adding a value
attribute to the input element.
<input type="text" value={make} defaultValue={item.make} onChange={e => setMake(e.target.value)} />
Upvotes: 1