在react中创建和管理复杂的输入状态

问题描述 投票:0回答:1

我正在尝试为 address 创建一个复杂的“输入”,其中包含地址行 1、地址行 2、国家、地区、城市和邮政编码的多个简单输入。 “输入”的行为应该像普通的简单输入一样,例如,它应该有一个值和一个 onChange。问题是当我在父组件中创建状态时

const [address,setAddress] = useState({...})

并将其传递给 AddressInput

<AddressInput value={address} onChange={handleAddressChange} />

由于状态变化,它开始无限循环渲染。

const AddressInput = ({ value, onChange }) => {

    const [address, setAddress] = useState({
        addressLine1: value.addressLine1,
        addressLine2: value.addressLine2,
        country: value.country,
        region: value.region,
        city: value.city,
        zipCode: value.zipCode,
    });

    const [modelValid, setModelValid] = useState({
        addressLine1: true,
        addressLine2: true,
        country: true,
        region: true,
        city: true,
        zipCode: true
    });


    const isModelValid =
        modelValid.addressLine1 &&
        modelValid.addressLine2 &&
        modelValid.country &&
        modelValid.region &&
        modelValid.city &
        modelValid.zipCode;



    const setModelProperty = (propertyName, val, isValid) => {
        setAddress(curr => ({
            ...curr,
            [propertyName]: val
        }));

        setModelValid(prev => ({
            ...prev,
            [propertyName]: isValid
        }));
    }


    // this is responsible for re-rendering of component when value param was changed 
    useEffect(() => {
        setAddress(
            {
                addressLine1: initialValue.addressLine1,
                addressLine2: initialValue.addressLine2,
                country: { value: initialValue.country, label: initialValue.country },
                region: { value: initialValue.region, label: initialValue.region },
                city: { value: initialValue.city, label: initialValue.city },
                zipCode: initialValue.zipCode
            }
        );
    }, [value]);

    // this one is for notify parent that value was changed internally
    useEffect(() => {
        if (onChange)
            onChange(
                {
                    addressLine1: address.addressLine1,
                    addressLine2: address.addressLine2,
                    country: address.country?.value,
                    region: address.region?.value,
                    city: address.city?.value,
                    zipCode: address.zipCode
                }
                , isModelValid);
    }, [address, isModelValid, onChange]);


    // ... other methods


    return (
        <Fragment>
            ... standart inputs with bindings goes here
        </Fragment>
    );
}

export default AddressInput
<AddressInputForm value={currentCompany.address} onChange={handleAddressChange}/>

    const handleAddressChange = useCallback((address, isValid) => {
        updateValidationValue("addressValid", isValid);
        setModelProperty("address", address); // when updating value on parent component it starts infinite loop of renderings
    }, [updateValidationValue])

所以我的问题是:在我的情况下,如何实现这种行为而不面临循环依赖等问题?预先感谢!

我尝试过使用React.memo,但它不起作用。如果您知道 GitHub 或 Stack Overflow 上的任何示例,请在评论中写下。

javascript reactjs react-hooks
1个回答
-1
投票

处理复杂的输入表单(例如 React 中的地址表单)时,仔细管理状态以防止无限循环至关重要。这些循环通常是由于父组件和子组件之间的状态更新而发生的。为了解决这个问题,请确保状态管理得到良好控制,以避免无休止的更新和重新渲染循环。

我们可以稍微重构一下代码来解决您的问题:

  1. 简化状态管理:我们可以直接使用父组件中的状态,而不是在
    AddressInput
    组件中复制父状态。这将避免不必要的状态同步。
  2. 受控
    AddressInput
    组件:
    我们可以直接更新父状态,使
    AddressInput
    成为受控组件。
const AddressInput = ({ value, onChange, onValidityChange }) => {
    const handleFieldChange = (fieldName) => (event) => {
        const newValue = { ...value, [fieldName]: event.target.value };
        onChange(newValue);
        checkValidity(newValue);
    };

    const checkValidity = (address) => {
        const isValid = Object.values(address).every(val => val); // Example: check if all fields are non-empty
        onValidityChange(isValid);
    };

    return (
        <Fragment>
            <input
                type="text"
                value={value.addressLine1}
                onChange={handleFieldChange('addressLine1')}
                placeholder="Address Line 1"
            />
            <input
                type="text"
                value={value.addressLine2}
                onChange={handleFieldChange('addressLine2')}
                placeholder="Address Line 2"
            />
            <input
                type="text"
                value={value.country}
                onChange={handleFieldChange('country')}
                placeholder="Country"
            />
            <input
                type="text"
                value={value.region}
                onChange={handleFieldChange('region')}
                placeholder="Region"
            />
            <input
                type="text"
                value={value.city}
                onChange={handleFieldChange('city')}
                placeholder="City"
            />
            <input
                type="text"
                value={value.zipCode}
                onChange={handleFieldChange('zipCode')}
                placeholder="Zip Code"
            />
        </Fragment>
    );
};
// ********************
// PARENT COMPONENT
// ********************

const [address, setAddress] = useState({
    addressLine1: '',
    addressLine2: '',
    country: '',
    region: '',
    city: '',
    zipCode: ''
});

const handleAddressChange = useCallback((newAddress) => {
    setAddress(newAddress);
}, []);

const handleValidityChange = useCallback((isValid) => {
    updateValidationValue("addressValid", isValid);
}, [updateValidationValue]);

return (
    <AddressInput
        value={address}
        onChange={handleAddressChange}
        onValidityChange={handleValidityChange}
    />
);

这种方法应该通过确保所有状态更新都得到最佳处理并且只有必要的更新才会触发重新渲染来解决无限渲染问题。

© www.soinside.com 2019 - 2024. All rights reserved.