React native navigation class function in header - react-native

I've got a problem with react native navigation and nested navigators.
Basically, the nested navigators (tab in a page) work pretty well. But when i add a button in the header with the _saveDetails function, it throw me an undefined function if i'm in the Players tab, and it works well when i'm on the Teams tab
Does anyone have an idea of what am i doing wrong? Thanks.
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerRight: <Button title="Save" onPress={() =>
params.handleSave()} />
};
};
_saveDetails() {
console.log('clicked save');
}
componentDidMount() {
this.props.navigation.setParams({ handleSave: this._saveDetails });
}
render() {
return (
<View />
);
}
}
const MainScreenNavigator = TabNavigator({
Players: { screen: HomeScreen},
Teams: { screen: HomeScreen},
});
const SimpleApp = StackNavigator({
Home: { screen: MainScreenNavigator },
Player: { screen: PlayerPage },
});

Please try this one and let me know if you fetch any other problem. Just convert your code according to below code.
static navigationOptions = {
header: (navigation, header) => ({
...header,
right: (
navigation.navigate('Settings')}>
Settings
)
})
}
Thanks

Just change your code become like this
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
// const { params = {} } = navigation.state; //delete this
return {
headerRight: <Button title="Save" onPress={() =>
navigation.state.params.handleSave()} /> // add navigation.state
};
};
.....

Related

How to pass a string to screens into a BottomTabNavigator

I have a BottomTabNavigator with 4 sreens inside of it, I would like to pass a string into some of them.
How can I do that ? Thank you.
class Main extends Component {
static navigationOptions = {
title: 'Developpe Inclinné',
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#e8142d',
},
};
render() {
const myPath='aRouteForFirebase'
return (
<AppContainer myPath={myPath} />
);
}
}
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session, // needs the props
Resultats // // needs the props
})
const AppContainer = createAppContainer(TabNavigator);
Here is a better implementation:
class Main extends Component {
constructor(props) {
super(props)
this.state = {
myPath: 'aRouteForFirebase',
}
this.AppContainer = createAppContainer(this.TabNavigator)
}
TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: () => <Session myPath={this.state.myPath} />},
Resultats { screen: () => < Resultats myPath={this.state.myPath} />}
})
render() {
const { AppContainer } = this
return (
<AppContainer />
)
}
}
Just for clarification:
class Main extends Component {
static navigationOptions = {
title: 'Developpe Inclinné',
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#e8142d',
},
};
render() {
const myPath='aRouteForFirebase'
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: () => <Session myPath={myPath} />},
Resultats { screen: () => < Resultats myPath={myPath} />}
}
const AppContainer = createAppContainer(TabNavigator);
return (
<AppContainer />
);
}
}
However I don't think this is good looking code, maybe you should think about integrating Redux
I can't find anything in react-navigation documentation which supports this. But I may be wrong. I can suggest one other way of doing it. Create a HOC and wrap your components with it. They all will have access to the common string.
const SomethingCommonHOC = (Component, extraProps ) => {
return class SomeComponent extends React.Component {
render() {
return <Component {...this.props} {...extraProps}/>;
}
};
};
// Use it something like this.
SomethingCommonHOC(Session);
Thanks
Edit: I understand it is difficult to explain actually. That's the reason i am not big fan of HOCs. :). I will give more try to explain you the edit required.
Create a new file:
Put this component definition in it:
const CommonHOC = (Component, extraProps) => {
return class SomeComponent extends React.Component {
render() {
return <Component {...this.props} {...extraProps} />;
}
};
};
export default CommonHOC
Then import it in your component files , in your case DeveloppeInclinne, Chrono, Session, Resultats.
In component where your need to pass common string: Let's say you are editing in session component file
import CommonHOC from the "path to CommonHOC"
export default CommonHOC(Session, {
myString: "myString"
})
Note: you can string like constant dynamic by converting 2nd param a the object and spreading inside the common component
You can do this:
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: props => <Session {...props} />},
Resultats { screen: props => < Resultats {...props} />}
}
// in class Session ...
console.log(this.props.myPath)
=> aRouteForFirebase

Passing Navigator object in renderItem

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

React Navigation - Tab Navigation - Getting different screens from only one class

It is possible to change between tabs without having more then one class?
On my code I have a class that returns multiple components, and I want my TabNavigator to switch between these componentes, not between classes like they have in the React Navigation docs (https://reactnavigation.org/docs/tab-based-navigation.html).
class Monument extends Component{
render(){
const {navigate} = this.props.navigation;
const { data } = this.props.navigation.state.params;
return (
<MonumentMaker category={'Castelos'} navigate={navigate} data={data}/>
<MonumentMaker category={'Museus'} navigate={navigate} data={data}/>
<MonumentMaker category={'Igrejas'} navigate={navigate} data={data}/>
<MonumentMaker category={'Locais de Interesse'} navigate={navigate} data={data}/>
);
}
}
export default TabNavigator({
Convento: {
screen:**first component**
},
Museus: {
screen:**second component**
},
Igrejas: {
screen:**third component**
},
OutrosLocais: {
screen:**forth component**
}
})
It is possible what I want to accomplish?
Thank you for your help!
You can make your TabNavigation as follows
const myTabNavigator = TabNavigator({
Convento: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
Museus: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
Igrejas: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
},
OutrosLocais: {
screen: { screen: props => <Monument {...props} {...myProps} /> }
}
})
Monument.js
const Monument = ({...props}, {...myProps}) => (
<View>
{...// More stuff}
</View>
)

React Navigation changing tab icons on tab navigator dynamically

So I am new to react native and redux. The app is already configured (by someone else) to have react-navigation and redux. Now we're using a TabNavigator (bottom) for our menu and this TabNavigator also contains the Login button. Now what I want to do is when the user logs in, the Login button (with text and icon) becomes Logout.
Is there a way to do that? Also my TabNavigator is in a separate file.
What I want is something like this:
TabNavigator(
{
...other screens,
//show this only if not logged in
Login: {
screen: LoginScreen
},
//show this only if logged in
Logout: {
screen: //There should be no screen here just the logout functionality
}
},
{...options here}
)
Thanks in advance.
You can do it using Redux:
AuthIcon.js:
const LOGGED_IN_IMAGE = require(...)
const LOGGED_OUT_IMAGE = require(...)
class AuthIcon extends React.Component {
render() {
const { loggedIn, focused, tintColor } = this.props
// loggedIn is what tells your app when the user is logged in, you can call it something else, it comes from redux
return (
<View>
<Image source={loggedIn ? LOGGED_IN_IMAGE : LOGGED_OUT_IMAGE} resizeMode='stretch' style={{ tintColor: focused ? tintColor : null, width: 21, height: 21 }} />
</View>
)
}
}
const ConnectedAuthIcon = connect(state => {
const { loggedIn } = state.auth
return { loggedIn }
})(AuthIcon)
export default ConnectedAuthIcon;
then inside your TabNavigator file:
import ConnectedAuthIcon from './AuthIcon.js'
export default TabNavigator({
Auth: {
screen: Auth,
navigationOptions: ({ navigation }) => ({
tabBarLabel: null,
tabBarIcon: ({ tintColor, focused }) => <ConnectedAuthIcon tintColor={tintColor} focused={focused} />,
title: "Auth"
})
}
})
Edit:
In your Auth.js:
class Auth extends React.Component {
render() {
const { loggedIn } = this.props
if (loggedIn) {
return <Profile />
} else {
return <Login />
}
}
}
export default connect(state => {
const { loggedIn } = state.auth
return { loggedIn }
})(Auth)

How to pass redux store in react native between pages?

In react native app I have 2 pages. If I upload a redux store with data on the 2 page, then return to the 1 page - how can I access the store with the uploaded data from the 2 page? So is there a way to access the store with data from all of the pages in react native?
Maybe simoke example or where to read?
Thanks
1page.js
class ScreenHome extends Component{
static navigationOptions = {
title: 'ScreenHome',
};
constructor(props){
super(props)
console.log("PROPS: ",props);
}
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Button
title="Go to load data page"
onPress={() => navigate('New', { name: 'Jane' })}
/>
<Button
title="Get redux data"
onPress={() => {console.log(this.props)}}
/>
</View>
);
}
}
class ScreenRegister extends Component{
static navigationOptions = {
title: 'ScreenRegister',
};
render(){
return <Text>ScreenRegister</Text>
}
}
const MainScreenNavigator = DrawerNavigator({
Recent: {
screen: ScreenHome
},
All: {
screen: ScreenRegister
},
});
export default SimpleApp = StackNavigator({
Home: {
screen: MainScreenNavigator
},
Chat: {
screen: ScreenHome
},
New: {
screen: testScreen
}
});
const mapStateToProps = (state) => {
const {items, isFetching, done} = state.myTestData
return {testScreen:{items, isFetching, done}};
}
const mapDispatchToProps = (dispatch) => {
return {
getNewItems: () => {
dispatch(fetchData());
}
}
}
export default someTest = connect(
mapStateToProps,
mapDispatchToProps
)(SimpleApp)
2page.js
class testScreen extends Component{
static navigationOptions = {
title: 'testScreen.js',
};
_reduxStuff = () => {
this.props.getNewItems();
}
render() {
const { navigate } = this.props.navigation;
const {done, items, isFetching} = this.props.testScreen;
return (
<View>
<Text>Some new screen</Text>
<Button
title="Load Data"
onPress={() => this._reduxStuff()}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const {items, isFetching, done} = state.myTestData
return {testScreen:{items, isFetching, done}};
}
const mapDispatchToProps = (dispatch) => {
return {
getNewItems: () => {
dispatch(fetchData());
}
}
}
export default FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(testScreen)
There should be a container for each page, a store for data you want to access between pages and actions to changing this store. By using mapStateToProps you can pass this store to the container of the page. You can find good example in here.
On your first container you'll need to make your async calls to fill your store.
You can do a dispatch on your componentWillMount() and populate your store with the received data.