How to use MobX with react-native-navigation by Wix? - mobx

Quote from site:
We fully support Redux, MobX and other state management libraries.
But if I try to pass store and Provider to registerComponent() I still receive error, that react-mobx can't inject store that doesn't exists. Also I've tried Provider by megahertz, but it seems like this code is outdated.
Are there any ways to use react native navigation v2 with mobx?

If you create a HOC in where you provide the store with a provider it works.
const addStore = (Component, ...props) => {
return class App extends React.Component {
render() {
return (
<Provider venues={Stores}>
<Component {...{
...this.props,
...props,
}} />
</Provider>
);
}
}
};
export async function RegisterScreens() {
Navigation.registerComponent('venuesOverview', () => addStore(VenuesOverview));
}

Related

Making navigation available to all components in React Native app

I'm using React Navigation 5 in my React Native app and I'm not exactly clear about how to make navigation available to all components. I also want to mention that my app uses Redux for state management but I didn't integrate React Navigation with Redux -- maybe I should!
My App.js renders my Navigator.js component which looks like below. BTW, my app requires authentication and one of the key functions of the navigator function is to redirect unauthenticated users to login screen.
class Navigator extends Component {
render() {
return (
<NavigationContainer>
{
this.props.isAuthenticated
? <MainMenuDrawer.Navigator drawerContent={(navigation) => <DrawerContent member={this.props.member} navigation={navigation} drawerActions={DrawerActions} handleClickLogOut={this.handleClickLogOut} />}>
<MainMenuDrawer.Screen name="Home" component={HomeStack} />
<MainMenuDrawer.Screen name="ToDoList" component={ToDoListStack} />
</MainMenuDrawer.Navigator>
: <SignInStack />
}
</NavigationContainer>
);
}
}
Then in my HomeStack, I have my Dashboard component which happens to be a class component that needs to access navigation. Here's my HomeStack:
const HomeStackNav = new createStackNavigator();
const HomeStack = () => {
return(
<HomeStackNav.Navigator screenOptions={{ headerShown: false }}>
<HomeStackNav.Screen name="DashboardScreen" component={Dashboard} />
<HomeStackNav.Screen name="SomeOtherScreen" component={SomeOtherComponent} />
</HomeStackNav.Navigator>
);
}
export default HomeStack;
Say, my Dashboard component looks like below. How do I make navigation available to this component?
class Dashboard extends Component {
handleNav() {
// Need to use navigation here...
}
render() {
return (
<Text>Welcome to Dashboard</Text>
<Button onPress={() => this.handleNav()}>Go somewhere</Button>
);
}
}
function mapStateToProps(state) {
return {
member: state.app.member
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(appActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Do I need to pass navigation manually to each component? That seems too repetitive and prone to error. How do I make navigation available throughout my app?
Solution 1:
you can use useNavigation hook for functional component
import {useNavigation} from '#react-navigation/native';
const navigation = useNavigation();
navigation.navigate("interests");
//or
navigation.push("interests");
Solution 2:
you can use HOC withNavigation to navigation in props in any component for class component Ref
you can install #react-navigation/compat by
yarn add #react-navigation/compat
You can import like below
import { withNavigation } from '#react-navigation/compat';
you can use withNavigation like below
export default withNavigation(Dashboard)
Note: then you can use this.props.navigation in Dashboard component

Can not implement callback inside of useFocusEffect from React-navigation

I have React-native app with topTabNavigator with three tabs. And usually componentDidMount and componentWillUnmount lifecycle methods don't work when the user changes the tab. Therefore instead of them I decided to use for the side effects onWillFocus and onDidFocus from React-Navigation. And before 5th version of this great library https://reactnavigation.org/ it was possible to import NavigationEvents component and put it to the view with focused callbacks:
import { NavigationEvents } from 'react-navigation';
class MyTeamScreen {
const store = this.props.store;
const members = store.members;
return (
<View>
<NavigationEvents
onWillFocus={payload => store.getTeamMebers()}
onDidFocus={payload => store.dispose()}
/>
<MymebersList team={members} />
</View>
);
}
export default MyScreen;
But at the moment there is no like this way after the upgrade of react reactnavigation library, because NavigationEvents is deprecated. And only one way to use useFocusEffect. And this is my hook:
function FetchMembers(store) {
useFocusEffect(
React.useCallback(() => {
return () => store.getMembers();
}, [store])
);
return null;
}
Class component:
class MyTeamScreen {
const store = this.props.store;
const members = store.members;
return (
<View>
<FetchMembers store={store} />
<MymebersList team={members} />
</View>
);
}
But I'm getting the error:
And I checked the store was initialized inside of the hook, but it can not call a method from it, because it's undefined.
Can you tell me please what I'm doing wrong? Is it a good way to use react-navigation methods instead of lifecycles componentDidMount and componentWillUnmount? Or maybe you could recommend me please the better way how to implement side effect when the user is changing the tab?
Looking at your code, I'm curious (but not so sure) that you may refer to the incorrect props?
Would you mind trying this?
Because the first parameter of functional component is props. To refer to props.store, you can use object destructuring like this
function FetchMembers({store}) {
useFocusEffect(
React.useCallback(() => {
return () => store.getMembers();
}, [store])
);
return null;
}
or
function FetchMembers(props) {
useFocusEffect(
React.useCallback(() => {
return () => props.store.getMembers();
}, [props.store])
);
return null;
}

How to get access to reducer inside App using React Navigation v5 in React Native?

I'm trying to build app with React Navigation v5 and stuck with Authentication flow.
Here is some code to understand what I'm trying to do:
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const AuthContext = React.createContext();
export default class App extends Component {
// constructor() ...
render() {
const store = configureStore(); // rootReducer
return (
<AuthContext.Provider store={store}>
<NavigationContainer>
// here I have to access my userReducer to check is user logged in and using LoginStack or Drawer
)
// my stacks
}
So in React Navigation docs Authentication flows uses function components and React Hooks inside them. But I'm using class-component, and I have my reducer in standalone file.
I tried to use connect() as I always do on child components in my app, but this won't work.
So is there any way to access (or map) reducer to App at the topmost level? Or maybe I'm doing something wrong and there is a better way to build switch between 2 separate stacks based on authentication?
Im not sure if this is the best way but you'll just have to use react hooks and redux subscribe:
export default function Navigation({store}) {
const [authorized, setAuthorized] = useState(
store.getState().user.auth !== null,
);
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setAuthorized(store.getState().user.auth !== null);
});
return () => { unsubscribe(); }
});
return (
<NavigationContainer>
{authorized ?
<Stack.Navigator>...</Stack.Navigator> : <Stack.Navigator>...</Stack.Navigator>}
</NavigationContainer>
)
}
Then pass your store:
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Navigation store={store} />
</Provider>
);
}
}

Context API w/ React Navigation (React Native)

I'm trying to wrap my mind around using Context in my React Native app that uses React Navigation. I think I am way off on this one. I am simply trying to pass the name of a book to my entire app through the navigation stacks.
App.js
const BookContext = React.createContext();
class BookProvider extends Component {
state = {
name: 'book name'
}
render() {
return (
<BookContext.Provider value={{
name: this.state.name
}}>
{this.props.children}
</BookContext.Provider>
)
}
}
export default function App() {
return (
<BookProvider>
<BookContext.Consumer>
{({ name }) => (<Routes name={name} />)} //my react navigation stacks component
</BookContext.Consumer>
</BookProvider>
);
}
and in Book.js (a component in the navigation stack)
componentDidMount() {
console.log(this.context)
}
returns an empty object {}
Any help is appreciated!
To save you some Googling, this is the correct approach: https://github.com/react-navigation/react-navigation/issues/935#issuecomment-359675855
Another way, if you're using a relatively new version of React, and your component in question at that route is a functional component, is to use the useContext React hook.
e.g.
import React, { useContext } from 'react'
import { BookContext } from '/path/to/BookContext'
function BookConsumerComponent() {
const { name } = useContext(BookContext);
console.log(name);
}

react-native-navigation theming with styled-components

I'm working with styled-components on my react-native project. We are using react-native-navigation to perform navigation within the application. So the question is how can I implement theme pattern from styled-components in such kind of application?
The problem is that to perform the idea of theming in terms of styled-components I have to wrap my top level component in <ThemeProvider /> like this:
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
But with react-native-navigation I don't have top level component. It has the idea of screens, so the application entry will look like this:
registerScreens(); // this is where you register all of your app's screens
// start the app
Navigation.startSingleScreenApp({
screen: { ... },
drawer: { ... },
passProps: { ... },
...
});
The answer was pretty simple. As react-native-navigation's registerComponent has possibility to pass redux store and Provider as a props:
Navigation.registerComponent('UNIQUE_ID', () => YourComponent, store, Provider);
We can create our custom Provider with both redux and styled-components Providers and pass this custom provider to the registerComponent like this:
import { Provider } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import theme from './theme';
const Provider = ({ store, children }) => (
<Provider store={store}>
<ThemeProvider theme={theme}>
{children}
</ThemeProvider>
</Provider>
);
export default Provider;
For more details look #1920.
I think you can do something like this
function wrap(Component) {
return function () {
return (
<ThemeProvider theme={theme}>
<Component />
<AnotherComponent/>
</ThemeProvider>
);
};
}
function registerScreens(store, Provider) {
Navigation.registerComponent('app.SomeScreen', () => wrap(SomeScreen), store, Provider);
// more screens...
}