React Navigation passing function as parameter - react-native

so i have lot of screen that have similar function eg:
ForgotPasswordCaptcha
ForgotUserIDCaptcha
RegisterCaptcha
these screens have same layout and functionality except they have different "Next" Button function in ForgotPassword users will be redirected into loginscreen, in ForgotUserID the user will be redirected into page that shows his userID. and in Register it will redirect user to Succes Page.
.
so the function that i create in navigator is handleOkay, and when i press the button in SomeReusableScreen it gives me this warning
Non-serializable values were found in the navigation state. Check:
forgotUserIDStackScreen > FUinputCASAScreen > params.handleOkay (Function)
This can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params.
what i want to achieve is i to pass function as parameter in Stack navigator so the screen will just invoke that function to redirect/doing something with redux,
i dont know i should ignore this warning since in other screens it will handle redux sagas, set asyncstorage

so i have found the way to do what i wanted.
instead of passing the screen as component in Stack.Screen i pass it as child like this
<Stack.Screen
initialParams={{progress: 0.2}}
name={routes.nameofthisroute}
options={({navigation, route}) => ({
headerTitle: props => {
let currentProps = {navigation, route};
return <NavigationHeaderTwo {...currentProps} />;
},
...otherStyle,
})}>
{props => (
<PickMethodScreen handlePickMethod={handlePickMethod} {...props} />
)}
</Stack.Screen>
and i props some function and state from Stack Navigator and its written in this docs here
we can also create react context so component can consume it without having lot of props

I implemented a very simple example to clear the concept. You can pass function as a prop and can dispatch up to the parent component using the same props key. This way you can utilize reusable component.
//consider this your generic Component
function IButton(props){
//receive the method as props
//dispatch the method back to the parent
return (
<button onClick={()=>props.handleClick('hello')}>click me </button>
)
}
const App=()=>{
function handleOnClick(text){
alert(text);
}
return(
<div>
{/*pass the function as Prop*/}
<IButton handleClick={handleOnClick}/>
</div>
)
}
ReactDOM.render( < App / > , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

How can I close a Popover programatically with native base?

I am using Native Base Popover.
scenario
As a user,
I can press on the list,
So that I can select a fruit
My problem is I don't understand how to close the <Popover /> from the outside of the component.
Here is my organization
<Formik>
<Popover>
<FlatList>
<Pressable onPress={() => handlePress(item.id)} /> //Banaba
<Pressable onPress={() => handlePress(item.id)} /> //Potato
<Pressable onPress={() => handlePress(item.id)} /> //Ananas
NativeBase offers a useDisclose() hook for handling opening/closing of modals and other popup windows.
That hook provides an isOpen state (as #mainak's answer mentions) as well as onOpen() and onClose() functions to manipulate that state. You can pass these helpers as arguments as-needed into the props of the same name within the Popover component to handle open/close state.
Optionally, you can in addition pass true or false into useDisclose() to override the starting value of isOpen (defaults to false).
Here is an example below for reference.
import React from "react";
import { Popover, useDisclose } from "native-base";
function MyComponent() {
const { isOpen, onClose, onOpen } = useDisclose()
return (
<>
<Button onPress={onOpen}>Open the Popover</Button>
<Popover isOpen={isOpen} onClose={onClose}>
<Popover.Content>
<Popover.Arrow />
<Popover.CloseButton />
<Popover.Header>My Popover Title</Popover.Header>
<Popover.Body>You can place the content of your popover inside the body.</Popover.Body>
<Popover.Footer>
<Button onPress={onClose} variant="ghost">Cancel</Button>
</Popover.Footer>
</Popover.Content>
</Popover>
</>
)
}
can you try isOpen prop in Popover tag and have it as a state value like
const [isOpen, setOpen] = React.useState(true);
...
<Formik>
<Popover isOpen={isOpen}>
<FlatList>
...

Why useNavigation hook doesn't work in custom side bar?

If I create custom drawer:
<DrawerStack.Navigator
drawerContent={props => <SideMenu {...props} />}>
It requires to pass props in order to get navigation object inside it.
And if I have following:
<ClientsStack.Navigator>
<ClientsStack.Screen name="Clients" component={ClientsScreen} />
<ClientsStack.Screen
name="ClientDetails"
component={ClientDetailsScreen}
/>
</ClientsStack.Navigator>
And inside ClientsScreen I have FlatList which has:
renderItem={({ item }) => <ClientCard id={item.id} />}
inside ClientCard component which is not screen it's just dummy component
I can use useNavigation hook there.
Why it can be used there but not in custom drawer component?
You can't use useNavigation inside your drawerContent cause the hook is only available from your screens and their childs.
At the origin the only way to get navigation was to pass it from your screen to their childs. But since the useContext hook exist, the library provides a useNavigation to easily get the navigation in deep nested components. (and simplify the code a lot)
A drawer custom component is not a screen or wrapped into a screen. So there is just no reason to get the navigation from it with useNavigation. And I easily guess there is no usecase where we have deep nested components inside it cause it is usually just a menu.

How to Create Drawer menu without Navigation?

I am looking to create a drawer, similar to a drawer navigator, but without the routes/navigation requirement.
I plan on placing some other components there that update a query. Any recommendations? Specifically the drawer would be used to display picklists, sliders, and date range components that would update the state and variables used in updating markers rendered on a map shown on the home page.
With Redux
You can use the contentComponent of the createDrawerNavigator to create your own custom drawer and bind it to redux-store.
By dispatching the actions with relevant queries you can update the variables as they are passed from the store to your Component.
Without Redux
You can either create a CustomDrawer component with similar animation and render it in your Component or use this react-native-drawer.
import Drawer from 'react-native-drawer'
class Application extends Component {
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render () {
return (
<Drawer
ref={(ref) => this._drawer = ref}
content={<DrawerContentComponent {...// Your Updater props}/>}
>
<Component />
</Drawer>
)
}
})

Detect what specific page is open?

Pretty simple question.. does anyone have a solution for detecting what component is open in React Native? Treating a page like a component?
My solution right now is to use a global state manager like Redux or Mobx and just constantly update it with whatever component you have open.
In you're renderScene property of your Navigator, you have access to the route object. So you can pass that down into whatever component you need.
<Navigator
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
style={styles.navigator}
initialRoute={{ title: 'Welcome' }}
renderScene={(route, navigator) => {
if (route.title === Welcome) {
return <Welcome navigator={navigator} route={route} />
// Now inside Welcome, if you do this.props.route.title
// you can access the current route name 'Welcome'
}
}}
/>

React Native Navigator renderScene called multiple times

I've been stumped for a while with RN Navigator trying to figure out why Navigator renders all the routes pushed in its stack.
Initially
<Navigator initialRoute={{name:"Route 1", index: 1}} />
Then upon issuing a navigator.push({ name : "Route 2", index: 2 }) the render() method of my component gets called which re-renders Navigator, which in turn calls renderScene.
After pushing the 2nd route and logging the route when renderScene gets called yields to:
Render() --> renderScene(),
{name:"Route 1", index: 1}
Render() --> renderScene(),
{name:"Route 2", index: 2}
Does anyone know why the renderScene() gets called as many times as there are routes inside the Navigator's stack? Is this is expected behaviour and if it is how can we speed up the rendering?
There is a significant performance hit when trying to render scenes of 5 routes before finally rendering the scene for the last pushed route, when in reality it should be calling render() once for rendering only the scene of the last pushed route.
Any help is greatly appreciated.
Thank you!
These are the relevant snippets:
nav.js
export function ListPage(){
return {
name: LIST_PAGE,
index: 1
}
}
Main App
<Navigator
ref={(ref) => this.navigator = navigator = ref}
initialRoute={nav.ListPage()}
renderScene={(route,navigator)=>this.renderListingsScene(route,navigator)}
/>
renderListingsScene(route, navigator){
console.log("renderScene()", route);
}
I had a similar problem (it was calling all routes I had defined at startup).
Once I removed the initialRouteStack from the Navigator Properties it stopped happening.
<Navigator
initialRoute={routes[0]}
//initialRouteStack={routes}
renderScene={ (route, navigator) => this._renderScene(route, navigator) }
/>
renderListingsScene must return a JSX code. You must return a <View> or another component in your renderScene. I think it re-render every scene because you are not providing any component as return value.