Note: I am not using Redux (only Context + Hooks)
I have a screen "Profile", that renders a TabView.
This TabView, renders two components as scenes:
My Profile screen, fetches the user data from my database, as it has to render some stuff that depends on this data.
Also, it is passed down via props to my "UserInformation" component, as it just renders the user data. So... there is no data fetching in my "UserInformation" component.
In the other hand, my "UserPosts" component is responsible of fetching the user posts, as it renders a component "UserPostsGrid" which renders those posts with pagination (endless FlatList).
Note: Data fetching is performed in custom hooks, not directly inside the component (also, those hooks manages the respective stateful data).
The main problem comes when I need to refresh the user data and the user posts when the user pull-to-refresh inside the "Profile" screen.
There is no problem for this screen to fetch the user data, as I have said before the "Profile" screen is responsible of that action.
But the posts are the stateful data of my child...
I have thought to solve this problem using the current solution:
React.forwardRef() + React.useImperativeHandle()
With this, I will be able to pass a ref to my "UserPosts" and use imperative programming for accessing the data fetching method:
I have never seen other scenarios for solving this problem like this, but it should work. Instead, other coders implement a ContextProvider to handle all the data and be able to access it wherever they want. The main problem I notice here is: extra memoization + extra re-renders.
Another solution might be to implement all the data fetching inside the parent, and pass the respective callbacks via props to the child. The main problem I notice here is that we are creating some kind of "God Component" and we might have multiple lines of code...
Is it an anti-pattern or a bad practice to implement the first solution ?
Yes, it is an antipattern in React, because React is built around the idea that you specify the state of your app, and then let React render the components depending on this given state.
If something should change, you are supposed to specify a new state that represents the difference to the old state.
Refs should only be used if there is no other option. Basically, if you use Refs you say "I don't want to use React for this specific part of my code".
As always, imperative code using refs should be avoided in most cases
I would approach this kind of problem by trying to translate the imperative action into some state of information.
E.g. you might use a prop
needsUpdate, which is set to
true when the data should be updated, and to
false when fetched.
To avoid unnecessary renders (setting
needsUpdate = false would cause another call of e.g. useEffect, even if nothing happens there), I probably would use a child component prop like
latestUpdateRequest and a child state like
latestUpdate which holds a counter or timestamp, and then compare
if(latestUpdateRequest > latestUpdate).
To inform the parent about the change just pass a callback function like
dataUpdated() as a prop.