nour
nour

Reputation: 163

React Js input is losing focus when I try typing in it but only when onChange is added

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

Answers (3)

Peter MAY
Peter MAY

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.

  1. RandomGenerated keys.
  2. Wrong event type.
  3. wrong react JSX attribute.

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

p1uton
p1uton

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

Dan Zuzevich
Dan Zuzevich

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

Related Questions