ReactNative ActivityIndicator not showing when animating property initiate false - javascript

I'm playing with react native and got a strange behaviour.
When I try to show a ActitvityIndicator for Android setting its animating property to true with a showProgress variable in the state it doesn't work if the variable is started as false.
In the sample below if the ActivityIndicator animating property start as true, then the buttons make the ActivityIndicator hide or appear correctly.
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TextInput,
TouchableHighlight,
ActivityIndicator
} from 'react-native';
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: true
};
}
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator animating={this.state.showProgress} size="large"/>
</View>
);
}
progressOff() {
this.setState({showProgress: false});
}
progressOn() {
this.setState({showProgress: true});
}
}
But if i use the code below, with the animating property starting as false, then the button to make the ActivityIndicator appear doesn't work:
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TextInput,
TouchableHighlight,
ActivityIndicator
} from 'react-native';
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: false
};
}
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator animating={this.state.showProgress} size="large"/>
</View>
);
}
progressOff() {
this.setState({showProgress: false});
}
progressOn() {
this.setState({showProgress: true});
}
}
What am I missing here?

This appears to be a bug in React Native. The code with initial state being showProgress: false works on iOS but not on Android.
I've opened an issue on github if you want to follow the progression:
https://github.com/facebook/react-native/issues/9023
Option 1
A workaround I've used is to use the showProgress variable to render a completely different view with the ActivityIndicator:
render() {
if (this.state.showProgress) {
return this.renderLoadingView();
} else {
return this.renderMainView();
}
}
Option 2
You can also set the opacity of the ActivityIndicator according to the state:
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator style={{opacity: this.state.showProgress ? 1.0 : 0.0}} animating={true} size="large"/>
</View>
);
}
However the spinner animation doesn't always start at the same position when using this method.

This is a bug of React-Native for component Activity Indicator.
I am not sure that fb has already solved it but you can try this
constructor(props) {
super(props);
this.state = {
opacity: 0
};
}
to show it use this.setState({opacity:1}) and to hide again this.setState({opacity:0}) in your called functions
and in the render where you are using activity indicator
<ActivityIndicator
animating={true}
color="#ffffff"
style={{height: 80, marginTop: 10, opacity: this.state.opacity }}
size="large"/>

If in your project you can use third party components, I recommend the use of react-native-loading-spinner-overlay
Solved easily our problems, beacause this component use a similar way to show or hide the Activity with the property visible.

Another way I found effective to work around that problem without much code is:
{ this.state.showProgress &&
<ActivityIndicator animating={true} size="large"/>
}

I tried a different approach which I think that it is a more "react way" to solve problems. So, the problems with the opacity solution is that If you just set it to 0, it still will be a animation, so it is not the best solution thinking in your app performance.
I created a separated component that I called <Loading/>, here is the code:
import { ActivityIndicator } from "react-native"
import React from "react"
import PropTypes from "prop-types"
const Loading = (props) =>
props.animating
? <ActivityIndicator style={props.style}
importantForAccessibility='auto' size={props.size}
color={props.size} /> : null
Loading.propTypes = {
animating: PropTypes.bool.isRequired,
style: PropTypes.oneOfType([PropTypes.style, PropTypes.object]),
}
export default Loading
Usage:
<Loading animating={true} importantForAccessibility='auto' size="large" color="#A02BFF" style={styles.loading} />
That way it will avoid to create a animation when it is not a necessity, you will create separated component that can be removed easily at the point that the ActivityIndicator issue becomes solved in the future by replacing it to the original ActivityIndicator native component.

The only problem I had with this, was that in Android it wasn't visible because of the background I had on my screen. I fixed by only changing the color prop to something I knew should stand out in the background:
<ActivityIndicator color={theme.secondary.color} />

i got this problem all by a mistake. i did not put ActivityIndeicator in the center of a view. so it positioned on top of a view, which is covered by a natigation bar. code below is correct. hope this can help u.
<View style={{alignItems: 'center', justifyContent: 'center', flex: 1, backgroundColor: 'white'}}>
<ActivityIndicator
animating={true}
style={
{
alignItems: 'center',
justifyContent: 'center',
opacity: this.state.loading ? 1 : 0
}}
size="large"
/>
</View>

A quick fix Use conditional rendering.. Keep animating : {true} and just Visible and invisible view.
Checkout :
https://kylewbanks.com/blog/how-to-conditionally-render-a-component-in-react-native

In my case, for react native version 0.59.10 , the size property type is different for Android and iOS, so for that I had to make a Platform check as following and it worked.
<ActivityIndicator
size={Platform.OS === "ios" ? 0 : "large"} //This platform check worked.
color={props.color}
animating={props.animating}
style={props.style}
/>

The transition of animating from false to true is too slow on Android. But you can force a re-render using the key prop:
<ActivityIndicator
key={`${props.animating}`}
animating={props.animating}
/>
When props.animating changes from false to true, they key also changes. This forces a re-render, meaning that a new component is rendered with animating = true, which will instantly start your spinner.

If you are testing it on Android one of the reason could be the color property.
Be sure to give the ActivityIndicator a color. For example:
<ActivityIndicator size="large" color="#0000ff" />

This solution work perfectly for me in Android.
Hope this will help you.
import {ActivityIndicator} from 'react-native';
const [opacity, setOpacity] = useState(0)
const onLoadStart = () => {
setOpacity(1);
};
const onLoad = () => {
setOpacity(0);
};
const onBuffer = ({isBuffering}) => {
setOpacity(isBuffering ? 1 : 0);
};
return(
<View>
<Video
video={{uri: props.videoSource}}
autoplay={false}
customStyles={{
seekBarProgress: {
backgroundColor: theme.color.primary,
},
seekBarKnob: {
backgroundColor: theme.color.primary,
},
}}
ref={ref => (player = ref)}
onBuffer={onBuffer}
onLoadStart={onLoadStart}
onLoad={onLoad}
/>
<ActivityIndicator
animating
size="large"
color={color.primarylight}
style={{
opacity: opacity,
position: 'absolute',
top: 70,
left: 70,
right: 70,
// height: 50,
}}
/>
</View>
)

Related

How do you execute Javascript in React-Native WebView?

I'm trying to execute javascript in a WebView that is loaded on an iOS advice. I'm trying to get a painfully simple example to work but it is consistently throwing an undefined or TypeError.
Here is the code:
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
constructor(props){
super(props)
this.onPressButton = this.onPressButton.bind(this)
}
onPressButton(){
this.webview.injectJavascript(`alert('hello')`)
}
render() {
return (
<View style={{ height: '100%' }}>
<WebView
ref={ref => (this.webview = ref)}
javaScriptEnabled={true}
source={{ uri: 'https://google.com' }}
/>
<Button onPress={this.onPressButton} style={{ height: '10%' }} title="Test Javascript" />
</View>
);
}
}
Please don't recommend to use injectedJavascript because that is not my desired functionality.
Would appreciate any other approaches as well.
According to react-native-webview, you need to use injectJavaScript, not injectJavascript. That'll fix your issue.
Also, there are few things that need to change
You don't need to bind this.onPressButton = this.onPressButton.bind(this) instead you can use an arrow function.
You don't need to use javaScriptEnabled={true}. It is already using default value as true.
After your script includes true. This is required, or you'll sometimes get silent failures.
Check complete sample code
import React, { Component } from "react";
import { View, Button } from "react-native";
import { WebView } from "react-native-webview";
const script = `
alert('hello')
true;
`;
export default class App extends Component {
onPressButton = () => {
this.webview.injectJavaScript(script);
};
render() {
return (
<View style={{ flex: 1 }}>
<WebView
ref={ref => (this.webview = ref)}
source={{ uri: "https://google.com" }}
/>
<Button onPress={this.onPressButton} title="Test Javascript" />
</View>
);
}
}
Hope this helps you. Feel free for doubts.

The text of button at react native is always uppercase

I created a component at react-native, but the text of the button is always at uppercase, someone knows why it doesn't take the text that pass, because I want to show 'Login', but it shows 'LOGIN'
import React, { Component } from 'react';
import { View, Button} from 'react-native';
import LabelApp from "../../config/labels.app";
const labelApp = LabelApp.loginView;
export default class Login extends Component {
constructor(props){
super(props);
this.handleClickBtnEnter = this.makeLogin.bind(this);
}
makeLogin() {
}
render() {
return (
<View>
<Button title= {labelApp.textButtonLogin} onPress={this.handleClickBtnEnter}/>
</View>
);
}
}
Label of component
const LabelApp = {
loginView: {
textButtonLogin: 'Ingresar',
},
}
export default LabelApp;
The visualization
For react Native Paper button use uppercase={false} prop:
<Button
mode="outlined"
uppercase={false}
accessibilityLabel="label for screen readers"
style={styles.yourButtonStyle}>Button label</Button>
So, the other two answers are correct that you should use TouchableOpacity, but as someone new to React Native, it took me awhile to understand what was going on here. Hopefully this explanation provides a little more context.
The built-in Button component seems to have some weird compatibility/visibility issues on occasion, one of which is rendering the title prop text all uppercase. When viewing the documentation for the Button component in Chrome, the preview shows all text being capitalized under the "Web" view but not Android or iOS (I was having this issue using Expo and Metro Bundler on an Android device, so not sure what to make of this). I couldn't find anything about capitalization/uppercase in the Button docs, so perhaps this is a bug.
The solution is to use a different component called TouchableOpacity. It also has an onPress event you can use and a built-in touch animation, but it has less out of the box styling than the Button component. Important to note from docs: "Opacity is controlled by wrapping the children in an Animated.View, which is added to the view hierarchy. Be aware that this can affect layout." It doesn't have a title prop, so you just put the button text in a Text component, like so:
<Button
title='text will be capitalized'
onPress={onPress}
/>
becomes
<TouchableOpacity onPress={onPress}>
<Text>text will stay lowercase</Text>
</TouchableOpacity>
I was having the same issue as OP, and this solved it for me.
From the official documentation
A basic button component that should render nicely on any platform. Supports a minimal level of customization.
The recommend use of touchable opacity or touchable native feedback
https://facebook.github.io/react-native/docs/touchableopacity
Below I've added textTransform: 'lowercase', as a style rule for the button to override any inherited text casing.
import React, { Component } from 'react'
import {
StyleSheet,
TouchableOpacity,
Text,
View,
} from 'react-native'
export default class App extends Component {
constructor(props) {
super(props)
this.state = { count: 0 }
}
onPress = () => {
this.setState({
count: this.state.count+1
})
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={this.onPress}
>
<Text> Touch Here </Text>
</TouchableOpacity>
<View style={[styles.countContainer]}>
<Text style={[styles.countText]}>
{ this.state.count !== 0 ? this.state.count: null}
</Text>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 10
},
button: {
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
textTransform: 'lowercase', // Notice this updates the default style
},
countContainer: {
alignItems: 'center',
padding: 10
},
countText: {
color: '#FF00FF'
}
})
https://snack.expo.io/Bko_W_gx8
This question is 3 years old and I'm not sure why no one has answered it correctly until now.
Native android buttons are all caps by default starting from android lollipop, which is what react native uses when you use the control Button from react-native in your app. To override the functionality, you just need to add this line in your styles.xml file inside your app theme (not the splash screen style)
<item name="android:textAllCaps">false</item>
You can get more details here: https://stackoverflow.com/a/30464346/11104068
The changes are not going to apply instantly obviously since the change is in the naive xml file and not in a JavaScript file. So you will need to do a npm/yarn run android
I've tried your code and it looks like it's the expected behaviour with Button component from react-native
You can see this at the official documentation
I believe that you need to change Button component, take it from another package to meet your needs.
As an alternative you can create your own button
import React, { Component } from 'react';
import { View, Button, TouchableHighlight, StyleSheet } from 'react-native';
import LabelApp from "../../config/labels.app";
const labelApp = LabelApp.loginView;
export default class Login extends Component {
constructor(props){
super(props);
this.handleClickBtnEnter = this.makeLogin.bind(this);
}
makeLogin() {
}
render() {
return (
<View>
<TouchableHighlight onPress={this.handleClickBtnEnter} underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>{labelApp.textButtonLogin}</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3'
},
buttonText: {
textAlign: 'center',
padding: 20,
color: 'white'
}
});
<Button
style={{
borderRadius: 10,
backgroundColor: "#000",
width: 200,
height: 50,
}}
>
<Text
uppercase={false}
>
Login
</Text>
</Button>

React Native Layout Animation Not Animating? Or Doing It Wrong?

I've followed a couple of good tutorials in using Layout Animation with react native. I cannot seem to get something so basic to animate. I am attempting to animate the Tab, when it has been clicked on, the flex size increase but in the mobile device, although the size does increase, it is static and no animation is applied. I'm testing this on an Android device.
Nav.js
import React from 'react';
import { View, LayoutAnimation } from 'react-native';
export class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 0
}
}
onTabPress(index) {
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ active: index });
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
render() {
return (
<View style={{ height: 56, elevation: 8, position: 'absolute', left: 0, bottom: 0, right: 0, backgroundColor: this.props.color, flex: 1, flexDirection: 'row' }}>
{
React.Children.map(this.props.children, (child, index) => (
React.cloneElement(child, {
index: index,
active: this.state.active === index,
onTabPress: this.onTabPress.bind(this),
})
))
}
</View>
);
}
}
Tab.js (this.props.children as shown above are a list of Tabs)
import React from 'react';
import { View, Text, TouchableWithoutFeedback, StyleSheet, Animated, Easing, Platform, LayoutAnimation } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
export class Tab extends React.Component {
constructor(props) {
super(props);
}
componentWillUpdate() {
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
handlePress() {
if (this.props.active) return;
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.props.onTabPress(this.props.index);
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
render() {
var active = this.props.active ? { flex: 1.75, top: 6 } : { flex: 1, top: 15 };
return (
<TouchableWithoutFeedback onPress={this.handlePress.bind(this)}>
<View style={[active, { alignItems: "center" }]}>
<Icon size={this.props.iconSize || 24} color={this.props.color || "white"} name={this.props.iconName} />
<Text style={{ color: this.props.color || "white" }}>{this.props.active && this.props.title}</Text>
</View>
</TouchableWithoutFeedback >
);
}
}
I've commented out the LayoutAnimation code blocks to clarify the areas where I have tried. I know i may be doing it wrong but I have followed these guides and it didn't work, so this code here was my attempt at trying to make it work my own way i guess. Thanks in advance.
https://blog.callstack.io/react-native-animations-revisited-part-i-783143d4884
https://medium.com/#Jpoliachik/react-native-s-layoutanimation-is-awesome-4a4d317afd3e
Well, seems that there was nothing wrong with my code. I went on http://snack.expo.io
to test this on ios and the animation worked. When moved to android, it was acting up exactly the way it was for my android device. A quick google search after that lead me to this.
https://github.com/facebook/react-native/issues/5267.
Basically, you just write the following in the constructor of your code and it will work. You can perform further checks on it too to define that this code would only run on android by using Platform.OS and an if statement
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
}

react native - screen cut off at bottom

I'm building a React Native app for the first time using an iOS Simulator with XCode, and it seems that my elements on the bottom of the screen are being cut off, as if the screen can only scroll so far. I've tried changing the overall container View's height, but it doesn't change how far down I can scroll. I've also tried removing the styling, as well as changing View to ScrollView, but neither helped. Another strange thing is that I need to add paddingTop, otherwise, the first Text is displayed over the time in the simulator
Here's the code:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Fields from './components/Fields.js'
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Calculator</Text>
<Text>1099 Calculator</Text>
<Fields />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
paddingTop: 50
},
});
Here's the top of the screen without the padding
And here's how it looks cut off
This is an issue I also had. To fix it just wrap your container with another view without padding but with flex. So like this:
export default class App extends React.Component {
render() {
return (
<View style={styles.main}>
<View style={styles.container}>
<Text>Calculator</Text>
<Text>1099 Calculator</Text>
<Fields />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
paddingTop: 50
},
main: {
flex: 1
}
});
I was facing the same problem, it turned out to be the padding that I added to my ScrollView. Maybe it is not the solution, it is simply to discard ideas..

React Native shows blank screen on phone

after searching for hours I am finally completly lost. I tried to build a simple Dictionary app following an outdated tutorial (https://code.tutsplus.com/tutorials/creating-a-dictionary-app-using-react-native-for-android--cms-24969) for react native. The standard app after I run "react-native init" works fine on my phone. However my code just shows a blank screen without any errors. Below I posted the code, which I used to replace everthing in index.adroid.js. I would really appreciate it, if you could help me here. Thanks in advance!
import React, {Component} from 'react';
import {
AppRegistry,
Text,
View,
TextInput,
StyleSheet,
} from 'react-native';
var english_german = require('./english_german.json');
class Dictionary extends Component {
constructor(props) {
super(props);
this.state = {
input: '',
output: ''
};
}
render() {
return(
<View style={styles.parent}>
<Text>
Type something in English:
</Text>
<TextInput
// style= {{height: 40}}
// placeholder="Type here to translate!"
onChangeText={(text) => this._onTextInputChangeText(text)}
value={this.state.input}
onSubmitEditing={ this.showTranslation().bind(this)} />
<Text style = {styles.germanLabel}>
German translation:
</Text>
<Text style = {styles.germanWord}>
{this.state.output}
</Text>
</View>
);
}
_onTextInputChangeText(text) {
//alert(text);
this.setState({
input : text
})
}
showTranslation() {
var translation = this.state.input in english_german ? english_german[this.state.input] : "Not found";
this.setState({
output: translation
});
}
}
const styles = StyleSheet.create({
// For the container View
parent: {
padding: 16
},
// For the Text label
germanLabel: {
marginTop: 20,
fontWeight: 'bold'
},
// For the Text translation
germanWord: {
marginTop: 15,
fontSize: 30,
fontStyle: 'italic'
}
});
AppRegistry.registerComponent('Dictionary', () => Dictionary);
Thank you guys!
I didn't get the error most of the time, but the syntax error at onSubmitEditing was the problem. For some reason it didn't work (show anything) when I uncommented the whole TextInput. Anyway the fix of Michael Cheng to onSubmitEditing={ this.showTranslation.bind(this)} worked.

Categories