Do i have to make seperate MyComponent.android.js file to use SafeArea - react-native

I am pretty new to react-native. Whenever i have to make use of SafeAreaView I have to make a seperate .android.js file for same component without safe area leading to duplication.
Is it possible to conditionally use SafeAreaView with platform.os?

Yes, It is possible to use SafeAreaView with Conditionally for Platform.OS.
SafeAreaView only applicable on ios, but also work on android.
On my code SafeAreaView work on both OS.
If there is a need only on a particular OS then give condition.

I have same problem. So what I done is that create one component called whatever you want eg. SafeScrollView.js and then render conditionally SafeAreaView in that component then pass children to that component.
For example :
I have SafeScrollView.js like below :
import React from 'react';
import { View, SafeAreaView, Platform } from 'react-native';
const SafeScrollView = (props) => {
if (Platform.OS === "ios") {
return (
<View style={props.style}>
{props.children}
</View>
);
}
return (
<SafeAreaView style={props.style}>
{props.children}
</SafeAreaView>
)
}
export default SafeScrollView
Then use SafeScrollView as a component like below :
<SafeScrollView>
// Your component
</SafeScrollView>
Now, In IOS it will render normal View component and if device is android it will render SafeScrollView.
So, you don't have to create separate file.

SafeAreaView only supports ios. So, you can use conditional statement to you this for particular os.

Related

Dark Mode not working React Navigation React Native

I Am working on this to implement Dark Mode in React Native using React Navigation. but it changes only the bottom bar navigator not the screens inside that. can you help me with this
Snack Code
https://snack.expo.io/#belgin/news-app
You're responsible for styling inside your own components. You're styling background as light, setting navigation theme to dark is not gonna magically change the colors you have defined.
For changing themes to work for your components, you need to use the useTheme hook to set colors in your own components instead of hardcoding them.
import * as React from 'react';
import { TouchableOpacity, Text } from 'react-native';
import { useTheme } from '#react-navigation/native';
function MyScreen() {
const { colors } = useTheme();
return (
<View style={{ flex: 1, backgroundColor: colors.background }}>
{/* screen content */}
</View>
);
}
https://reactnavigation.org/docs/themes/#using-the-current-theme-in-your-own-components
Other method is that, you can also create a state which can store your current view-mode (light/dark mode). This is very simple to implement using react-redux. You can refer this video to get better understanding of react and redux.
This is far more simpler implementation of redux.
Note - dependencies such as thunk, react-redux, etc etc are not installed in this video. You can identify which dependencies you're gonna need step-by-step by following error that came in your way. Eg. if createStore gives error try to import createStore as legacy_createstore like done in this question

StatusBar backgroundColor removed after pressing back button (android)

I'm using the StatusBar component in react native (Android). Here is an example code from my App.js component:
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import { RootNavigator } from './components/Router';
export default class MainApp extends Component {
render() {
return (
<View style={{flex: 1}}>
<StatusBar backgroundColor='black' barStyle="light-content"/>
<RootNavigator />
</View>
);
}
}
The StatusBar is working properly when you launch the app, when you navigate through the entire app and when put in background and then return.
It's NOT working when exiting the app by pressing back button. When you launch the app again, the statusbar backgroundColor is suddenly grey (default color).
Is this a known bug or something? I can't figure out how to fix this.
Alright, shortly after submitting the question I found out about another strategy, using imperative API. I avoided it at first since according to official documentation:
For cases where using a component is not ideal, there
is also an imperative API exposed as static functions on the
component. It is however not recommended to use the static API and the
component for the same prop because any value set by the static API
will get overriden by the one set by the component in the next render.
Here is my revised code:
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import { RootNavigator } from './components/Router';
export default class MainApp extends Component {
componentWillMount() {
StatusBar.setBackgroundColor('black');
}
render() {
return (
<View style={{flex: 1}}>
<StatusBar backgroundColor='black' barStyle="light-content"/>
<RootNavigator />
</View>
);
}
}
It seems like this works properly now. When I press the back button and launch the app again the statusbar remains black. I won't declare this as the correct answer just yet in case someone has an explanation why this happens or a better solution.
Edit: This also appears to work only 90% of the time or so. I've noticed, once in a while, when pressing back button and returning the statusbar remained grey. It is absolutely boggling at this point, I suppose componentWillMount isn't always triggered?
Edit2: After switching to componentDidMount instead of componentWillMount as suggested, it seems to be working 100% of the time now.

React.Children.only expected to receive a single React element child

import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import Login from './js/components/Login';
import userReducers from './js/reducers/user';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
let store = createStore(combineReducers({userReducers}));
class App extends Component {
render() {
return (
<Login />
);
}
}
export default class Testextends Component {
render() {
return (
<Provider store = {store}>
<App />
</Provider>
// <View>
// <Text>jhdgf</Text>
// </View>
);
}
}
AppRegistry.registerComponent('Test', () => Test);
It gives me following error:
I'm trying one basic example of react-native with redux. If I remove TouchableHighlight also , the error still persists. Any ideas what is wrong here?
Its doesn't seems you're doing something wrong, on the code pasted, are you sure the error came from this part of your code ?
Maybe try to put between () your TouchableHighlight component, but I don't think that's going to change something...
Or try a ternary rather than a binary operation.
Also be sure that you import TouchableHighlight and Text from react-native.
Try to delete all not necessary white space
<View>{ !this.props.user.loggedIn &&
<TouchableHighlight onPress={this.onLoginButtonPress}>
<Text>Login</Text>
</TouchableHighlight>
}</View>
Because in the following example
<View> {...}</View>
you have two children: first white space and second all in curved brackets.
Had the same issue with the following React Native code:
import {Link} from 'react-router-native'
<Link to={props.url}>{props.title}</Link>
Spend few hours trying to understand why the similar code works in WEB but not in native view. There is no viable error messaging or hints from the React Native runtime on which component causes this failure, so it's insanely hard to identify the issue.
Managed to fix this crash by doing the following:
import {Link} from 'react-router-native'
import {Text} from "react-native";
<Link to={props.url}><Text>{props.title}</Text></Link>
Looks like the title text was treated as multiple elements, so we ended up with such an error.
This is not a solution for the subject. But, because there are so many ways to fail with this error in React Native, it may be useful to put this another example of such a crash for those who Googled and ended here.
First off, "export default class Testextends Component" should actually be "export default class Test extends Component"
the reason why that error is thrown is because the Provider component takes only one element.
for Example:
wrong:
<provider store = {store}>
<div/>
<div/>
<provider>
correct:
<provider store = {store}>
<App/>
<provider>
which you did correctly, if you commented the component. Try the class error and see if it works.

Reusable Button with image tag (React Native)

I want to turn this section of code into a reusable component so I don't have to write the same thing out 5 times.
<TouchableOpacity onPress={console.log('pressed')}>
<Image
source={require('../img/button_australia.png')}
/>
</TouchableOpacity>
The new component I made to mirror this is as follows:
import React from 'react';
import { Image, TouchableOpacity } from 'react-native';
const ImgButton = ({ onPress, img }) => {
return (
<TouchableOpacity onPress={onPress}>
<Image
source={require(img)}
/>
</TouchableOpacity>
);
};
export { ImgButton };
After importing this component ImgButton I call it with this block of code:
<ImgButton
onPress={console.log("pressed")}
img={'../img/button_australia.png'}
/>
I get the error: "Requiring unknown module '../img/button_australia.png'"
I assume I've gone wrong when passing the string down as a prop but from the examples I've looked I don't see what's wrong with what I've done. Thanks :)
As discussed in this react-native issue, it's not possible to require assets or javascript modules with dynamically generated names, e.g. variables.
This is because the React Native packager uses require (and import) statements to generate the module and asset bundles at compile-time, so the value of the variable is not known.
The simplest way is to just pass the image source to the component directly:
<ImgButton
onPress={console.log("pressed")}
img={require('../img/button_australia.png')}
/>

Change view in Navigator in React Native

I'm new to react native.
I was using NavigatorIOS but it was too limiting so I'm trying to use Navigator. In NavigatorIOS I can change a view by calling this.props.navigator.push() but it doesn't work in Navigator, it seems to be structured differently. How do I change views in using Navigator?
That's the minimal working navigator - it can do much more (see at the end):
You need your main "navigator" view to render Navigator component
In the Navigator you need to specify how you should render scenes for different routes (renderScene property)
In this "renderScene" method you should render View (scene) based on which route is being rendered. Route is a plain javascript object, and by convention scenes can be identified by "id" parameter of the route. For clarity and separation of concerns I usually define each scene as separate named component and use the name of that components as "id", though it's just a convention. It could be whatever (like scene number for example). Make sure you pass navigator as property to all those views in renderScene so that you can navigate further (see below example)
When you want to switch to another view, you in fact push or replace route to that view and navigator takes care about rendering that route as scene and properly animating the scene (though animation set is quite limited) - you can control general animation scheme but also have each scene animating differently (see the official docs for some examples). Navigator keeps stack (or rather array) of routes so you can move freely between those that are already on the stack (by pushing new, popping, replacing etc.)
"Navigator" View:
render: function() {
<Navigator style={styles.navigator}
renderScene={(route, nav) =>
{return this.renderScene(route, nav)}}
/>
},
renderScene: function(route,nav) {
switch (route.id) {
case "SomeComponent":
return <SomeComponent navigator={nav} />
case "SomeOtherComponent:
return <SomeOtherComponent navigator={nav} />
}
}
SomeComponent:
onSomethingClicked: function() {
// this will push the new component on top of me (you can go back)
this.props.navigator.push({id: "SomeOtherComponent"});
}
onSomethingOtherClicked: function() {
// this will replace myself with the other component (no going back)
this.props.navigator.replace({id: "SomeOtherComponent"});
}
More details here https://facebook.github.io/react-native/docs/navigator.html and you can find a lot of examples in Samples project which is part of react-native: https://github.com/facebook/react-native/tree/master/Examples/UIExplorer
I find that Facebook examples are either to simplistic or to complex when demonstrating how the Navigator works. Based on #jarek-potiuk example, I created a simple app that will switch screens back and forth.
In this example I'm using: react-native: 0.36.1
index.android.js
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import Apple from './app/Apple';
import Orange from './app/Orange'
class wyse extends Component {
render() {
return (
<Navigator
initialRoute={{screen: 'Apple'}}
renderScene={(route, nav) => {return this.renderScene(route, nav)}}
/>
)
}
renderScene(route,nav) {
switch (route.screen) {
case "Apple":
return <Apple navigator={nav} />
case "Orange":
return <Orange navigator={nav} />
}
}
}
AppRegistry.registerComponent('wyse', () => wyse);
app/Apple.js
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Apple extends Component {
render() {
return (
<View>
<Text>Apple</Text>
<TouchableHighlight onPress={this.goOrange.bind(this)}>
<Text>Go to Orange</Text>
</TouchableHighlight>
</View>
)
}
goOrange() {
console.log("go to orange");
this.props.navigator.push({ screen: 'Orange' });
}
}
app/Orange.js
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Orange extends Component {
render() {
return (
<View>
<Text>Orange</Text>
<TouchableHighlight onPress={this.goApple.bind(this)}>
<Text>Go to Apple</Text>
</TouchableHighlight>
</View>
)
}
goApple() {
console.log("go to apple");
this.props.navigator.push({ screen: 'Apple' });
}
}
I was having the same trouble, couldn't find a good example of navigation. I wanted the ability to control views to go to a new screen but also have the ability to go back to the previous screen.
I used the above answer by Andrew Wei and created a new app then copied his code. This works well but the .push will keep on creating new layers over each other (Apple > Orange > Apple > Orange > Apple > Orange etc.). So I used .pop in the Orange.js file under goApple() instead of .push.
This works like a "back" button now, which was what I was looking for, while teaching how to navigate to other pages.