Skip to content

[Suggestion]: Improvements to an example in the document #8141

Description

@jx-xue

Summary

I learned a lot from this example. After understanding it more deeply, I discovered that if a child item is the only child of its parent item, the parent item should also be deleted after deleting it. The original documentation didn't consider this, and there were many details to pay attention to during implementation. I finally completed it, and I'll post my code below.

Page

https://react.dev/learn/choosing-the-state-structure

Details

import {useImmer} from 'use-immer'
import {initialTravelPlan} from './travel.ts'

interface PlaceTreeProps {
id: number
parentId: number
plan: TravelPlan
onComplete: (parentId: number, id: number) => void
}

interface TravelPlan {
[key: number]: {
id: number
title: string
childIds: number[]
}
}

export default function HookDemo() {
const [plan, setPlan] = useImmer(initialTravelPlan)
const root = plan[0]
const planetIds = root.childIds
const handleComplete = (parentId: number, id: number) => {
setPlan(draft => {
if (id===0) return
handleChildrenDelete(id)
handleParentDelete(parentId,id)

        function handleParentDelete(pId: number,cId: number) {
            const parent = draft[pId]
            if (!parent) return
            parent.childIds = parent.childIds.filter((childId) => childId !== cId)
            **// if the parent has no more children, delete it too**
            if (parent.childIds.length === 0 && pId !== 0) {
                const grandParentId = Object.values(draft).find(p => p.childIds.includes(pId))?.id
                if (grandParentId !== undefined) {
                    handleParentDelete(grandParentId, pId)
                }
                delete draft[pId]
            }
        }

        function handleChildrenDelete(childId: number) {
            const child = draft[childId]
            if (!child) return
            child.childIds.forEach((grandChildId) => {
                handleChildrenDelete(grandChildId)
            })
            delete draft[childId]
        }
    })
    console.log(parentId, id)
}
return (
    <div>
        <h2>Place to Visit</h2>
        <ol>
            {planetIds.map((id) => (
                <PlaceTree
                    id={id}
                    key={id}
                    parentId={0}
                    plan={plan}
                    onComplete={handleComplete}
                />
            ))}
        </ol>
    </div>
)

}

function PlaceTree({id, parentId, plan, onComplete}: PlaceTreeProps) {
const place = plan[id]
const childIds = place.childIds
return (


  • {place.title}
    <button type={'button'} onClick={() => onComplete(parentId, id)}>
    Del

    {
    childIds.length > 0 &&

      {
      childIds.map(childId => )
      }

    }

  • )
    }

    Activity

    1. Devashy-patel commented on Apr 23, 2026

      @Devashy-patel

      This is a really interesting extension of the example, and your implementation highlights an important edge case when working with tree-like state.

      That said, I think this behavior falls more into application-specific logic rather than something the documentation necessarily needs to include by default.

      The goal of the “Choosing the State Structure” example in React Documentation seems to be demonstrating how to structure and update normalized state, rather than defining all possible business rules for tree manipulation.

      Automatically deleting a parent when it has no children is a valid design choice, but it depends on the requirements of the application. In some cases, empty nodes might still be meaningful and should be preserved.

      Your recursive approach is a great demonstration of how the chosen state structure makes more advanced operations possible. It might be helpful to frame this as an optional extension or learning exercise rather than a change to the core example.

      Overall, this is a solid exploration of the problem space and shows how flexible the normalized structure can be.

    2. preetpdsilva-netizen commented on Jul 31, 2026

      @preetpdsilva-netizen

      Hey Maxwell, I'd be happy to get this new docs page setup. Is this issue still available?

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions