简体   繁体   中英

How to avoid data duplication in react forms

Without using any libraries I am struggling to get a clean code doing a simple form in React.

Let's say I have

import React, { useState } from 'react';

export const CreateUserForm = () => {
  const [firstName, setFirstName] = useState('');

  const userAddress = {};

  const handleAddressChange = ({ address, isValid }) => {
    userAddress = { ...address };
  };

  const subscribe = () => {
    createUser({firstName, userAddress}).then((response) => console.log(response));
  };

  return (
    <div>
      <input onChange={setFirstName}></input>
      <AddressForm onChange={handleAddressChange}></AddressForm>
      <button onClick={subscribe}></button>
    </div>
  );
};

Here I am duplicating the address in the CreateUserForm component ( address is managed in the AddressForm state).

I could add some props to the AddressForm component, so I would end up with something like that:

import React, { useState } from 'react';

export const CreateUserForm = () => {
  const [firstName, setFirstName] = useState('');
  const [address, setAddress] = useState({});

  const handleAddressChange = ({ address, isValid }) => {
    setAddress({ ...address });
  };

  const subscribe = () => {
    createUser({firstName, userAddress}).then((response) => console.log(response));
  };

  return (
    <div>
      <input onChange={setFirstName}></input>
      <AddressForm address={address} onChange={handleAddressChange}></AddressForm>
      <button onClick={subscribe}></button>
    </div>
  );
};

So here I would not duplicate the address but I would render the whole CreateUserForm component each time the address change, am I wrong?

So I feel like neither one of those solution is fine, or am I missing something? The first one seems more natural to me but I kinda duplicate the information..

What would be the correct way?

The second option is the correct option as the first likely won't work and if it does then there are some bugs waiting to show themselves if you ever make the CreateUserForm component more complicated.

Option 1 doesn't use state properly and can very easily mess up because the userAddress isn't stateful. It'll be reset to {} everytime the component re-renders. In order for the first option to work, you'd need to useRef to set up your address variable.

import React, { useState, useRef } from 'react';

export const CreateUserForm = () => {
  const [firstName, setFirstName] = useState('');

  const userAddress = useRef({});

  const handleAddressChange = ({ address, isValid }) => {
    userAddress.current = { ...address };
  };

  const subscribe = () => {
    createUser({firstName, userAddress: userAddress.current}).then((response) => console.log(response));
  };

  return (
    <div>
      <input onChange={setFirstName}></input>
      <AddressForm onChange={handleAddressChange}></AddressForm>
      <button onClick={subscribe}></button>
    </div>
  );
};

This change to the first option would prevent re-renders at the cost of making the AddressForm much less controlled. It would also prevent you from passing the address down to any other children of the component since they would not re-render because the parent doesn't either unless you change the first name input.

The CreateUserForm component would definitely re-render every time handleAddressChange is called in the second option. Typically in smaller applications, the re-render will have no issues with performance. If you begin to have performance issues, then figure out what children of CreateUserForm take a while to re-render and memoize them (React.Memo or PureComponent) that way the component will re-render quickly. Although memoization requires you to also make sure that what you are passing into the components are referentially stable (ie useCallback and useRef).

What I usually do to check if a form is rendering quickly enough is to hold down a key on the form inputs and see if it repeats in a non-jerky way. Although that particular metric is mostly from my ideals and isn't fully necessary for all applications.

I know you were trying to work without a library for the form, but react-hook-form seems to be able to give the range from completely uncontrolled to controlled depending on the purpose to keep up performance and minimize excess re-renders.

Second approach is better as you don't have to maintain data in both components (parent and child).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM