I am working on a simple React Native App, and have decided to user react-navigation. I have also decided to go with Flow for static type checking. What I can't figure out, is how to define navigation related props with Flow.
e.g. I define my App.js to use StackNavigator like so:
import StackNavigator from 'react-navigation';
import Main from './app/containers/Main';
const App = StackNavigator({
Main: { screen: Main },
});
export default App;
then I define my Main class, but I don't know how to reference react-navigation in my props:
// #flow
import React, { Component } from 'react';
import { View, Text } from 'react-native';
type Props = {
navigate: ????
};
type State = {};
class Main extends Component<Props, State> {
...
}
According to https://github.com/react-navigation/react-navigation/issues/3643
import { NavigationState, NavigationScreenProp } from 'react-navigation';
type Props = {
navigation: NavigationScreenProp<NavigationState>
};
Import NavigationScreenProp from react-navigation:
// #flow
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { NavigationScreenProp } from 'react-navigation';
type Props = {
navigate: NavigationScreenProp<{}>
};
type State = {};
class Main extends Component<Props, State> {
...
}
react-navigation has a flow file. Maybe you can import from there or just copy-paste
https://github.com/react-navigation/react-navigation/blob/master/flow/react-navigation.js#L72
To prevent Cannot import the type NavigationState as a value. Use the import type instead.
// #flow
import React, { Component } from 'react';
import type { NavigationState, NavigationScreenProp } from 'react-navigation';
type Props = {
navigation: NavigationScreenProp<NavigationState>
}
type State = {}
class Main extends Component<Props, State> {
...
}
You can also add below to .flowconfig to prevent flow-typed/npm/redux-devtools-extension_v2.x.x.js:23:33
[lints]
deprecated-utility=off
Related
In my React Native project I have multiple screens, every screen I have to import these types and set is as props type in my screen over and over again.
import { RouteProp } from '#react-navigation/native'
import { StackNavigationProp } from '#react-navigation/stack'
import { NavigatorParameters } from '../references/types/navigators'
type PropsType = {
navigation: StackNavigationProp<NavigatorParameters, 'ChangeData'>,
route: RouteProp<NavigatorParameters, 'ChangeData'>
}
function ChangeData({ navigation, route }: PropsType) {
Can it be more be simplified into like this? So from that I can only import one type only
import ScreenPropsPatternType from '../references/types/screen-props-pattern'
function ChangeData({ navigation, route }: ScreenPropsPatternType<'ChangeData'>) {
The React Navigation lib offers a generic type already for this exact purpose:
Alternatively, you can also import a generic type to define types for
both the navigation and route props from the corresponding navigator:
In your case, it would look like this:
import { StackScreenProps } from '#react-navigation/stack';
import { NavigatorParameters } from '../references/types/navigators'
type PropsType = StackScreenProps<NavigatorParameters, 'ChangeData'>;
function ChangeData({ navigation, route }: PropsType) {
If you'd like to simplify this further, you could probably wrap it and export your own type. See the Generic Object Types documentation for more details on how this works.
import { StackScreenProps } from '#react-navigation/stack';
import { NavigatorParameters } from '../references/types/navigators'
export type ScreenPropsPatternType<K extends keyof NavigatorParameters> =
StackScreenProps<NavigatorParameters, K>;
So you'd be able to easily use it within screen components.
import { ScreenPropsPatternType } from '../references/types/screen-props-pattern';
type PropsType = ScreenPropsPatternType<'ChangeData'> & {
// If you have any other custom props, this is totally optional.
customProp: number;
}
function ChangeData({ navigation, route, customProp }: PropsType) {
I'm getting below error when im trying to navigate to different screen from home screen. i defined initial loading screen in routes. From initial screen trying to navigate to different screen then getting below error.
this.props is returning just {}. Not really sure why.
login.component.js
import React, {Component} from 'react';
import {NavigationActions} from 'react-navigation';
import {
Platform,
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
Touchable,
Image,
Button
} from 'react-native';
import { handleFbLogin } from '../../config/authConfig/';
class Login extends Component {
render () {
console.log("this.props")
console.log(this.props)
return (
<View style={{flex: 1,backgroundColor: '#37afa6'}}>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('setupProfile')}
/>
</View>
);
}
}
export default Login;
routes/index.js
import {StackNavigator} from 'react-navigation';
import Login from '../pages/Login.page';
import SetupProfile from '../pages/setupProfile.page';
const Config = {
navigation: {
login: {
screen: Login
},
setupProfile: {
screen: SetupProfile,
}
}
}
export default Config;
App.container.js
import React, {Component} from 'react';
import Login from './pages/Login.page';
import {connect} from 'react-redux';
import Router from './routes';
import {
Platform,
StyleSheet,
Text,
View,
TextInput
} from 'react-native';
class App extends Component {
render () {
return (
<Router />
);
}
}
export default App;
index.js(startup point):
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import app from './app/index';
import Config from './app/routes/index';
export const AppNavigator = StackNavigator(Config.navigation,{initialRouteName : 'login'});
AppRegistry.registerComponent('BuddApp', () => AppNavigator);
export default app;
i'm able to load initalscreen after these changes but when trying to navigate still getting the same error.
EDIT :
I got it. You forgot the constructor in your login screen, that's why your props are empty :
constructor(props) {
super(props);
}
PREVIOUS ANSWER :
Without the full code, i can't see what the exact problem is, but i will give you an exemple of use of react-navigation :
In your index.js (start point of your app) :
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
export const AppNavigator = StackNavigator(Config.navigation);
AppRegistry.registerComponent('appName', () => AppNavigator);
In the navigation config :
const Config = {
navigation: {
login: {
screen: Login
},
setupProfile: {
screen: SetupProfile,
}
}
}
The first object found in the navigation config is the start of your app.
Now you can use your navigation function in the "login" page with navigate :
this.props.navigation.navigate('setupProfile')
If the AppRegistry is filled everything should run fine.
I hope it help !
I'm trying to work with MobX for a new project.
I started it on May 2017, and everything was working well. I had to stop, and now I go on developing it. I had to make an npm install to manage making it working, but now, I have some problem with stores...
I rely on this tutorial for the structure : https://blog.callstack.io/write-react-native-apps-in-2017-style-with-mobx-e2dffc209fcb
This is my structure :
Main index.js
import { Provider } from 'mobx-react';
import Stack from './router';
import stores from './stores';
export default class App extends Component {
render() {
return (
<Provider {...stores}>
<Stack />
</Provider>
);
}
}
Index.js of my stores in ./stores/index.js
import ChatStore from './ChatStore';
import UserStore from './UserStore';
export default {
UserStore: new UserStore(),
ChatStore: new ChatStore(),
};
./stores/UserStore.js (important parts)
import { observer, inject } from 'mobx-react';
import {autobind} from 'core-decorators';
...
#inject(['ChatStore'])
#observer
#autobind
export default class UserStore {
#observable isAuthenticated = false;
#observable isConnecting = false;
#observable user = null;
#observable messages = [];
#observable hasMoreMessages = false;
#observable skip = 0;
...
login() {
const payload = {
strategy: 'local',
material_id: DeviceInfo.getManufacturer(),
password: DeviceInfo.getManufacturer()
};
return this.authenticate(payload);
}
...
Now, for components part :
Router.js
import { StackNavigator } from 'react-navigation';
import Home from './containers/Home';
const stackNavigatorConfig = {
initialRouteName: 'Home',
};
export default StackNavigator({
Home: {
screen: Home,
},
}, stackNavigatorConfig);
./containers/Home.js
import React, { Component } from 'react';
import { AsyncStorage } from 'react-native';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
#inject('UserStore')
#observer
export default class Home extends Component {
props: Props;
...
render() {
this.props.UserStore.login().catch(error => {
console.log('LOGIN', 'ERROR', JSON.stringify(error), error.message);
});
return {
...
}
}
And then, I get an error :
So, I sum up :
I use <Provider> from MobX, to give all my stores to my app
Then, I get the Store I want in my component with #inject
I use it as a props, using this.props.UserStore...
But it does not work. I rely on this tutorial for the structure : https://blog.callstack.io/write-react-native-apps-in-2017-style-with-mobx-e2dffc209fcb
Maybe there was an update between May 2017 and today, that makes things different... It was working well on May 2017.
I think this is a dummy error, but I can't find which one...
Everything looks good except the decorators on your UserStore class: #inject(['ChatStore']) #observer #autobind. #inject(['ChatStore']) #observer is used on React components, #autobind might still work as intended.
It should work if you remove those.
maybe worth using #action from mobx
Please take a look at these:
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
As you see, it imports something, but why React is out of {} and all others are into it? {Component} { AppRegistry, Text, View }
Anyway, when should I wrap something into {}?
The difference is in how the file exports, without {} is the default export. There can only ever be one default export.
Anything inside the {} is part of a named exported function, class, or variable that is exported.
If you look at the react source code you will find the following es5 code.
var ReactComponent = require('./ReactComponent');
...
var React = {
...
Component: ReactComponent,
...
}
module.exports = React;
When you import React, { Component } you are importing all of React
along with React.Component as Component
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Becomes
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
This is commonly used to destructure an object like the following.
const person = {
firstName: 'John',
lastName: 'Doe',
};
const { firstName } = person;
Which is the same as
person.firstName
When you
export default MyComponent // import MyComponent
export MyComponent // import { MyComponent }
I've seen an upgrade guide here how to use withRouter but I have a problem implementing it on my project. It's having problem with my connect. When I have my connect above withRouter, router exists on props but not my states, and when I have connect below withRouter, states exist but not router.
Here's my code:
import React from 'react';
import { withRouter } from 'react-router';
import moment from 'moment';
import { connect } from 'react-redux';
export default class ThisComponent extends React.Component {
render() {
console.log(this.props)
return ...
}
});
export default connect(state => ({ oneState: state.oneState, twoState: state.twoState }))(ThisComponent)
export default withRouter(ThisComponent)
Well you have 3 "export default" when you are supposed to have only one per file.
Your component should look like this
import React from 'react';
import { withRouter } from 'react-router';
import moment from 'moment';
import { connect } from 'react-redux';
class ThisComponent extends React.Component {
render() {
console.log(this.props)
return ...
}
});
ThisComponent = connect(state => ({ oneState: state.oneState, twoState: state.twoState }))(ThisComponent)
export default withRouter(ThisComponent)