Value of React input field persists when clicking

Viewed 188

I'm trying to use a fetched value from DB as an initial value (shown until the component is clicked) of an input field.

However, when clicking on or typing in the input field, value of the input field persists meaning that a new value cannot be entered.

Component's file:

import React, { useEffect, useState } from "react";
import InputItem from './InputItem';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';

interface Props {
    income: string;
    setIncome: (value: string) => void; 
    totalExpenses: number;
    currencySymbol: string;
}

const IncomeExpensesContainer: React.FC<Props> = ({
        income,
        setIncome, 
        totalExpenses, 
        currencySymbol,
    }: Props) => {

    // Initializing didMount as false
    const [didMount, setDidMount] = useState(false);

    // Setting didMount to true upon mounting
    useEffect(() => { setDidMount(true) }, []);

   useEffect(() => {
        if (didMount) {
            axios.delete('http://localhost:4000/app/income')
            .catch(function (error) {
                console.log(error);
            });
    
            axios.post('http://localhost:4000/app/income', 
            {
                income: income
            });
        }
    }, [didMount,income]);
 
    return (
        <Grid container spacing={1} className="income-expenses-container">
            <Grid item xs={6}>
                <InputItem 
                    onBlur={setIncome}
                    title="Income" 
                    type="number" 
                    placeholder="Your income" 
                    value={income}
                    />
            </Grid>
            <Grid item xs={6} className="centered">
                <h3>Total Expenses</h3>
                <div>{totalExpenses}{currencySymbol}</div>
            </Grid>
        </Grid>
    );
  }

  export default IncomeExpensesContainer;

How could value be used just an initial value of the input field? Thank you!

2 Answers

Use the defualtValue prop instead. See Uncontrolled Components.

In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.

<InputItem 
  onBlur={setIncome}
  title="Income" 
  type="number" 
  placeholder="Your income" 
  defaultValue={income}
/>

I have figured out the solution:

  • created const [insertedValue, setInsertedValue] = useState(income); and assigned value={insertedValue}
  • used useEffect(() => { setInsertedValue(income) }, [income]);

This is the updated code:

import React, { useEffect, useState } from "react";
import InputItem from './InputItem';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';
interface Props {
    income: string;
    setIncome: (value: string) => void; 
    totalExpenses: number;
    currencySymbol: string;
}

const IncomeExpensesContainer: React.FC<Props> = ({
        income,
        setIncome, 
        totalExpenses, 
        currencySymbol,
    }: Props) => {

    const [insertedValue, setInsertedValue] = useState<string>(income);

    // Initializing didMount as false
    const [didMount, setDidMount] = useState(false);

    // Setting didMount to true upon mounting
    useEffect(() => { setDidMount(true) }, []);
    
    useEffect(() => { 
        setInsertedValue(income) 
    }, [income]);

   useEffect(() => {
        if (didMount) {
            axios.delete('http://localhost:4000/app/income')
            .catch(function (error) {
                console.log(error);
            });
    
            axios.post('http://localhost:4000/app/income', 
            {
                income: income
            });
        }
    }, [didMount,income]);
 
    return (
        <Grid container spacing={1} className="income-expenses-container">
            <Grid item xs={6}>
                <InputItem 
                    onChange={setInsertedValue}
                    onBlur={setIncome}
                    title="Income" 
                    type="number" 
                    placeholder="Your income" 
                    value={insertedValue}
                    />
            </Grid>
            <Grid item xs={6} className="centered">
                <h3>Total Expenses</h3>
                <div>{totalExpenses}{currencySymbol}</div>
            </Grid>
        </Grid>
    );
  }

  export default IncomeExpensesContainer;
Related