LearnerRC
LearnerRC

Reputation: 59

React form with nested json and controlled components

I'm buidling a react form with reusable components.

I'd like to update my json so I can nest some data, but I'm getting a "controlled vs uncontrolled" error (again!) which I can't solve. I've been researching on it, but I'm probably lacking the right vocabulary to be efficient. Where or how should I define "pet"..?

Later, my goal is to be able to add several pets to one owner.

PetsForm

import React, { useState, useEffect } from 'react';
import Controls from './reusables/Controls';
import { useForm, Form } from './reusables/useForm';

const initialFieldValues = {
    id: 0,
    owner: '',
    pet : [{
        petName: '',
        petBreed: ''
    }]
}
export default function PetsForm(props) {
    const {
        values,
        setValues,
        errors,
        setErrors,
        handleInputChange,
        resetForm
    } = useForm(initialFieldValues, true, validate);
 return (
    <Paper>
       <Box>
             <Controls.Input
                   label="Owner"
                   name='owner'
                   placeholder="Owner"
                   value={values.owner = values.owner.toUpperCase()}
                   onChange={handleInputChange}
              />
              <Controls.Input
                   label="PetName"
                   name='petName'
                   placeholder="Pet Name"
                   value={values.petName = values.petName.toUpperCase()}
                   onChange={handleInputChange}
              />
              <Controls.Input
                   label="PetBreed"
                   name='petBreed'
                   placeholder="Pet Breed"
                   value={values.petBreed}
                   onChange={handleInputChange}
              />
        </Box>
   </Paper>
etc...
}

My useForm

export function useForm(initialFieldValues, validateOnChange=false, validate) {

    const [values, setValues] = useState(initialFieldValues);
    const [errors, setErrors] = useState({});

    const handleInputChange = event => {
        const { name, value } = event.target 
        setValues({
            ...values,
            [name]: value,
        })
        if(validateOnChange)
        validate({ 
            [name]: value,
        })
    }

    return {
        values,
        setValues,
        errors,
        setErrors,
        handleInputChange,
    };
}

Upvotes: 0

Views: 1120

Answers (1)

Shyam
Shyam

Reputation: 5497

You are getting the error because your values values.petName and values.petBreed is undefined. Because your shape of values is

const initialFieldValues = {
    id: 0,
    owner: '',
    pet : [{
        petName: '',
        petBreed: ''
    }]
}

So the petname and petBreed has to refered as values.pet.[0].petName and values.pet.[0].petBreed .

Setting the value of nested Object

You can use the lodash set to set the value of deeply nested object . All you need to do is to provide the name as dotted path values . For petname you need to provide the name attribute as pet.[0].petName

Now we can set the values of your value object using lodash set,

 const handleInputChange = event => {
        const { name, value } = event.target 
        // clone the values . you can use lodash cloneDeep for this
        set(clonedValues, name, value);
        setValues(clonedValues); 
        ....
    }

Now you can retrieve the deeply nested values as

<Controls.Input
    label="PetBreed"
    name='pet.[0].petBreed'
    placeholder="Pet Breed"
    value={values.pet.[0].petBreed}
    onChange={handleInputChange}
 />

Upvotes: 1

Related Questions