Home > other >  React update array prop from child to parent
React update array prop from child to parent

Time:01-19

Hello I am struggling to properly update my state from my child component to my parent. Basically I am trying to set the current state to true onclick.

This is my parent component:

export default function Layout({ children }: Props) {
  const [navigation, setNavigation] = useState([
    { name: 'Dashboard', href: '/', icon: HomeIcon, current: true },
    { name: 'Create Fact', href: '/facts/create', icon: UsersIcon, current: false },
    { name: 'Documents', href: '/documents', icon: InboxIcon, current: false }
  ])

  return (
    <>
      <Sidebar navigation={navigation} setNavigation={setNavigation} />

This is my child Component (Sidebar)

type Props = {
  navigation: Array<{
    name: string
    href: string
    icon: any
    current: boolean
  }>
  setNavigation: (
    navigation: Array<{
      name: string
      href: string
      icon: any
      current: boolean
    }>
  ) => void
}

const Sidebar = ({navigation, setNavigation}: Props) => {
  const router = useRouter()
  const toggleNavigation = (name: string) => {
    // todo: Here I would like to properly update the state with the current selected navigation item (current)
    const newNavigation = navigation.map(nav => {
      if (nav.name === name) {
          nav.current = true
        return nav
      }
    })
  }

  return (
    <nav className="flex-1 px-2 pb-4 space-y-1">
      {navigation.map(item => (
        <span
          onClick={() => toggleNavigation(item.name)}

CodePudding user response:

There are three problems:

  1. You never call setNavigation with your new array.

  2. You don't clear current on the formerly-current item.

  3. Although you're creating a new array, you're reusing the objects within it, even when you change them, which is against the Do Not Modify State Directly rule.

To fix all three (see *** comments):

const toggleNavigation = (name: string) => {
    const newNavigation = navigation.map(nav => {
        if (nav.name === name) {
            // *** #3 Create a *new* object with the updated state
            nav = {...nav, current: true};
        } else if (nav.current) { // *** #2 make the old current no longer current
            nav = {...nav, current: false};
        }
        return nav;
    });
    // *** #1 Do the call to set the navigation
    setNavigation(newNavigation);
};

Separately, though, I would suggest separating navigation out into two things:

  • The set of navigation objects.
  • The name of the current navigation item.

Then setting the navigation item is just setting a new string, not creating a whole new array with an updated object in it.

CodePudding user response:

T.J. Crowder's solution and explanation are great. Additionally, you can write that logic in a shorter syntax. Just a preference.

const newNavigation = navigation.map(nav => {
  return nav.name === name 
    ? { ...nav, current: true } 
    : { ...nav, current: false }
})
  •  Tags:  
  • Related