Hugo Albuquerque
Hugo Albuquerque

Reputation: 1

React (useState): Changing an Object Property from Child Element

Hello guys I'm having hard time on changing a single object property from another from child side.

import ComboData from './ComboData.js'
export default function BasicTabs() {

  const [country, setCountry] = useState({ selectedID: 0, data: [{ id: 1, text: "USA" }, { id: 2, text: "PORTUGAL" }] });

  return(
     <div>
        <ComboData type="text" name={"country"} label={"Country"} selectd={country.selectedID}
                valueData={country.data} idRef={"data.id"} valRef={"data.text"}
                setValOut={setCountry({...country, [country.selectedId]})}
                placeholder={"Select Your Country"}
              />
     </div>
  (

ON THE OTHER SIDE...

function ComboData({ name, label, placeholder, value, idRef, valRef, setValOut}) {
    return (
        <div>
            <label htmlFor={name}>{label}</label>
            <select onChange={(e) => setValOut(e.target.value)} name={name} value={selectd}>
                <option value="0" disabled>{placeholder}</option>
                {valueData ? (valueData.length > 0 ?
                    valueData.map(
                        (item, index) => <option key={item[idRef]} value={item[idRef]} id={item[idRef]}>{item[valRef]}</option>
                    ) : (
                        <option disabled>empty</option>
                    )
                ) : (
                    <option disabled>empty</option>
                )}
            </select>
        </div>
    )
}
export default ComboData;

I expeted that when a option is selected from my ComboData it has to update only the SelectedID of the useState.. LIKE THIS...

FROM:

country{[{ selectedID: 0, data: [{ id: 1, text: "USA" }, { id: 21, text: "PORTUGAL" }]}

TO:

country{[{ selectedID: 1, data: [{ id: 1, text: "USA" }, { id: 2, text: "PORTUGAL" }]}

OR

country{[{ selectedID: 2, data: [{ id: 1, text: "USA" }, { id: 21, text: "PORTUGAL" }]}

ISSUES:

1- Uncaught TypeError: Cannot read properties of undefined (reading 'data')

2- By DELETING the Line valueData={country.data} ===> Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. at renderWithHooks (react-dom.development.js:16187:1)

Upvotes: 0

Views: 269

Answers (1)

Kyomen
Kyomen

Reputation: 92

There are some issues with your code. First, you need to pass the props with the exact same name to your component. For example, you defined:

function ComboData({ /*...*/ value, /*...*/ })

But passed:

<ComboData
  /*...*/
  valueData={country.data} 
  /*...*/
/>

Second, but related to the first, you used valueData inside ComboData, but the prop name is value as shown above.

Third, passing big object props like these can cause a lot of problems, because:

  • Everytime a single part of it changes, it has to update the whole component, not only the child that depends on that specific part of the object prop. In other words, if country.selectedId changes, all options from map updates, because the state have changed.
  • Consequence of the up above, memory usage would go nuts depending on how much this state changes. That's why you 're getting the second error you mentioned.
  • I'm not certain if it applies here, but it may cause bugs due to shallow copies instead of deep copies.

Additionally, I think that first error occurs due to useState "set..." not being instantaneous. You can't use await in it, but it does need some time to change the state. I'd recommend a loading state to make sure it had enough time to update.

However, I'd recommend you to refactor it to something like this instead:

const [selectedCountryId, setSelectedCountryId] = useState(0)
const [countryOptions, setCountryOptions] = useState([{...}, {...}])

And pass those as separated props

An optimization could be done if countryOptions (equivalent to your country.data) never changes:

const countryOptions = [{...}, {...}]

Hope this helps. Good luck!

Upvotes: 0

Related Questions