Why is this.props.componentId needed? - react-native

Why is this.props.componentId needed?
What is its purpose?
Why can't we use the library without that id being needed?
react-navigation doesn't need something like that, and react-native-navigation v1 didn't use anything like that. So why does v2 needs and uses that? The reason I ask is firstly to understand it, and secondly to see if I can skip this since now I cannot use RNN v2 from a saga.

Here's a detailed answer from a blogpost by the react-native-navigation library developer.
So now we want to enable the following behavior: after user clicks on the text, the app pushes the ViewPost screen. Later on it will be very easy to attach the same function to a list item instead of the text. To push a new screen into this screen’s navigation stack, we will use Navigation.push. In the new API this method expects to receive the current componentId which can be found in props.componentID. So in PostsList.js we create a pushViewPostScreen function and attach it to the onPress event of the Text.
import React, {PureComponent} from 'react';
import {View, Text} from 'react-native-ui-lib';
import PropTypes from 'prop-types';
import {Navigation} from 'react-native-navigation';
class PostsList extends PureComponent {
static propTypes = {
navigator: PropTypes.object,
componentId: PropTypes.string
};
constructor(props) {
super(props);
this.pushViewPostScreen = this.pushViewPostScreen.bind(this);
}
pushViewPostScreen() {
// We pass the componentId to Navigation.push
// to reference the which component will be pushed
// to the navigation stack
Navigation.push(this.props.componentId, {
component: {
name: 'blog.ViewPost',
passProps: {
text: 'Some props that we are passing'
},
options: {
topBar: {
title: {
text: 'Post1'
}
}
}
}
});
}
render() {
return (
<View flex center bg-blue60>
<Text onPress={this.pushViewPostScreen}>Posts List Screen</Text>
</View>
);
}
}
export default PostsList;
In the official docs, it seems that the Screen API, which is responsible of pushing, popping and changing navigation, will be accessible through the Navigation module always expecting a props.componentId to get the reference to the component.

Related

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);
}

Is there a way to read the options before use the mergeOptions function in react native navigation v2?

Is there a way to read the options before using the mergeOptions function.
I'm trying to add a sideMenu that opens and closes with the same button. But to handle that logic, Instead of making use of redux, I want to read the options before the merge, so I can simply do something like visible: !pastVisible.
navigationButtonPressed({ buttonId }) {
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
'left': {
visible: false
}
}
});
console.log(`Se presiono ${buttonId}`);
}
So basically I want to read the value of the visible option before changed it.
By now, I can only achieve this using redux.
import React, {Component} from 'react';
import {View, Text} from 'react-native';
import { Navigation } from 'react-native-navigation';
import { connect } from 'react-redux';
import { toggleSideMenu } from './../../store/actions/index';
class SideDrawer extends Component {
constructor(props) {
super(props);
Navigation.events().registerComponentDidDisappearListener(({ componentId }) => {
this.props.toggleSideMenu(false);
});
}
render() {
return (
<View>
<Text>This is the sidedrawer</Text>
</View>
);
}
}
const mapDispatchToProps = dispatch => {
return {
toggleSideMenu: (visible) => dispatch(toggleSideMenu(visible))
};
};
export default connect(null, mapDispatchToProps)(SideDrawer);
Then I just add the listeners to the sidemenu component. Depending on the case, I update the current state of the component (visible or not).
Finally on the components where I want to use the side drawer button I just implement the navigationButtenPressed method. Then I just call the reducer to know the current visible state and toggled it.
navigationButtonPressed({ buttonId }) {
const visible = !this.props.sideMenu;
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
'left': {
visible: visible
}
}
});
this.props.toggleSideMenu(visible);
}
If there is a more easy way to achieve this I'll be glad to know about it.

MobX with React Native: store is undefined

This is my first go at using MobX so this may be a simpler problem than I imagine, but I'm not getting any errors with the things I've tried; the store is simply undefined wherever I try to use it. I've tried both importing the store directly into components and passing props from the main file (also with , but I'm not sure if I used that right). I've experimented with several different .babelrc file settings as well, but that doesn't seem to be an issue.
Here is the UserStore:
import React from 'react';
import { observable } from 'mobx';
class UserStore {
#observable info = {
username: "bob",
password: "secret",
email: "bob#email.com"
}
}
const userStore = new UserStore()
export default userStore;
Here is a simplified App.js:
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Profile from './app/Profile.js';
import { UserStore } from './app/UserStore.js';
export default class App extends Component {
constructor(){
super();
this.state = {
page: 'Profile',
}
}
changePage(){
switch (this.state.page) {
case "Profile":
return <Profile logout={this.logout.bind(this)} userStore={UserStore}/>;
}
}
render() {
return (
<View>
{this.changePage()}
</View>
);
}
}
And here is a simplified Profile.js:
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { observer } from 'mobx-react/native';
#observer
export default class Profile extends Component {
render() {
console.log(this.props.userStore);
return (
<View>
<Text>Profile Page</Text>
<Text>username: {props from store go here}</Text>
<Text>password: {props from store go here}</Text>
<Text>email: {props from store go here}</Text>
</View>
);
}
}
All I'm trying to do right now is get the pre-defined observable "info" object from the store to the Profile.js component and display that information. This is being way more difficult than it should be - any insight is greatly appreciated!
Since you declared export default userStore; in UserStore.js
Try changing the way you import in App.js by removing the {}:
import UserStore from './app/UserStore.js';
{} is needed only if you want to do a named import. Here is a good read if you want to know more.

List of default method of class create by React.createClass() that I can override

By watching, reading tutorials from variable source, I already known 2 methods that I can override are getInitialState() and render().
I wonder are there any other methods that I can override.
I already searched on Google but I haven't found any official document yet.
Can anybody help me ?
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
Image,
Navigator,
View
} from 'react-native';
var LessonList = React.createClass({
getInitialState: function() {
return {
meow:'MeowMeow',
};
},
render:function(){
return(
<Text>
Keep Calm and Meow On !!!
</Text>
);
}
});
List available in Component Specs in React docs.

Disable rotation for specific views in react native

How can I disable rotation only for specific views (e.g: when using Navigator) and not for the entire app?
The question here already addresses disabling rotation for the entire app
With the react-native-orientation package, it's possible to lock the orientation to portrait/landscape. The package and documentation can be found here: https://github.com/yamill/react-native-orientation
Remember; you should not put your locking inside the rendering of scenes (nor the renderScene method). Since the Navigator re-renders all the scenes in the route stack, this would probably cause weird side effects for you. Rather, the locking/unlocking should be put in the code that interacts with the route stack (ie. calls the push/pop methods).
If your case is about more specific control over orientations of different screens in StackNavigator (something like Portrait -> LandscapeLeft -> LandscapeRight -> Portrait, and all the way back), here is a may-not-that-pretty solution:
packages needed: react-navigation, react-native-orientation;
define base screens as follow:
// baseScreen.js
import React, { Component } from "react";
import Orientation from "react-native-orientation";
export class PortraitScreen extends Component {
constructor(props) {
super(props);
this._willFocusSubscription = this.props.navigation.addListener("willFocus", payload => {
// lock to portrait when this screen is about to appear
Orientation.lockToPortrait();
})
}
componentWillUnmount() {
// remove subscription when unmount
this._willFocusSubscription.remove();
}
}
export class LandscapeScreen extends Component {
constructor(props) {
super(props);
this._willFocusSubscription = this.props.navigation.addListener("willFocus", payload => {
// lock to landscape
Orientation.lockToLandscape();
})
}
componentWillUnmount() {
// remove subscription either
this._willFocusSubscription.remove();
}
}
define concrete screens which extends abovementioned base screen(s):
// moduleScreens.js
import React from "react";
import { Button, View } from "react-native";
import { PortraitScreen, LandscapeScreen } from "/path/to/baseScreen";
export class VideoDescScreen extends PortraitScreen {
render() {
return (
<View>
<Button
title="watch video"
onPress={() => this.props.navigation.navigate("VideoPlayer")}
/>
</View>
)
}
}
export class VideoPlayerScreen extends LandscapeScreen {
render() {
return <View>...</View>
}
}
create route like this:
// route.js
import React from "react";
import { createStackNavigator } from "react-navigation";
import { VideoDescScreen, VideoPlayerScreen } from "/path/to/moduleScreens";
const stack = createStackNavigator(
{
VideoDesc: {
screen: VideoDescScreen
},
VideoPlayer: {
screen: VideoPlayerScreen
}
}
)
How it works? According to doc, we observe event willFocus when screen is initialized, and each time this screen is about to appear (focused) in navigation, we lock device to our desired orientation, works for both PUSH(to) and POP(back from) behaviors.
Hope it helps.