setParams not working in react-native - reactjs

I am using react-native with redux. I am trying to update current screen's params so that they can be accessed in a component used in top-bar but parameter is not getting set.
My code is following:
Screen Route:
AlertNameForm: {
screen: AlertNameForm,
navigationOptions: ({navigation}) => CancelAndDone(navigation)
}
Component Screen: In componentDidMount I am setting parameter.
class AlertNameForm {
..........
componentDidMount() {
this.props.navigation.setParams({onDonePress: this.onDonePress})
}
onDonePress: () => {
// want to access this function in top-bar buttons.
}
}
Following is further components:
export const CancelAndDone = (navigation) => ({
headerLeft: <ButtonCancel navigation={navigation} />,
headerRight: <ButtonDone navigation={navigation} />
})
const ButtonDone = withTheme(({navigation, theme: { tertiaryColor } }) => (
<Button color={tertiaryColor} title="Done" onPress={() => {
if (navigation.state.params && navigation.state.params.onDonePress) {
navigation.state.params.onDonePress()
}
else {
navigation.dispatch(NavigationActions.back())
}
}} />
))
But in ButtonDone component I am not able to access function onDonePress
Is there any other way to setParams for current screen in react-native.

You should reference navigation.state.paramsusing this.props since navigation should be passed as a prop to that component.

You can assign the function within the target component as follows:
componentDidMount = () => {
const { navigation } = this.props
navigation.setParams({
onDonePress: () => this.myFunction(),
})
}
myFunction = () => { /*body function*/ }
In your header or footer component call:
navigation.state.params.onDonePress or route.params.onDonePress if you using React Navigation v5.

Related

Not able to navigate to another tab in BottomTabNavigator

I have BottomTabNavigator with 4 tabs I have the structure as in the screenshot below. That is the View below the BottomTabBar which is achieved but, the problem is I am not able to navigate from the Home/search tab to any other tab. Also, I tried with passing navigation in <Appcontainer /> as given below in the code but it is also not working.
I am using react-navigation v3.11.2
Is there any other way to pass navigation prop in <Appcontainer />. Or Any other method so I can able to navigate in BootomTabs.
const Tabs = createBottomTabNavigator(
{
Home: {
screen: Home,
},
Search: {
screen: Search,
},
Add: {
screen: () => null,
navigationOptions: () => ({
tabBarOnPress: async ({ navigation }) => {
navigation.navigate('Upload');
}
}),
},
Profile: {
screen: Profile,
},
},
);
export default class ParentTabs extends React.Component {
render() {
const { navigate } = this.props;
return (
<View>
<AppContainer navigate={navigate} />
<View>
<Text>My Text</Text>
</View>
</View>
);
}
}
const AppContainer = createAppContainer(Tabs);
navigation.navigate('Upload');
You cannot navigate to any random component. Upload should be a route name defined in your tab navigator.
Otherwise, you need to trigger Upload logic inside your Add screen

How to call a function from react-navigation header?

I am actually using react navigation https://reactnavigation.org/, I have a Component with a method:
class Sing extends Component {
singASong = () => {
console.log('hello i am singing');
}
}
this component will be rendered with react-navigation and a custom header, the question is: how to call the method singASong from the custom header?:
const routeStackNavigation = {
SingingMode: {
screen: Sing,
navigationOptions: ({ navigation }) => ({
header: (
<CustomNav onPressAction={() =>{
// how to call the method singASong from here ?
}
}
/>
),
}),
},
};
UPDATE
I set and test the value with this:
this.props.navigation.setParams({'onPress': 'akumen' });
setTimeout(() => {
console.log(this.props.navigation.getParam('onPress')); // akumen
}, 2000);
And i tests values with:
onPress={() =>{
console.log('NAVIGATION',navigation);
console.log('ON PRESS',navigation.getParam('onPress'));
console.log('PARAMS',navigation.state.params);
return navigation.getParam('onPress');
}
}
but get undefined
FOR STACK NAVIGATION
Below I share pic with my real world example, it was a stack navigation and I did not notice before, but param it just set under the right stack, so take this as motivation to explore:
You can use navigation params:
In componentDidMount
this.props.navigation.setParams({onPressAction: ()=>this.singASong()})
In navigationOptions
<CustomNav onPressAction={navigation.getParam("onPressAction") }/>

React navigation header right button

I want add button in react-native header , the button is to mas and unmask password in the page, the problem on click when i change the state to change secureTextEntry value, the icon wont change will keep as the initial value;
the function is working fine but the icon cant change
this.state.secureTextEntry ? "eye" : "eye-slash"
this is the main code
class ChangePasswordScreen extends Component {
constructor(props) {
super(props);
this.state = {
newPassword: null,
currentPassword: null,
confirmPassword: null,
errors: [],
secureTextEntry: true
};
this.maskPassword = this.maskPassword.bind(this)
}
componentDidMount() {
this.props.navigation.setParams({
headerRight: ( < TouchableOpacity onPress = {
() => {
this.maskPassword();
}
} > < Icon style = {
styles.eyeIcon
}
name = {
this.state.secureTextEntry ? "eye" : "eye-slash"
}
size = {
20
}
color = {
Colors.WHITE
}
/></TouchableOpacity > )
})
}
static navigationOptions = ({
navigation
}) => {
return {
// headerTitle: <LogoTitle />,
headerRight: navigation.state.params && navigation.state.params.headerRight,
};
};
maskPassword = () => {
this.setState({
secureTextEntry: !this.state.secureTextEntry
})
}
}
Kinda late, might help someone nevertheless.
If you wish to add a button to the header of a screen, from the screen itself, not the App.js file and you are using a functional component, it goes like this:
import { useNavigation } from '#react-navigation/native'
export default function () {
const nav = useNavigation();
useEffect(() => {
nav.setOptions({
headerRight: () => <Button />,
});
}
}
The problem is this.setState will not re-render header component . if you want to change header right then you have to call setParams again
Try this code in componentDidMount
componentDidMount() {
this.props.navigation.setParams({
headerRight: this.setHeaderRight(this.state.secureTextEntry)
});
}
Set function for header right
setHeaderRight = state => {
//console.log("setHeaderRight", this.state.secureTextEntry);
return (
<TouchableOpacity
onPress={() => {
this.maskPassword();
}}
>
<Icon
style={styles.eyeIcon}
name={state ? "eye" : "eye-slash"}
size={20}
color={Colors.WHITE}
/>
</TouchableOpacity>
);
};
Set header right again when state set
maskPassword = () => {
this.setState({
secureTextEntry: !this.state.secureTextEntry
});
this.props.navigation.setParams({
headerRight: this.setHeaderRight(!this.state.secureTextEntry)
});
};
You are setting a Component as a navigation param on Component mount and passing in a state value at the time the Component mounted.
This param never gets changed or updated again so the navigation header never gets re rendered.
A better way would be to pass the value of state directly as a navigation param and use that in the component that is used directly in the navigationOptions

How do I get state or props into TabNavigator

I'd like to get user image into the tabBarIcon just like Instagram does it. I can't figure out the way to do it.
I tried getting the state but on the init of the app the state is empty.
I've tried like
const store = store.getState().user
but it's undefined on app init
I have MainTabNavigator.js
ProfileStack.navigationOptions = {
tabBarLabel: () => {
return null
},
tabBarIcon: ({focused}) => (
<Image source={{uri: ???}}/>
)
}
const TabNavigator = createBottomTabNavigator({
HomeStack,
SearchStack,
DashboardStack,
ProfileStack,
});
TabNavigator.path = '';
export default TabNavigator;
I can't get the props or state since this isn't a class
The solution was quite simple.... I don't know why I didn't think of it earlier.
The trick was to create a component that would get the state from redux and when the state changes after the init, the state would finally have the Object and there the image path
ProfileStack.navigationOptions = {
tabBarLabel: () => {
return null
},
tabBarIcon: ({focused}) => (
<BottomTabImage focused={focused} />
)
}
See i changed it to BottomTabImage which is just a component
And this is the component file
BottomTabImage.js
import {connect} from 'react-redux';
class BottomTabImage extends Component {
render() {
const uri = this.props.auth.image !== undefined ?`http://localhost/storage/creator_images/${this.props.auth.image)}`: '';
return <Image style={styles.profileImage} source={{uri}} />
}
}
const styles = StyleSheet.create({
profileImage: {
...
}
});
function mapStateToProps(state) {
return {
auth: state.user.auth
}
}
export default connect(mapStateToProps, {})(BottomTabImage)

How do you navigate to another component that does not receive the props of react navigation?

I'm working with React Native and React Navigation.
I have a component called App.js in which I declare the Drawer Navigation of React-Navigation.
In this I have an option to log out but I can not navigate to another component after removing the AsyncStorage
Does anyone know how to achieve it?
Thank you.
This is my code:
App.js
import { createDrawerNavigator, DrawerItems, NavigationActions } from 'react-navigation';
const customDrawerComponent = (props) => (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<DrawerItems
{...props}
/>
<TouchableOpacity style={styles.button} onPress={this.logOut} >
<Text> Logout </Text>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
logOut = () => {
// NOT WORKS
// this.props.navigation.navigate('Login')
//NOT WORKS:
this.myAction();
}
myAction = () => {
const nav = NavigationActions.navigate({
routeName: 'App',
});
return nav;
};
const AppDrawNavigator = createDrawerNavigator(
{
MainComponent: { screen: MainComponent,
navigationOptions: ({navigation}) => ({
drawerLockMode: 'locked-closed'
}) },
Login: { screen: LoginComponent,
navigationOptions: ({navigation}) => ({
drawerLockMode: 'locked-closed'
}) },
User: { screen: UsersComponent }
},
{
contentComponent: customDrawerComponent,
}
);
make this as a class like
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
From your question I understand that either you want to :-
navigate from outside the components
navigate from components which do not have navigation prop.
For this I have tried 2 solutions and both work extremely fine though I based towards the second one.
First Solution
Use withNavigation from react-navigation package. If your components are deeply nested they wont have navigation prop unless u specify them manually or put them in context ;passing navigation prop becomes a real pain. So instead use withNavigation and your component would have navigation prop.
import {withNavigation} from "react-navigation";
const Component = ({navigation}) => {
const onPress = () => {
navigation.navigate(//ROUTE_NAME//)
}
return (
<TouchableOpacity onPress={onPress}>
<Text>Navigate</Text>
</TouchableOpacity>
)
}
export default withNavigation(Component);
Second Solution
Create a helper script and use that.
"use strict";
import React from "react";
import {NavigationActions} from "react-navigation";
let _container; // eslint-disable-line
export const navigation = {
mapProps: (SomeComponent) => {
return class extends React.Component {
static navigationOptions = SomeComponent.navigationOptions; // better use hoist-non-react-statics
render () {
const {navigation: {state: {params}}} = this.props;
return <SomeComponent {...params} {...this.props} />;
}
}
},
setContainer: (container) => {
_container = container;
},
reset: (routeName, params) => {
_container.dispatch(
NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName,
params
})
]
})
);
},
goBack: () => {
_container.dispatch(NavigationActions.back());
},
navigate: (routeName, params) => {
_container.dispatch(
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName,
params
})
);
},
navigateDeep: (actions) => {
_container.dispatch(
actions.reduceRight(
(prevAction, action) =>
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName: action.routeName,
params: action.params,
action: prevAction
}),
undefined
)
);
},
getCurrentRoute: () => {
if (!_container || !_container.state.nav) {
return null;
}
return _container.state.nav.routes[_container.state.nav.index] || null;
}
};
In your parent component when you mount the navigation call following:-
"use strict";
import React from "react";
import App from "./routes";
import {navigation} from "utils";
class Setup extends React.Component {
render () {
return (
<App
ref={navigatorRef => {
navigation.setContainer(navigatorRef);
}}
/>
);
}
}
export default App;
Now, in your components you can directly use helpers from this script itself and navigation would be accessibly globally now.
import {navigate} from "utils/navigation";
For more details you can this thread
Your logout function is declared outside of the Navigator. This means you don't have access to the navigation prop there. However, your customDrawerComponent is a screen of your Navigator and it should have access to it.
So you can try something like this (props here are the props passed to the customDrawerComponent):
onPress={()=> {props.navigation.navigate("Login")}}
Plus your App.js seems kind of strange since you're not exporting any component. Have you pasted the whole code of App.js or just parts of it?

Resources