'React Native: Why I can not pass a reference to another screen?
I have 3 screens and I can navigate between them. The first one is "Reiew Screen" where the user can put a raiting, write a title and description of their review. The second one (most problematic for me) is the media screen where the user can select multiple photos to support their review. The third screen is "Review screen" where the user can again double check and edit their review (if they need to).
On the review screen I see the photos the user has selected and if I want I can remove some of them. If I want, I also can edit the title and the description of the review.
Editing the photos and keeping the edit consistent with the "Media Screen" is done using refs. I am using ImageBrowser from here so that I can keep consistent state between MediaScreen and ReviewScreen when the user deletes the photos. The problem is when I want to edit the title and the description. If I want to edit title.description I need to naigate from Reiew page to Rate page (because Rate page contains the title and the description). When I press "done" I need to navigate back to Review page where I see the updated title and the updated description as well as the photos which I have selected on the previous steps. When I navigate from Rate page to Review page I have error that the reference of ImageBrowser I have created is null. I tried passing this reference between the screens, as a property but for some reason it seems I am passing undefined.
I am thinking maybe the refs object which I have created have life span and it is expired?
I am posting the pages and how I navigate between them:
Rate Screen
const RateScreen = (props) => {
const { place } = props.route.params;
//imageBrowser is the object which I try to retrieve from the Review page, but it comes as undefined
const { finalObject, imageBrowser } = props.route.params;
console.log("RATE SCREEN IS BEING RERENDERED: ", props.route.params);
const [rating, setRating] = useState(0);
const [title, setTitle] = useState(null);
const [description, setDescription] = useState(null);
// again trying to retrieve the refs
const [gallery, setGallery] = useState(props.route.params.imageBrowser);
function ratingCompleted(value) {
// not relevant
}
function updateTitle(value) {
//not relevant
}
function updateDescription(value) {
//not relevant
}
useEffect(() => {
// way to fetch some value for imageBrowser but it is still null
console.log("useeffect for image browser is being rendered");
if (imageBrowser) {
setGallery(imageBrowser);
}
}, [imageBrowser]);
function navigate() {
if (finalObject) {
//in the case I have edited the object I navigate back to Review Screen
props.navigation.navigate("Review Screen", {
finalObject: {
...finalObject,
rating: rating,
title: title,
description: description,
},
// it comes as null so my code throws an exception
imageBrowser: gallery,
});
} else {
// In the case I have just created a review I am just navigating to the next step which is Add Media screen
props.navigation.navigate("Add Media", {
rateObject: { ...rateObject, place: place },
});
}
}
useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => navigate()
),
});
}, [rating, title, description]);
return (
<View>
<TextInput
value={title}
onChangeText={(value) => updateTitle(value)}
/>
<TextInput
value={description}
onChangeText={(value) => updateDescription(value)}
/>
</View>
);
};
export default RateScreen;
Part of my Add Media Screen where I create the refs, and I pass it to the Review Page:
const GalleryScreen = (props) => {
const { selected, setPhotos, setImageBrowser } = props.route.params;
const [newSelected, setNewSelected] = useState(selected);
//using refs
const imageBrowserRef = useRef(null);
useEffect(() => {
setNewSelected(selected);
}, []);
useEffect(() => {
if (imageBrowserRef) {
setImageBrowser(imageBrowserRef);
}
}, [imageBrowserRef]);
const renderSelectedComponent = (number) => {
return (
//not related
);
};
return (
<View style={styles.viewStyle}>
<ImageBrowser
// creating the ref
ref={imageBrowserRef}
max={10}
callback={(promise) => {
//not related
}}
renderSelectedComponent={renderSelectedComponent}
/>
</View>
);
};
useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => (
<TouchableOpacity
onPress={() => {
props.navigation.navigate("Review Screen", {
finalObject: {
...rateObject,
photos: photos,
},
// passing the refs, which for this step works fine
imageBrowser: imageBrowserRef,
});
}}
>
<AntDesign name="check" size={24} color="white" />
</TouchableOpacity>
</View>
),
});
Finally my "Review Page":
import {
renderTitle,
renderRating,
renderDescription,
renderMedia,
} from "./renderItems";
const ReviewScreen = (props) => {
//at this step imageBrowser is not null and my interactions with the browser are as expected
const { finalObject, imageBrowser } = props.route.params;
const { navigation } = props;
const [editedObject, setEditedObject] = useState(finalObject);
const [rating, setRating] = useState(finalObject.rating);
function ratingCompleted(value) {
//not relevant
}
function removePhoto(item) {
//again as I expect, the removed photo is removed from BOTH screens (Media screen and Review screen)
let index = imageBrowser.current.state.photos.indexOf(item);
let updatedPhotos = editedObject.photos.filter(function (photo) {
return photo != item;
});
imageBrowser.current.selectImage(index);
setEditedObject({
...editedObject,
photos: updatedPhotos,
});
}
useLayoutEffect(() => {
//doesnt matter
),
headerLeft: (props) => (
<HeaderBackButton
{...props}
onPress={() => {
// if I go back to Add Media screen, thanks to refs both screens are consistent
navigation.navigate("Add Media", {
rateObject: editedObject,
});
}}
/>
),
});
}, [editedObject]);
return (
<View>
{renderRating(editedObject, ratingCompleted)}
// with render title I am trying to pass imageBrowser refs as a property but it doesnt work
{renderTitle(
{ ...editedObject, rating: rating },
imageBrowser,
props.navigation
)}
//similar with render description
{renderDescription(
{ ...editedObject, rating: rating },
imageBrowser,
props.navigation
)}
//render media does not navigate to anywhere, it simply displays the photos
{renderMedia(editedObject, removePhoto)}
</View>
);
};
My renderTitle mthod:
export const renderTitle = (finalObject, imageBrowser, navigation) => {
return (
<SafeAreaView style={{ flexDirection: "row", height: 60 }}>
<ScrollView style={{ marginHorizontal: 10, marginTop: 10 }}>
<Text style={styles.titleStyle}>{finalObject.title}</Text>
</ScrollView>
<TouchableOpacity
onPress={() => {
make sure imageBrowser is not null and it is not
console.log("trying to edit title: ", imageBrowser.current.props);
//again on this page I see that imageBrowser is not null, but passing it to the "Rate page" somehow magically it is null
navigation.navigate("Rate Your Visit", {
finalObject: finalObject,
imageBrowser: imageBrowser,
});
}}
>
<MaterialIcons name="edit" size={24} color="#0696d4" />
</TouchableOpacity>
</SafeAreaView>
);
};
Any ideas are welcome
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
