Use Navigator in React Native + Redux - javascript

I'm new to React Native.
I have a React Native app using the Redux framework. I'm using the Navigator component to handle navigation but this.props.navigator is undefined in component. How can I pass "this.props.navigator" to component?
I'm not able to find any good examples of how to do it correctly so I'm looking for some help and clarification.
index.anroid.js
...
import App from './app/app';
AppRegistry.registerComponent('RnDemo', () => App);
app.js
...
import App from './containers/app';
const logger = createLogger();
const createStoreWithMiddleware = applyMiddleware(thunk, logger)(createStore);
const store = createStoreWithMiddleware(rootReducer);
const rootApp = () => {
return (
<Provider store={store}>
<App />
</Provider>
)
}
export default rootApp;
containers/app.js
...
class App extends Component {
renderScene(route, navigator) {
let Component = route.component
return (
<Component navigator={navigator} route={route} />
)
}
configureScene(route) {
if (route.name && route.name === 'Home') {
return Navigator.SceneConfigs.FadeAndroid
} else {
return Navigator.SceneConfigs.FloatFromBottomAndroid
}
}
_selectPage(page) {
if(page === 'detail') {
this.props.navigator.push({
component: Detail,
name: 'Detail'
}) // **this.props.navigator.push is not a function**
}
}
render() {
const { navigator } = this.props;
console.log(navigator); // **>>>>>> Undefined**
return (
<View style={styles.container}>
<View style={styles.menu}>
<TouchableOpacity onPress={() => this._selectPage('detail')}><View style={styles.menuItem}><Text>Detail</Text></View></TouchableOpacity>
</View>
<View style={styles.content}>
<Navigator
ref='navigator'
style={styles.navigator}
configureScene={this.configureScene}
renderScene={this.renderScene}
initialRoute={{
component: Home,
name: 'Main'
}}
/>
</View>
</View>
)
}
}

renderScene(route, navigator) {
let Component = route.component
return (
// <Component navigator={navigator} route={route} />
<Component {...this.props} navigator={navigator} route={route} />
)
}
Try it

Related

React-Native Router Flux with Netwoking and Sceenchooser

I have a problem.
My App work right now but not like how I want.
App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true,
};
}
componentDidMount() {
fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((json) => {
this.setState({ data: json.movies });
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
render() {
const { data, isLoading } = this.state;
const goToPageTwo = () => Actions.sw({text: 'Hello World!'});
return (
<View style={{ flex: 1, padding: 24 }}>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => { Actions.hp({text: item.id}) }}>
<Text>{item.title}</Text>
</TouchableOpacity>
)}
/>
)}
</View>
);
}
};
index.js / Route.js in my case sm.js
import app from './App';
import sw from './StarWars'
import bttf from './BacktotheFuture'
import hP from './handlePage';
import tM from './theMatrix';
import iN from './Inception';
const SceneManager = () => (
<Router>
<Scene>
<Scene key='home'
component={app}
title='BTour'
initial
/>
<Scene key='sw'
component={sw}
title='star wars'
/>
<Scene key='hp'
component={hP}
title='BTour'
/>
<Scene key='bttf'
component={bttf}
title='Back to the future'
/>
<Scene key='tM'
component={tM}
title='MAtrix'
/>
<Scene key='iN'
component={iN}
title='Inception'
/>
</Scene>
</Router>
)
export default SceneManager;
and my Handlepage.js
import StarWars from './StarWars';
import BTTF from './BacktotheFuture';
import TM from './theMatrix';
import IN from './Inception';
export default class handlePage extends Component {
renderElement() {
if(this.props.text ==1) {
return <StarWars/>
}
if (this.props.text ==2) {
return <BTTF/>
}
if (this.props.text ==3) {
return <TM/>
}
if(this.props.text == 4) {
return <IN/>
}
return null;
}
render(){
return(
this.renderElement()
)
}
};
NOW MY PROBLEM:
I want to use the Scene what I have defined in my Router class. For example when I press the first button in the Home Screen then "Star Wars" will be open but not the Component what I write in the route class.
Can I take the Component Scene from Route.js to the HandlePage.js in the If-Statemant OR can I put the If Statemant in the Router class.
Right now only the script in the IF-Statemant will open.
I don't think defining handlePage as class is necessary, you can just declare a function to navigate to your scene depending on item.id
Handlepage.js
import { Actions } from 'react-native-router-flux';
export const navigateByItemId = (id) => {
switch (id) {
case 1:
Actions.jump('sw');
break;
case 2:
Actions.jump('bttf');
break;
case 3:
Actions.jump('tM');
break;
default:
Actions.jump('iN');
}
};
And in App.js you can call this utility function to decide where to navigate
import { navigateByItemId } from './Handlepage';
...
<TouchableOpacity onPress={() => { navigateByItemId(item.id) }}>
<Text>{item.title}</Text>
</TouchableOpacity>
You can delete handlePage declaration in your router:
<Scene key='hp' //you can delete this code
component={hP}
title='BTour'
/>

createSwitchNavigator with React Navigation v5?

I my old version I make something like this:
going to splash screen, if user is connected go to App, else go to login.
And I can navigate into screen by using this.props.navigation.navigate("Register")
SplashScreen.js :
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
this.props.navigation.navigate(user ? "App" : "Login")
});
}
in App.js
const Container = createAppContainer(
createSwitchNavigator(
{
Splash: SplashScreen,
Login: LoginScreen,
Register: RegisterScreen,
App: AppContainer,
},
{
initialRouteName: "Splash",
}
)
);
//Other code
render(){
return (<Container/>)
}
Now I try to use react Navigation v5 but everything seem to be more complicated.
My App.js look like this :
export default function App() {
const [isDarkTheme, setIsDarkTheme] = React.useState(false);
const theme = isDarkTheme ? CombinedDarkTheme : CombinedDefaultTheme; // Use Light/Dark theme based on a state
function toggleTheme() {
// We will pass this function to Drawer and invoke it on theme switch press
setIsDarkTheme(isDark => !isDark);
}
return (
<PaperProvider theme={theme}>
<NavigationContainer theme={theme}>
<Drawer.Navigator
drawerContent={props => <DrawerContent {...props} toggleTheme={toggleTheme}/>}
>
<Drawer.Screen
name="HomeDrawer"
component={MainTabScreen}
/>
<Drawer.Screen
name="SettingsScreen"
component={SettingsStackScreen}
/>
</Drawer.Navigator>
</NavigationContainer>
</PaperProvider>
);
}
How I'm suppose to do something like this but with PaperProvider ?
You could simply do your API Call and depending on the result, display or not the routes. Something like :
import { useEffect } from 'react';
export default function App() {
const [isDarkTheme, setIsDarkTheme] = React.useState(false);
const [isAuthed, setIsAuthed] = React.useState(false);
useEffect(async () => setIsAuthed(await getIsAuthed()));
const theme = isDarkTheme ? CombinedDarkTheme : CombinedDefaultTheme; // Use Light/Dark theme based on a state
function toggleTheme() {
// We will pass this function to Drawer and invoke it on theme switch press
setIsDarkTheme((isDark) => !isDark);
}
const defineRoutes = () => {
if (!isAuthed) {
return <Drawer.Screen name='Login' component={Login} /> // Don't know your login comp name, but you got the idea
}
// Don'tknow if Fragment exists on react native and is usable here, just try
return (
<Fragment>
<Drawer.Screen name='HomeDrawer' component={MainTabScreen} />
<Drawer.Screen name='SettingsScreen' component={SettingsStackScreen} />
</Fragment>
);
};
return (
<PaperProvider theme={theme}>
<NavigationContainer theme={theme}>
<Drawer.Navigator
drawerContent={(props) => (
<DrawerContent {...props} toggleTheme={toggleTheme} />
)}
>
{defineRoutes()}
</Drawer.Navigator>
</NavigationContainer>
</PaperProvider>
);
}

Display fullscreen image when state is loading using Redux

I have a fullscreen image I would like to display when my app state is loading. How can set this up?
I would like to implement something like this:
if (videos == "") {
return (
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={require("../assets/images/tc-logo.png")}
/>
</View>
);
}
My current app.js looks like this
export default class App extends React.Component {
runInitialAppStartActions = () => {
store.dispatch(fetchData());
};
render() {
return (
<ImageBackground
source={require("./assets/images/TC_background.jpg")}
style={styles.container}
>
<Provider store={store}>
<PersistGate
loading={null}
persistor={persistor}
onBeforeLift={this.runInitialAppStartActions}
>
<AppNavigator
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
</PersistGate>
</Provider>
</ImageBackground>
);
}
}
Sory, i can't write comment, i need 50 reputation for comments.So I'm writing as an answer.
Im new in react, maybe you can solve with componentDidMount() method.
You can look this question: Stop rendering of a component after componentDidMount()
You can simply do it like this. I just assumed that you have a state named videos.
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as videoAction from '../actions/videoAction.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
videos: ''
};
}
async componentDidMount() {
await this.props.videoAction.getVideo();
this.setState({
videos: this.props.videoState.videos
});
}
runInitialAppStartActions = () => {
store.dispatch(fetchData());
};
render() {
const {videos} = this.state;
if (videos == "") {
return (
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={require("../assets/images/tc-logo.png")}
/>
</View>
);
}else{
return (
<ImageBackground
source={require("./assets/images/TC_background.jpg")}
style={styles.container}
>
<Provider store={store}>
<PersistGate
loading={null}
persistor={persistor}
onBeforeLift={this.runInitialAppStartActions}
>
<AppNavigator
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
</PersistGate>
</Provider>
</ImageBackground>
);
}
}
}
const mapStateToProps = (state) => {
return {
videoState: state.videoState
};
};
const mapDispatchToProps = (dispatch) => {
return{
videoAction: bindActionCreators(videoAction, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);

Accessing context in RouteMapper

Is there a way to get this.context in the RouteMapper?
I am able to call this.context.openMenu in Playground.
I have the following:
class App extends Component {
...
renderScene(route, navigator) {
return (
<Playground navigator={navigator} />
)
}
render() {
return (
<Navigator
ref="nav"
renderScene={this._renderScene.bind(this)} />
)
}
}
App.childContextTypes = {
openMenu: React.PropTypes.func
}
This is Playground:
class Playground extends Component {
...
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={Styles.navBar}
routeMapper={PlaygroundMapper} />
} />
)
}
}
Playground.contextTypes = {
openMenu: React.PropTypes.func
}
Then PlaygroundMapper:
export default {
LeftButton(route, navigator, index, navState) {
return (
<TouchableOpacity onPress={() => this.context.openMenu}>
<Text>Menu</Text>
</TouchableOpacity>
)
},
...
}
I've reached this conclusion.
Since I have access to this.context in Playground, I did this:
<Navigator.NavigationBar
style={Styles.navBar}
routeMapper={ArticlesRouteMapper(this.context)} />
} />
Then in the RouteMapper, I did this:
export default (context) => ({
LeftButton(route, navigator, index, navState) {
return <Hamburger _onPress={() => context.openMenu()} />
},
})
Works, but not sure if this is the proper way to do it.

Pass state through Navigator

I'm using the React Native core Navigator component but having trouble figuring out how to pass data between components when pressing buttons in the Navigation Bar. Here is some example code of the setup that I have.
const NavigationBarRouteMapper = {
Title: (route, navigator) => {
let title;
switch (route.component.displayName) {
case 'FirstScreen':
title = 'First Screen';
break;
}
return (
<Text>
{title}
</Text>
)
},
LeftButton: (route, navigator) => {
let onButtonPress, buttonTitle;
switch (route.component.displayName) {
case 'SecondScreen':
buttonTitle = 'Close';
onButtonPress = () => navigator.pop();
break;
}
return (
<TouchableOpacity
onPress={onButtonPress}>
<Text>
{buttonTitle}
</Text>
</TouchableOpacity>
)
},
RightButton: (route, navigator) => {
let onButtonPress, buttonTitle;
switch (route.component.displayName) {
case 'SecondScreen':
buttonTitle = 'Save';
onButtonPress = () => {}; // #TODO Call onButtonPress in SecondScreen component
break;
}
return (
<TouchableOpacity
onPress={onButtonPress}>
<Text>
{buttonTitle}
</Text>
</TouchableOpacity>
);
}
};
const App = React.createClass({
renderScene(route, navigator) {
return <route.component navigator={navigator} {...route.props} />;
},
render() {
return (
<Navigator
style={styles.appContainer}
initialRoute={{component: SplashScreen}}
renderScene={this.renderScene}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
/>
}
/>
);
}
});
const FirstScreen = React.createClass({
onButtonPress() {
this.props.navigator.push({component: SecondScreen});
},
render() {
return (
<View>
<TouchableHighlight onPress={this.onButtonPress}>
<Text>
Click Me
</Text>
</TouchableHighlight>
</View>
);
}
});
const SecondScreen = React.createClass({
getInitialState() {
return {
input: ''
}
},
onButtonPress() {
if (this.state.input.length) {
// Do something with this.state.input such as POST to remote API
this.props.navigator.pop();
}
},
render() {
return (
<View style={styles.container}>
<TextInput
onChangeText={(input) => this.setState({input})}
value={this.state.input}
/>
</View>
);
}
});
You can see from the comments that I have a value stored in the state of SecondScreen that I want to do something with when someone hits the Save button. Any ideas?
In your renderScene function, while returning 'route.component' you can pass props and read them in the component.

Categories