Reputation: 123
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:
{base_url}/parent/a
in the browser ; the API call is executed, everything works fine/parent/b
; the component doesn't update.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
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