I'm a beinner in React-Native and i'm using stackNavigator from react-navigation package to create a Wizard. But i can't pass parameters between different screens of stacks:
class TaskWizard extends React.Component {
constructor(props) {
super(props)
let {service} = this.getNavigationParams()
this.state = {
model : {service.fields}
}
}
getNavigationParams() {
return this.props.navigation.state.params || {}
}
}
const TaskWizardStack = createStackNavigator({
Wizard : {
screen : TaskWizard,
},
})
const PostTaskStack = createStackNavigator({
// some screens here then Task wizard
Deep : {
screen : Deep
}
TaskWizard : {
screen : TaskWizardStack
}
})
export default PostTaskStack
I know i can pass parameters to a screen like this from Deep component :
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('TaskWizard', {
service: {id : 1, fields : [{}]} },
})
/>
</View>
This will pass service parameter from inside of Deep component to TaskWizard Stack. I need to pass it from TaskWizard to every child screens like Wizard. Is it possible?
Try using screenprops to pass the data to other components
https://reactnavigation.org/docs/en/stack-navigator.html#navigator-props
Code below works for me
export const AdminBottomNavigator = createBottomTabNavigator(
{Home: {screen: SchoolDashboard},
Students: {screen: StudentList},
Message: {screen: Dashboard},
Settings: {screen: StudentList},},
defaultNavigationOptions: ({ navigation }) =>
({tabBarIcon: ({ focused, horizontal, tintColor }) =>
{let screenProps=navigation.getScreenProps();}})})
Related
Like so:
I'm running react-navigation v4.
First you have to follow the tutorial on how to set up a react-navigation modal, the one that has a jump animation and doesn't look like the native formSheet. You have to set up a stack navigator with your root navigator as one child and the modal as another:
And it scales, because you can have more than one of these modals as children.
The code for this is the following:
const RootNavigator = createStackNavigator(
{
index: { screen: AppNavigator },
[NC.SCREEN_ROOM_INFO]: { screen: RoomInfoNavigator },
[NC.SCREEN_CHAT_CREATE]: { screen: RoomCreateNavigator },
[NC.SCREEN_CHAT_SEARCH]: { screen: ChatSearchNavigator },
[NC.SCREEN_CHAT_GIF_PICKER]: { screen: GifPickerNavigator }
},
{
mode: 'modal',
headerMode: 'none',
transitionConfig: () => ({
transitionSpec: {
duration: 0
}
}),
transparentCard: true
}
)
Then you need to implement these, from my example, 4 navigators that will be displayed as modals each like so:
// Here you'll specify the screens you'll navigate in this modal, starting from index.
const RoomInfoStack = createStackNavigator({
index: { screen: NavigableRoomInfo },
[NC.SCREEN_ROOM_ROSTER]: { screen: NavigableRoomRoster },
[NC.SCREEN_ROOM_NOTIFICATION_PREFERENCES]: { screen: NavigableRoomNotificationPreferences },
[NC.SCREEN_ROOM_EDIT]: { screen: NavigableRoomEdit }
})
type NavigationComponent<T = any> = {
navigation?: NavigationScreenProp<NavigationState, T>
}
type Props = NavigationComponent
// THIS code is from the react-navigation tutorial on how to make a react-navigation modal:
// https://reactnavigation.org/docs/4.x/custom-navigators/#extending-navigators
class RoomInfoNavigator extends React.Component<Props> {
static router = {
...RoomInfoStack.router,
getStateForAction: (action, lastState) => {
// check for custom actions and return a different navigation state.
return RoomInfoStack.router.getStateForAction(action, lastState)
}
}
constructor(props) {
super(props)
this.onClose = this.onClose.bind(this)
}
onClose() {
this.props.navigation?.goBack()
}
// And here is the trick, you'll render an always open RN formSheet
// and link its dismiss callbacks to the goBack action in react-navigation
// and render your stack as its children, redirecting the navigator var to it in props.
render() {
return (
<Modal
visible={true}
animationType={'slide'}
supportedOrientations={['portrait', 'landscape']}
presentationStyle={'formSheet'}
onRequestClose={() => this.onClose()}
onDismiss={() => this.onClose()}
>
<RoomInfoStack {...this.props} />
</Modal>
)
}
}
export { RoomInfoNavigator }
This export is what our root stack imported before. Then you just need to render the screens, I have a pattern that I do to extract the navigation params to props in case this screen is ever displayed without navigation:
const NavigableRoomInfo = (props: NavigationComponent<RoomInfoProps>) => {
const roomInfo = props.navigation!.state!.params!.roomInfo
const roomInfoFromStore = useRoomFromStore(roomInfo.id)
// Here I update the navigation params so the navigation bar also updates.
useEffect(() => {
props.navigation?.setParams({
roomInfo: roomInfoFromStore
})
}, [roomInfoFromStore])
// You can even specify a different Status bar color in case it's seen through modal view:
return (
<>
<StatusBar barStyle="default" />
<RoomInfo roomInfo={roomInfoFromStore} navigation={props.navigation} />
</>
)
}
Sources:
https://reactnavigation.org/docs/4.x/modal
https://reactnavigation.org/docs/4.x/custom-navigators/#extending-navigators
I'm trying to add a state change event to a nested navigator.
Usually I would define it like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: NextNavigator,
},
})
but I need something like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props)=>{ return <NextNavigator {...props}/> },
},
})
where the screen object is a function.
The problem is I have no idea what signature screen as a function has, and how to pass it to the navigator
I'm getting close with something like this:
screen: (props, state)=>{ return <NextNavigator {...props} state /> },
But it's still not correct.
Does anyone know how this works?
If you don't want #JeffGuKang to answer, you can declare and use global variables and functions.
let NICKNAME = '';
function setNickName(data) {
NICKNAME = data;
}
function getNickName() {
return NICKNAME;
}
export { setNickName,getNickName }
...
//set data
import { setNickName} from './globalpath';
setNickName(data)
....
//get data
import { getNickName} from './globalpath';
getNickName()
Use Params
Use setParams, getParam for changing state in components wrapped by navigatior.
A Component (Set changing value)
const name = this.props.navigation.setParams({ name: this.state.name, age: this.state.age });
B Component (Get value)
const name = this.props.navigation.getParam('name', 'DefaultValue');
const age = this.props.navigation.getParam('age' 0);
setParams is a trigger to run getParam in other components is wrapped navigation.
Extend Navigator
You can extend the component created by Naivgator as below. link
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = MyStack.router;
state = {
a: 'b',
}
render() {
const { navigation } = this.props;
return <MyStack {...this.state, ...this.props} />;
}
}
Or it is able to use functional component instead.
const MyStack = createStackNavigator({ ... });
CustomNavigator = (props) => {
return <MyStack {...props} />
}
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props) => { return <MyStack {...props} /> },
},
Third: CustomNavigator,
})
And do not forget import `React` to use functional component.
I'm struggling to make Navigator object visible in List Component.
Here the code explained: as you can see in RootDrawer, I have Concept component. It simply shows a list of items based on a id passed in param.
Each item points to another Concept with another id, but I get
undefined is not an object (evaluating 'navigation.navigate')
when I press on that <RippleButton> with onPress={() => navigation.navigate('Concept',{id:12}). The problem here I'm not passing the Navigation object correctly. How can I solve?
main Navigator drawer
const RootDrawer = DrawerNavigator(
{
Home: {
screen: StackNavigator({
Home: { screen: Home }
})
},
Search: {
screen: StackNavigator({
Cerca: { screen: Search },
Concept: { screen: Concept },
})
}
}
);
Concept component
export default class Concept extends Component {
loading = true
static navigationOptions = ({ navigation,state }) => ({
headerTitle: "",
title: `${navigation.state.params.title}` || "",
})
constructor(props) {
super(props);
}
async componentDidMount(){
}
render() {
const { params } = this.props.navigation.state;
const id = params ? params.id : null;
const { t, i18n, navigation } = this.props;
const Concept = DB.get(id)
return (
<View>
<ScrollView>
<List data={Concept.array|| []} title={"MyTile"} navigation={navigation} />
</ScrollView>
</View>
);
}
}
List Component
class List extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const { navigation } = this.props;
}
_renderItem = (item,navigation) => (
<RippleButton
id={item.id}
onPress={() => navigation.navigate('Concept', { id: 12, otherParam: 'anything you want here'})}> //this line throws the error
<Text>{item.Term}</Text>
</RippleButton>
)
render() {
const { navigation } = this.props;
return (
<View>
<FlatList
data={this.props.data}
renderItem={({item}) => this._renderItem(item, navigation)}
/>
</View>)
}
}
Instead of passing the navigation prop, you can try using the withNavigation HOC.
Where your List Component is defined:
import { withNavigation } from 'react-navigation`
....
export default withNavigation(List)
Now the List component will have access to the navigation prop
I am programming React Native App. I am using react-navigation for navigating screens.
I have 2 Stack Navigators, they are stayed in a Tab Navigator. I want handle click event when I press tab item on TabBar to refresh its view.
export const HomeStack = StackNavigator({
Home: {screen: ScreenHome},
Detail: {screen: ScreenDetail}
})
export const UserStack = StackNavigator({
User: {screen: ScreenUser}
})
export const Tabbar = TabNavigator({
HomeTab: {
screen: HomeStack,
},
UserTab: {
screen: UserStack,
}
}, {
tabBarComponent: ({ jumpToIndex, ...props}) => (
<TabBarBottom
{...props}
jumpToIndex={(index) => {
if(props.navigation.state.index === index) {
props.navigation.clickButton(); //----> pass props params (code processes)
}
else {
jumpToIndex(index);
}
}
}
/>
),
tabBarPosition: 'bottom'
});
class TabClass extends Component{
constructor(props){
super(props)
this.clickButton = this.clickButton.bind(this)
}
clickButton(){
console.log('click button')
}
render (){
return (
<Tabbar clickButton={()=> this.clickButton()}/>
)
}
}
I want to pass clickButton() function from TabClass Component to the code which processes event click tab bar. When if(props.navigation.state.index === index), I want it will call clickButton(). I try it but it doesn't work.
Is there any way to solve my matter?
I tried onNavigationStateChange(prevState, currentState).
class TabClass extends Component{
constructor(props){
super(props)
this.clickButton = this.clickButton.bind(this)
}
clickButton(){
console.log('click button')
}
_handleNavagationStateChange(prevState, currentState){
console.log('handle navigation state change')
}
render (){
return (
<Tabbar onNavigationStateChange={(prevState, currentState) => {
this._handleNavagationStateChange(prevState, currentState)
}}
clickButton={()=> this.clickButton()}
/>
)
}
}
However, _handleNavagationStateChange(prevState, currentState) only run when navigation state changes (for examples, if I stay at HomeTab, I click User Tab Item, this function will run; if I stay at HomeTab, I click Home Tab Item, this function will not run).
Is there any way to handle click event on tab item.
according to the newest documentation here, you can use navigationOptions: {navigationOptions: () => doWhatever()} to handle tab bar taps.
From the react-navigation 4.x docs, you can override tabBarOnPress within navigationOptions:
tabBarOnPress: ({ navigation, defaultHandler }) => {
defaultHandler(); // Call the default handler to actually switch tabs
// do extra stuff here
},
Please try the code following when customize the event touch of TabBar:
import { TabBarBottom, TabNavigator, StackNavigator } from 'react-navigation';
export const MainScreenNavigator = TabNavigator({
Home: { screen: HomeViewContainer },
Search: { screen: SearchViewContainer },
}, {
tabBarComponent: ({ jumpToIndex, ...props, navigation }) => (
<TabBarBottom
{...props}
jumpToIndex = { tabIndex => {
const currentIndex = navigation.state.index;
if (tabIndex == 1) {
// do some thing. Call Native Live Stream Record
} else {
jumpToIndex(tabIndex);
}
console.log('Select tab: ', tabIndex);
}}
/>),
tabBarPosition: 'bottom',
});
I'm not simply trying to replace the left middle or right components, I want to replace the whole header so that I can use the Toolbar from react-native-material-ui.
You can override a header property in your navigationOptions:
import { HeaderBackButton } from 'react-navigation'
class Foo extends React.Component {
static navigationOptions = (navigationProps) => {
return {
header: (props) => (
<MyCustomHeaderContainer>
<HeaderBackButton
onPress={() => props.navigation.goBack(null)}
tintColor="#fff"
/>
<ContentComponent />
</MyCustomHeaderContainer>
),
};
}
}
This assumes that Foo component is routable, e.g.
StackNavigator({
Foo: {
screen: Foo,
},
});
UPD: You can also specify navigationOptions at the moment you create a navigator. This may be handy in some cases, e.g. nested navigation:
const MainNavigator = TabNavigator({...});
export default StackNavigator({
MainNavigator: {
screen: MainNavigator,
navigationOptions: {
header: navigationProps => <YourHeaderComponent {...navigationProps} />
},
}
}