Reputation: 5302
I'm new to ReactNative
and React
and there is a thing I don't understand: how to make an already navigated page re-render when a child page changes its state. Let me explain better:
I implemented these components/pages:
ProfilesPage
: a page with a FlatList
with some profiles (name, surname...). Also with a BottomSheet popup here you can create a new profile or delete one.ProfileDetailPage
: when you select a single Profile from the ProfilesPage
, it navigates here. In this page you can edit the profile's name and surname or delete it.In the ProfilesPage
everything works fine: I got the profiles array from my repository implementation and everytime a new profile is added or delete, I just use setState
to rerender the whole page.
The problem comes when I am in the ProfileDetailPage
: if a edit a profile detail name or surname or delete it and navigate back to the ProfilesPage
, the page won't rerender!
My questions are:
What I've tried (related to question 2)
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
console.log('FOCUS');
setRefresh(previousState => !previousState); // To force re-render
setCurrentProfiles(ProfilesRepository.getAllProfiles());
});
return unsubscribe;
}, [props.navigation]);
Redux
or another state management library what I'm searching for? It seems that it has hooks to rerender each page when the state changes.My code:
ProfilesRepository
(Note that I keep profiles array cached, I don't see why I should re get it from the AsyncStorage
everytime)
const initializeAsync = async () => {
if (!profiles) {
const profilesRaw = await AsyncStorage.getItem(profilesKey);
if (profilesRaw) {
profiles = JSON.parse(profilesRaw).map((p) =>
Object.assign(new Profile(), p)
);
} else {
profiles = Array<Profile>();
}
}
};
let profiles: Array<Profile> | null = null;
initializeAsync();
export const getAllProfiles = (): Profile[] => {
return profiles!;
};
export const getProfileById = (profileId: number): Profile | undefined => {
return profiles!.find((p) => p.id === profileId);
};
export const deleteProfileById = async (profileId: number) => {
const profileToDelete = profiles!.find((p) => p.id === profileId);
profiles = profiles!.filter((item) => item !== profileToDelete);
await AsyncStorage.setItem(profilesKey, JSON.stringify(profiles));
};
ProfilesPage
const ProfilesPage = (props) => {
const [currentProfiles, setCurrentProfiles] = React.useState(
// Pass only the function without calling it to use the lazy init overload
// This is because otherwise everytime the bottomPopup does something, it redo getAllProfiles
ProfilesRepository.getAllProfiles
);
const [refresh, setRefresh] = React.useState(false);
// I need this to re-render the profiles list when coming back from a view
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
console.log('FOCUS');
setRefresh(previousState => !previousState); // To force re-render
setCurrentProfiles(ProfilesRepository.getAllProfiles());
});
return unsubscribe;
}, [props.navigation]);
const [currentSelectedProfile, setCurrentSelectedProfile] =
React.useState<Profile>();
const onDeleteSelectedProfileHandler = async () => {
await ProfilesRepository.deleteProfileById(
currentSelectedProfile!.id
);
setCurrentProfiles(ProfilesRepository.getAllProfiles());
};
return (
<View style={styles.screen}>
<FlatList
data={currentProfiles}
keyExtractor={(item, index) => item.id.toString()}
renderItem={renderProfileItem}
/>
// ...
Upvotes: 0
Views: 161
Reputation: 13926
Your problem can be solved simply through redux
. Examples corresponding to this are as follows.
To solve the current problem without using redux
, you must bring the modified profile data.
//Repository.js
export const getAllProfiles = async (): Profile[] => {
await initializeAsync();
return profiles!;
};
//ProfilePage.js
const [currentProfiles, setCurrentProfiles] = React.useState([]);
...
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
ProfilesRepository.getAllProfiles().then(res => setCurrentProfiles(res))
});
return unsubscribe;
}, [props.navigation]);
Upvotes: 1
Reputation: 2877
Instead of using useEffect
, just use useFocusEffect
import { useFocusEffect } from '@react-navigation/native';
useFocusEffect(useCallback(() => {
setCurrentProfiles(ProfilesRepository.getAllProfiles());
}, []));
Upvotes: 1