React Native Navigation 4 - Screen doesn't get parameters - react-native

Pretty strange thing cause this function is all over my app but one screen that doesn't get the paramenter I'm passing from the previous one.
1° screen CountdownScreen.js
const sessionEnded = () => {
endSound.play();
props.navigation.navigate('Daily');
props.navigation.setParams({time: meditationTime});
console.log('passing?', meditationTime);
};
(first was props.navigation.navigate('Daily', {time: meditationTime}) but didn't work as well )
Daily (the screen that receives, where I move to correctly)
const JournalDailyScreen = props => {
const meditationTime = props.navigation.getParam('time');
console.log('meditation time', meditationTime);
... and so on
I always get undefined
Any idea where I mistake?
Thanks!

Related

Trigger UseEffect whenever i switch tabs in React Native Tab Navigator

I have implemented react native tab navigator and added 4 screens to it.
I post some record to api in the second screen and i want to have the updated record in the 4th screens where i am getting updated records..
Useeffect only gets targeted only once, and when i put something in it's argument it gives me strange behavior.
I want useeffect to reload and call the api to get latest items in the 4th screen without putting anything in it's arguement(empty argument)
Any help would be highly appreciated.
Try doing this ;
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
makeApiCall();
});
return unsubscribe;
}, [navigation]);
Get navigation in component's arguments(destructuring)
like below;
const My4thTab = ({ navigation }) => {
}
This way useEffect will trigger only once, every time you come on this screen
but make sure to clear the previous state where you store your data, otherwise, there could be a record duplication.
Hope it helps :)

React Native: Didn't rerender the component after data was updated

i have two screens one where the profile information is showing and another screen to edit the information. If i entered the first screen profile it's shows me the right data from the database. Then i move to the next screen where i can change the Information everthing worked so far. But if I go back to the previous screen i still see the old data. So there is no rerendering.
But if i navigate to the other screen that screen fetched the new data. and the call getCurrentUserProfile is executed
This ist the screen with the profile information about the user.
const ProfileScreen = props => {
const [userObj, setUserObj] = useState({});
useEffect(() => {
let mounted = true;
getCurrentUserProfile().then(user => {
if (mounted) {
setUserObj(user);
}
console.log(user)
});
return () => mounted = false;
}, []);
console.log("----b------") // This is only output on the first call
}
How can i fix this. Is there a way when in the database is something changed, so the component rerender and fetches the new data.
Thanks.
You are probably using #react-navigation package. So, if you want to refetch data when come back to the previous page, you can use https://reactnavigation.org/docs/use-is-focused on that “previous” page.
Just look at their docs, you will get the idea.
P.S: Usually we don't unmount components directly. In React Native, we use navigator to mount/unmount components

Passing params across components in ReactNative

In my ReactNative application, I'm trying to pass some data across components using "route" in Navigation stack. I'm sending as in below code:
navigation.navigate('OtpScreen', { otpKey: '1234' });
I'm capturing in OtpScreen as below:
const OTPScreen = ({ navigation, route }) => {
const { otpKey }= route.params;
}
Its throwing undefined 'params' when this screen is presented.
I've gone through sample code available in Snack
What could be the problem with my code?
Below your render type this
const param = route.params.otpKey;
I hope that helps!
I noticed that in the recent version of ReactNative, I've to use below line of code to fetch the params from navigation.
const otpKey = navigation.getParam('otpKey', '');
I think, we can't use "parmas" any more.

Pass variables via `this.props.navigation` multiple times

So, for begging, in react-native-navigation there's a possibility to pass some data via this.props.navigation.navigate().
Here's how you should pass the data :
this.props.navigation.navigate('RouteName', {/*Data to pass*/})
And so, moving to the problem
The case where this problem was encountered :
I have a list of items which I click on and I navigate to the next screen, the data of the pressed item being sent during the navigation process, and when I get to the next screen, the passed data is assigned to state, and I further operate with it. Here are the commands which I use for passing data:
Pass data
this.props.navigation.navigate('Screen2',{param1: value1, param2: value2})
Receive data
ComponentWillMount = () => {
const param1 = this.props.navigation.getParam('param1');
const param2 = this.props.navigation.getParam('param2');
this.setState({param1, param2)}
}
The Problem itself
My Problem is that if I go back to the first screen, and press on another item, then it's data isn't passed via this.props.navigation.navigate(), the data on the second screen remains unmodified from the first navigation process. How this problem can be resolved?
I think i figured it out,
I was able to replicate the issue using drawerNavigator and tabbed navigator in the react-navigation 3.0.5.
Basically they save the components even when you run navigation.goBack.
The screen isn't being mounted again so it doesnt call componentWillMount() and it doesn't check for data there.
there are 2 (edit 3) ways to fix this.
one is to turn off this performance enhancement
const MyApp = createDrawerNavigator(
{
Screen1: Screen1,
Screen2: Screen2
},
{
unmountInactiveRoutes: true
}
);
The second option and the more elegant one is to subscribe to navigation events
componentWillMount() {
console.log("mounting");
const willFocusSubscription = this.props.navigation.addListener(
"willFocus",
() => {
console.debug("willFocus");
const thing = this.props.navigation.getParam("thing");
const thing2 = this.props.navigation.getParam("thing2");
this.setState({thing, thing2});
}
);
}
Just dont forget to unsubscribe in componentWillUnmount
componentWillUnmount() {
willFocusSubscription.remove();
}
The third way is basically the same as the second but subscribing declaratively. This means no componentWillMount or WillUnmount.
First a callback to set the state appropriately
willFocus = ({action}) => {
console.debug("willFocus", action);
const thing = action.params["thing"];
const thing2 = action.params["thing2"];
this.setState({thing, thing2});
};
now in render add the component
render() {
console.log("data is:", this.state.thing);
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={this.willFocus}
/>
.... rest of render body
</View>
);
}
This doesn't display anything but it takes care of subscribing and unsubscribing.

react native transition animation for navigator.replace()

I'm working on a navigation that there's no more than one route in the stack.
So I'm using replace() instead of the pop() and push(). But the problem I noticed is that while using replace(), there isn't a transition animation available such as the one I see with pop() and push().
Is there a way to get around that? Or is there other ways to implement a navigation with one view replacing an existing one (with transition animation of course)?
I was having the same issue and managed to find the solution here provided by zhaotai.
To solve the issue, define the following new method in the js file where you import the Navigator component.
Navigator.prototype.replaceWithAnimation = function (route) {
const activeLength = this.state.presentedIndex + 1;
const activeStack = this.state.routeStack.slice(0, activeLength);
const activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
const nextStack = activeStack.concat([route]);
const destIndex = nextStack.length - 1;
const nextSceneConfig = this.props.configureScene(route, nextStack);
const nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]);
const replacedStack = activeStack.slice(0, activeLength - 1).concat([route]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity, null, () => {
this.immediatelyResetRouteStack(replacedStack);
});
});
};
Then, you can call navigator.replaceWithAnimation(route) instead.