orangeBall
orangeBall

Reputation: 123

React - update or re-render parent component when navigating to another child

I have two components ChildA and ChildB which share a fair amount of UI elements plus the same logic to retrieve data from API before the component is rendered. The common logic and UI elements are externalized in Parent component. The API call is executed from Parent in useEffect hook. The common UI elements are rendered based on the data retrieved by the API call.

Problem:

When I navigate from /parent/a to /parent/b, the API call is not executed so the component doesn't update.

I guess the reason is Parent has already been mounted the first time I entered {base_url}/parent/a in the browser, so it doesn't execute useEffect a second time when I try to navigate to /parent/b. However I have no idea how to solve it. Am I going all wrong here?

Steps to reproduce:

Requirements:

Routing:

<Route path="/parent/a" render={props => <Parent child={ChildA} name="child-a" {...props} /> } exact />
<Route path="/parent/b" render={props => <Parent child={ChildB} name="child-b" {...props} /> } exact />

Child A :

export default function ChildA() {
    return (
        <div>
            <h1>Child A</h1>
            {/*Clicking this link will reproduce the issue*/}
            <Link to="b">Go to Child B</Link>
        </div>
    )    
}

Child B :

export default function ChildB() {    
    return (
        <div>
            <h1>Child B</h1>
            {/*Clicking this link will reproduce the issue*/}
            <Link to="a">Go to Child A</Link>
        </div>
    )    
}

Parent :

interface ParentProps {
    child: any,
    name: string
}

export default function Parent(props: ParentProps) {    
    const Child = props.child

    const [model, setModel] = useState<Model | null>(null)


    useEffect(() => {
        fetchData()
    }, [])


    function fetchData() {
        console.log('EXECUTING API CALL FOR:', props.name)
        // ...API call ommitted for brevity...
    }

    return (
        <div>
            <h1>Parent</h1>               
            {/* ...Display lots of UI depending on Model - omitted for brevity... */}
            <Child/>
        </div>
    )    
}

Upvotes: 1

Views: 1283

Answers (1)

Arun Kumar Mohan
Arun Kumar Mohan

Reputation: 11915

Since you're passing an empty dependencies array, the effect only gets run once. You can add props.name to the dependencies array to make sure fetchData gets called when props.name changes.

useEffect(() => {
  function fetchData() {
    console.log('Executing API call for:', props.name)
    // Fetch data
  }

  fetchData()
}, [props.name])

The fetchData function is added inside the effect to make the effect's dependencies clearly visible. I would recommend reading this for more information. And you can use the exhaustive-deps ESLint rule which is a part of the eslint-plugin-react-hooks package to help you find effects where the dependencies array does not include all the effect's dependencies.

Upvotes: 2

Related Questions