Passing params across components in ReactNative - react-native

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.

Related

ReactNative UI freezing for a second before rendering a component with a fetch in useEffect()

TL;DR: My UI freezes for .5-1s when I try to render a component that does a API fetch within a useEffect().
I have ComponentX which is a component that fetches data from an API in a useEffect() via a redux dispatch. I'm using RTK to build my redux store.
function ComponentX() {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(fetchListData()); // fetch list data is a redux thunk.
}, [dispatch]);
...
return <FlatList data={data} /> // pseudo code
}
as you can see the fetch will happen everytime the component is rendered.
Now I have ComponentX in App along with another component called ComponentY.
Here's a rudamentary implementation on how my app determines which component to show. Pretend each component has a button that executes the onClick
function App() {
const [componentToRender, setComponentToRender] = useState("x");
if (componentToRender === "x") {
return <ComponentX onClick={() => setComponentToRender("y")}/>
} else {
return <ComponentY onClick={() => setComponentToRender("x")}/>
}
}
Now the issue happens when I try to move from ComponentY to ComponentX. When I click the "back" button on ComponentY the UI will freeze for .5-1s then show ComponentX. Removing the dispatch(fetchListData()); from the useEffect fixes the issue but obviously I can't do that since I need the data from the API.
Another fascinating thing is that I tried wrapping the dispatch in an if statement assuming that it would prevent a data fetch thus resolving the "lag" when shouldReload is false. The UI still froze before rendering ComponentX.
useEffect(() => {
if (shouldReload) { // assume this is false
console.log("reloading");
dispatch(fetchListData());
}
}, [dispatch, shouldReload]);
Any idea what's going on here?
EDIT:
I've done a little more pruning of code trying to simplify things. What I found that removing redux from the equation fixes the issue. By simply doing below, the lag disappears. This leads me to believe it has something to do with Redux/RTK.
const [listData, setListData] = useState([]);
useEffect(() => {
getListData().then(setListData)
}, []);
Sometimes running the code after interactions/animations completed solves the issue.
useEffect(() => {
InteractionManager.runAfterInteractions(() => {
dispatch(fetchListData());
});
}, [dispatch]);

Type error in getting route params within nested navigator using Typescript [react-navigation v6]

Having a navigation type definition as bellow, when I navigate from e.g AOne to BTwo with id:99 the console log of props.route.params shows correct info. But props.route.params.id throws type error
TypeError: undefined is not an object (evaluating 'props.route.params.id')
// navigation related imports in all components
import {BottomTabScreenProps} from '#react-navigation/bottom-tabs';
import {CompositeScreenProps, NavigatorScreenParams} from '#react-navigation/core';
import {StackScreenProps} from '#react-navigation/stack';
// type defenitions
export type StackOneParams = {
AOne:undefined,
BOne: undefined,
// some other screens
};
export type StackTwoParams = {
ATwo: undefined;
BTwo:{id:number};
// some other screens
};
export type TabParams = {
StackOne: NavigatorScreenParams<StackOneParams>;
StackTwo: NavigatorScreenParams<StackTwoParams>;
// ... some other stacks each represent a tab
};
export type RootParamList = {
ROne: undefined; // these screens should stand alone and not blong to any tab
RTwo: undefined;
Tabs: NavigatorScreenParams<TabParams>
}
// navigation from AOne to BTwo
props.navigation.navigate('Tabs', {
screen: 'StackTwo',
params: {screen: 'BTwo', params: {id: 99}}
}); // this part give correct auto complete hints in VSCode and no compilation error
// BTwo component (screen)
//--------------------------------
type Props = CompositeScreenProps<
StackScreenProps<RootParamList, 'Tabs'>,
CompositeScreenProps<
BottomTabScreenProps<TabPrams, 'StackTwo'>,
StackScreenProps<StackTwoParams, 'BTwo'>
>
>;// using CompositeScreenProps to be able to navigate to screens in another tabs
// otherwise just `type Props=StackScreenProps<StackTwoParams, 'BTwo'>` works fine but cannot navigate to other tabs
const BTwo:React.FC<Props> = (props) =>{
console.log(props.route.params)// the log shows {id:23}
// but props.route.params.id gives error and also no auto completion hint
return(...)
}
is this the correct way to define screen props for a specific screen, like what I have in BTwo screen? or the sequence of the composition should be different?
most of the examples (and the official documentation) show the most simple composition where the target screen is not even in second level of nesting (in the official doc profile is not really in nested bottom tabs)
How should I solve the type error in this case?
the image shows the VSCode auto-complete suggestions
Solution using CompositeScreenProps
My other explanations were not quite accurate. The way you have defined the CompositeScreenProp is not correct. Here is the correct way to implement this.
type ScreenProps = CompositeScreenProps<
StackScreenProps<StackTwoParams, "BTwo">,
CompositeScreenProps<
BottomTabScreenProps<TabParams, "StackTwo">,
StackScreenProps<RootParamList>
>
>
The first parameter of CompositeScreenProps contains the type of the navigator that owns the screen. In this case BTwo is owned by StackTwo and this determines the primary navigator, which is a Stack.
The above yields to the correct types as well.
const BTwo = (props: ScreenProps) => {
return <></>
}
Solution using separate types for navigation and route
We can type the navigation object and the route object separately as follows.
type NavigationProps = CompositeNavigationProp<
StackNavigationProp<StackTwoParams, "BTwo">,
CompositeNavigationProp<
BottomTabNavigationProp<TabParams, "StackTwo">,
StackNavigationProp<RootParamList>
>
>
type ScreenPropsA = {
navigation: NavigationProps
route: RouteProp<StackTwoParams, "BTwo">
}
Notice the usage of CompositeNavigationProp and RouteProp here.
Then, use it as follows.
const BTwo = ({ route, navigation }: ScreenProps) => {
return <></>
}
Both, route and navigation are now correctly typed.

React Native Navigation 4 - Screen doesn't get parameters

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!

React Native Multiselect

I am new to React Native
I am trying to create a multiselect view where user can select and deselect the items and then the selected items should pass back to the previous container and when user comes back to the next view the selected items should be checked.
I am trying to implement but getting the issue it is not updating data accurately. It shows only 1 selected item when I came back again to the screen.
Can anyone tell me the best way to do that or if there is any tutorial.
Should I do it with Redux or using react native?
Any help would be appreciated!!
Thanks!!
I believe the issue you describe is due to the following:
In componentDidMount you are calling updateItemWithSelected in a loop. This updateItemWithSelected call is both overwriting the checked attributes for all of the arrayHolder values on each call and also not using the updater function version of setState, so the later call of the loop may overwrite the earlier calls since setState is async and batched. If you are not using updateItemWithSelected elsewhere you should simplify componentDidMount to:
componentDidMount() {
const selectedTitles = {};
const { state } = this.props.navigation
const params = state.params || {};
if (params.data.length){
params.data.forEach((element) => {
// create a map of selected titles
selectedTitles[element.title] = true;
})
}
const arrayHolder = this.array.map(item => {
// map over `this.array` and set `checked` if title is in `selectedTitles`
return {...item, checked: !!selectedTitles[item.title]};
});
this.setState({ arrayHolder });
}
and delete updateItemWithSelected.

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.