Redux action fired but it doesn't navigate (React-Navigation) - react-native

I just started a new project and I'm currently integrating Redux-Saga with React-Navigation. Here is the versions I use :
"react": "16.6.1",
"react-native": "0.57.5",
"react-navigation": "^1.5.11",
"react-navigation-redux-helpers": "^1.0.5",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-saga": "^1.0.1",
"reduxsauce": "^1.0.1",
Here is my Main file and the Navigator file :
export default class Root extends React.Component {
props: Props;
renderApp() {
const store = createStore(rootReducer, applyMiddleware(logger, middleware));
return (
<Provider store={store}>
<Navigator/>
</Provider>
);
}
render() {
return this.renderApp();
}
}
AppRegistry.registerComponent('Root', () => Root);
/*************************/
export const Navigator = new StackNavigator({
Main: {
screen: MainScreen
},
Tutorial:{
screen: TutorialScreen
}
},
{
initialRouteName: 'Main',
mode:'modal'
});
export const middleware = createReactNavigationReduxMiddleware(
"root",
state => state.navigation,
);
const addListener = createReduxBoundAddListener("root");
class Nav extends React.Component {
render() {
return (
<Navigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.navigation,
addListener
})} />
)
}
}
const mapStateToProps = (navigation) => ({
navigation,
});
const mapDispatchToProps = dispatch => ({
dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(Nav)
The Actions is correctly called when I assigned a Navigate Actions navigate: (routeName, params = {}) => dispatch(Actions.navigate(routeName, params)) on my main file for a Button.
But the View does'nt "navigate" and stay stuck on the first View. It looks like that redux is correctly set then, but the Navigation actions not well managed. But I'm not able to find what is the exact reason of this problem. Any ideas from where it could come from ?
My container file :
type Props = {
user?: Object,
navigate: (routeName: string, params?: Object) => void,
};
class MainScreen extends React.Component {
render() {
const {user, navigate}= this.props;
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Button title="mainScreen" onPress={()=> navigate("Tutorial",{})} >
</Button>
</View>
);
}
}
const mapDispatchToProps = dispatch => ({
navigate: (routeName, params = {}) => dispatch(Actions.navigate(routeName, params))
});
const mapStateToProps = (state) => {
const { user } = state
return { user }
};
export default connect(mapStateToProps, mapDispatchToProps)(MainScreen);

You are recreating your store each time the component is rendered.
const store = createStore(rootReducer, applyMiddleware(logger, middleware));
export default class Root extends React.Component {
props: Props;
renderApp() {
return (
<Provider store={store}>
<Navigator/>
</Provider>
);
}
render() {
return this.renderApp();
}
}

Related

Passing props to tabNavigator's screens

In my app, I want to pass some data in my createMaterialTopTabNavigator (const NewTab) to both it's child screens (Graphical and Tabular). The data in child screens changes on the basis of the dropdown value in the custom header that will be above createMaterialTopTabNavigator. As soon as the value from the dropdown is selected, it will trigger a fetch request in both the Graphical and Tabular Screen.
Here is my App.js where I am routing all my screens.
const NewTab = createMaterialTopTabNavigator({
Graphical: Graphical,
Tabular: Tabular
});
const DailyStack = createStackNavigator({
Dashboard,
Daily,
Login,
SalesDashboard,
About : {
screen: NewTab,
navigationOptions : {
header : <CustomHeader />
}
}
})
const MonthlyStack = createStackNavigator({
Monthly: Monthly
})
const RangeStack = createStackNavigator({
Range: Range
})
export const BottomTabNavigation = createBottomTabNavigator({
Daily: {
screen: DailyStack
},
Monthly: {
screen: MonthlyStack
},
Range: {
screen: RangeStack
}
})
const DashboardStackNavigator = createStackNavigator({
BottomTabNavigation:BottomTabNavigation,
}, {
headerMode: 'none'
})
export const AppDrawerNavigator = createDrawerNavigator({
DashboardStackNavigator,
Login: {
screen: Login
},
Logout: {
screen: Logout
}
})
const OpenNav = createSwitchNavigator({
Splash: {screen: SplashScreen},
Login: { screen: Login },
Home: { screen: Home }
})
const AppNavigator = createStackNavigator({
OpenNav,
AppDrawerNavigator: { screen: AppDrawerNavigator },
ClaimsDashboard: ClaimsDashboard
},{
headerMode:'none'
});
class App extends Component {
constructor(){
super();
console.disableYellowBox = true;
}
render() {
return (
<AppContainer />
);
}
};
const AppContainer = createAppContainer(AppNavigator);
export default App;
CustomHeader.js
import React, { Component } from 'react'
import { Text, View, Picker } from 'react-native'
import Icon from 'react-native-vector-icons/AntDesign'
export default class CustomHeader extends Component {
constructor(props) {
super(props)
this.state = {
user: ''
}
}
updateUser = (user) => {
this.setState({ user: user })
}
render() {
return (
<View>
<Icon name="arrowleft" size={30} onPress={navigation.goBack()} />
<View>
<Picker selectedValue = {this.state.user} onValueChange = {this.updateUser}>
<Picker.Item label = "Steve" value = "steve" />
<Picker.Item label = "Ellen" value = "ellen" />
<Picker.Item label = "Maria" value = "maria" />
</Picker>
</View>
</View>
)
}
}
Considering all files included in App.js, I tried using screenprops for the same, but is not sure how to do it.
In dire need for solution. Please help.
Yes, you can use screenProps here as I made below, but I strongly recommend using third party to manage state like redux, mobx, etc... or simply using context.
class DailyStackWithData extends React.Component {
static router = DailyStack.router
state = {
user: 'steve'
}
setUser = user => this.setState({ user })
componentDidMount(){
this.props.navigation.setParams({ setUser: this.setUser });
}
render(){
const { user } = this.state;
const { navigation } = this.props;
return (<DailyStack screenProps={user} navigation={navigation}/>)
}
}
export const BottomTabNavigation = createBottomTabNavigator({
Daily: {
screen: DailyStack
},
...
});
CustomHeader.js
import React, { Component } from 'react';
import { Text, View, Picker } from 'react-native';
import Icon from 'react-native-vector-icons/AntDesign';
import { withNavigation } from "react-navigation";
class CustomHeader extends Component {
constructor(props) {
super(props)
this.state = {
user: ''
}
}
updateUser = user => {
const setUser = this.props.navigation.getParam('setUser', () => {});
setUser(user);
this.setState({ user: user });
}
render() {
return (
<View>
<Icon name="arrowleft" size={30} onPress={()=>{}} />
<View>
<Picker selectedValue = {this.state.user} onValueChange = {this.updateUser}>
<Picker.Item label = "Steve" value = "steve" />
<Picker.Item label = "Ellen" value = "ellen" />
<Picker.Item label = "Maria" value = "maria" />
</Picker>
</View>
</View>
)
}
}
export default withNavigation(CustomHeader)
and in Graphical or Tabular screen you can get that variable by screenProps props. I've made an example here

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

Could not find "store" in either context or props of Connect(App)

I don't get why I get the error. I did include all the required libraries in my code. Btw, I use navReducer
This is my getStore code:
export default function getStore(navReducer) {
const store= createStore(
getRootReducer(navReducer),
undefined,
applyMiddleware(thunk)
);
return store;
}
The problem lies in my index.js where I named the class App
const AppNavigator = StackNavigator
(
Routes,
{
navigationOptions: {
title: ({ state }) => {
if (state.params) {
return `${state.params.title}`;
}
}
},
initialRouteName:'Login',
headerMode:'none'
}
);
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
const store = getStore(navReducer);
class AppWithNavigationState extends Component {
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})}
/>
);
}
}
AppWithNavigationState.propTypes = {
dispatch: PropTypes.func.isRequired,
nav: PropTypes.object.isRequired,
};
const mapStateToProps=(state)=>{
return {
nav:state.nav
}};
export class App extends Component{
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
export default connect(mapStateToProps)(App);
Sorry my code is too long but I think it's important to include it.
You shouldn't use connect on the Provider, try:
export default App;
instead of
export default connect(mapStateToProps)(App);

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.