Dito
Dito

Reputation: 995

Set form element initial value which is received via props

I am using React Hooks to handle a simple form. I receive 'transaction' object as a prop and I want to set input values based on this object, because I am trying to update this 'transaction'

As you can see below I am trying to set my description inputs value based on 'transaction' object, which I receive as prop

//TransactionsForm.js

const TransactionsForm = ({ onSubmit, transaction }) => {

  const [values, setValues] = useState({
    description: transaction.description
  })

  const handleChange = event => {
    const { name, value } = event.target
    setValues({ ...values, [name]: value })
  }

  return (
    <form>      
          <input
            type="text"
            placeholder="description"
            name="description"
            value={values.description}
            onChange={handleChange}
          /> 
        <button>Submit</button>
    </form>
  )
}

this 'transaction' object looks like this initially:

{
    id: null,
    description: '',
    transactionType: '',
    date: '',
    category: '',
    amount: ''
}

but then I update it, when the user clicks the edit button and set it to the clicked transaction object to pass to my TransactionsForm.js and thus update the form input values based on this.

if I set my input value like this

<input
   type="text"
   placeholder="description"
   name="description"
   value={transaction.description} // like this it's working, but when I use values.description the value is not updating
   onChange={handleChange}
/> 

Upvotes: 0

Views: 2322

Answers (1)

UjinT34
UjinT34

Reputation: 4977

I guess you have an issue with updating values when TransactionsForm props change. useState only sets the initial value on the first render and doesn't update it when props change. You need to do it manually:

const TransactionsForm = ({ onSubmit, transaction }) => {

  const [values, setValues] = useState({
    description: transaction.description
  });

  useEffect(() => setValues(
    oldValues => (
      {...oldValues, description: transaction.description}
    )
  ), [transaction.description]);

  ...

useEffect will be called on initial render and every time transaction.description gets a new value. Since setValues receives a new object you'll get unnecessary extra render after the initial one. To avoid that you can store only the description in your state:

const [descripton, setDescription] = useState(transaction.description);

If you really need a complex state structure consider using useReducer and store "oldProps" with useRef:

const TransactionsForm = ({ onSubmit, transaction }) => {

  const [values, setValues] = useState({
    description: transaction.description
  });
  const oldDescription = useRef(transaction.description);

  useEffect(() => {
    if (oldDescription.current !== transaction.description) {
      oldDescription.current = transaction.description;
      setValues(
        oldValues => (
          {...oldValues, description: transaction.description}
        )
      );
    }
 }, [transaction.description]);

  ...

Upvotes: 4

Related Questions