react-native: Switch Component onValueChange doesnt get called in Android - android

I've been struggling with a weird issue in switch components in React Native when running inside Android app.
Lets say, I have a component which render method looks like this:
render() {
return (
<View>
<View>
<Text>
Test Title
</Text>
<Switch
value={ this.state.value }
onValueChange={
this.test.bind( this )
}
/>
</View>
</View>
);
}
The test method is:
constructor(props){
super(props);
this.state = {
value: true
};
}
test(){
this.setState( {value: !this.state.value})
}
When I run my module inside my iOS app the onValueChange method gets called and everything works as expected, however, when I do the same in my Android app the method never gets called when the value is changed to false. What is more, I cannot change the value more than once i.e I can only set the value to false and it will not allow me to set it to true afterwards. The only way I can play with the switch element again is by holding the bar, nonetheless, the value never gets changed (The switch component doesn't change its color) nor the method called .
Has anyone faced something similar? Is this a issue with RN and its Switch component for Android?
I am using:
react: 15.4.1
react-native: 0.39
***NOTE 1: The onValueChange gets called when I put my RN code inside an activity but it fails when it's inside a fragment.

Try This.
constructor(props){
super(props);
this.state = {
value: true
};
}
and in your render
render() {
return (
<View>
<Text>
Test Title
</Text>
<Switch
value={ this.state.value }
onValueChange={(value) => this.setState({value})}
/>
</View>
);
}
You can remove your test() function

what works for me is this,
constructor(props) {
super(props)
this.state = {
isOpen : false
}
this.onControlChange = this.onControlChange.bind(this);
}
onControlChange(value) {
return this.setState({
isOpen: !this.state.isOpen
});
}
and in return use this way
render() {
return (
<Switch
onValueChange={this.onControlChange}
value={this.state.isOpen}
/>
)
}
so i believe that you should declare binding for your function in constructor. I tested this for Android emulator only.
Hope this helps.

Related

How to setState() depend on which component func is called?

I want to set dynamic state depending on which function is called, I know if we can't setstate in render, but still i need to do that to set dynamic state,
there's some way to make it possible?
export default class Foo extends Component {
constructor(props) {
super(props);
this.state={
dynamicView:false
}
}
renderText(key, value){
this.setState({[key]:value})
<Text>Simple render</Text>
}
renderButton(key, value){
this.setState({[key]:value})
<Text>Simple render</Text>
}
render(){
return(
<View>
{this.state.dynamicView ? this.renderButton("button","ValueButton") : this.renderText("text", "valueText")}
<Button
title="change Component"
onPress={()=>this.setState({dynamicView:!this.state.dynamicView})}
/>
<Button
title="Isi State"
onPress={()=>alert(JSON.stringify(this.state,null,4))}
/>
</View>
)
}
}
with those code I can set dynamic state, but the problem is while both of component function is called, i have two state (button and text), i want to avoid that, so i just have 1 state (button / text) depending on which component is display,
how can i do that?
Note: this is just a simple use-case, all i need to know is to set state depend on which function is called
If you want to keep just either button or text state then you should consider changing the other state.
renderText(key, value){
this.setState({[key]:value})
this.setState({button:false})
<Text>Simple render</Text>
}
I think you need to have another variable in state, and update that based on the function fired up based on the condition of dynamicView.
export default class Foo extends Component {
constructor(props) {
super(props);
this.state={
dynamicView:false,
viewType:''
}
}
renderText(viewType){
this.setState(viewType)
<Text>Simple render</Text>
}
renderButton(viewType){
this.setState({viewType})
<Text>Simple render</Text>
}
render(){
return(
<View>
{this.state.dynamicView ? this.renderButton("button","ValueButton") :
this.renderText("text", "valueText")}
<Button
title="change Component"
onPress={()=>this.setState({dynamicView:!this.state.dynamicView})}
/>
<Button
title="Isi State"
onPress={()=>alert(JSON.stringify(this.state,null,4))}
/>
</View>
)
}
}

How to load the same last component class in react native using react navigation?

My application scenario is like, let say you have three components:
class ComponentOne extends Component {
render() {
return (
<View>
<Text>Component One</Text>
<Button
title='Go To Component Two'
onPress={() => this.props.navigation.navigate('two')}/>
</View>
);
}
}
class ComponentTwo extends Component {
render() {
return (
<View>
<Text>Component Two</Text>
<Button
title='Go To Component Three'
onPress={() => this.props.navigation.navigate('three')}/>
</View>
);
}
}
class ComponentThree extends Component {
render() {
return (
<View>
<Text>Component Three</Text>
<Button
title='Go To Component One'
onPress={() => this.props.navigation.navigate('one')}/>
</View>
);
}
}
export default createStackNavigator({one: ComponentOne,two: ComponentTwo,three: ComponentThree});
Now when I load the app the ComponentOne will be loaded, inside the ComponentOne when I click on the button Go To Component Two it will navigate me to the ComponentTwo, inside ComponentTwo when I click on the button Go To Component Three it will navigate me to the ComponentThree and so on. Now let say I am in ComponentTwo and on the same time I close the application in the phone and I open the app switcher and clean all the running apps and load the same app again, so, it will be again loaded with ComponentOne.
My question is how to program the react navigation to continue from the same component where last time I left, even after cleaning the app from a background (cleaning all apps in app switcher)?
Is there any builtin way in react navigation to do this? Can anyone tell? Examples will be more appreciated. Thanks!
All Navigators have onNavigationStateChange where you can handle the navigation state changing. Example code:
import React from 'react';
import { AsyncStorage } from 'react-native';
import { createStackNavigator, NavigationActions } from 'react-navigation';
const Navigator = createStackNavigator({
ComponentOne: {
screen: ComponentOne,
},
ComponentTwo: {
screen: ComponentTwo,
},
ComponentThree: {
screen: ComponentThree,
},
}, {
initialRouteName: 'ComponentOne',
});
class App extends Component {
constructor(props) {
this.navigator = React.createRef();
}
componentDidMount() {
try {
// Retrieve the last route
const value = AsyncStorage.getItem('lastNavigationRoute').then((result) => {
if (result) {
this.navigator.current.dispatch(NavigationActions.navigate({
routeName: result,
}));
}
});
} catch (e) {
// handle the error
}
}
handleStateChange = (previousState, nextState) => {
// Here we get the Navigate action type only
const navigateAction = NavigationActions.navigate({ routeName: 'dummyRoute' });
if (action.type === navigateAction.type) {
try {
// Saving the last route
AsyncStorage.setItem('lastNavigationRoute', nextState.routeName);
} catch (e) {
// handle the error
}
}
}
render() {
// You could also set a state with loader to handle loading from AsyncStorage
return (
<Navigator onNavigationStateChange={this.handleStateChange} ref={this.navigator} />
);
}
}
How it works:
On every navigation state changing you also save the last routeName
from Navigate action
When component did mount, you check for saved
route in AsyncStorage
If there is a route, you dispatch the navigate action (it's possible to implement replace action as well)
Hope it helps.
i dont think that there is a solution directly using react-navigation.
What i think you could do is to save a value of the current screen to the storage of the phone and then use this value on app start to detect which screen to show

Autofocus SearchBar didn't work on Android

I use React Native Element's SearchBar component for my app.
I set the autoFocus = {true}.
It works fine on iOS, but not on Android.
Any idea how to solve this ?
Here the working code sample.
// imports
....
....
class Abc extends Component {
componentDidMount() {
this.searchBar = true; // Manual Method
}
render() {
return (
<View>
<SearchBar
autoFocus // Automatic Method
ref={(searchBar) = { this.searchBar = searchBar; }}
/>
</View>
);
}
}
Your approach is correct. I have given the manual method but I suggest you go with your method. Also make sure you have passed the autoFocus prop to your <TextInput /> wrapped inside your SearchBar component.
Eg:
const SearchBar = (props) => {
const { prop1, prop2, ...txtIpProps } = props;
return (
<SearchBar>
<TextInput {...txtInpProps}/>
</SearchBar>
)
};

Change a button color by using onPress on React Native

I would like to have button change its color when pressed. I tryed checking out other similar topics but I couldn't find a solution. The code renders and the initial button color is red, but when I press it, nothing happens.
export default class someProgram extends Component {
render() {
var buttonColor = "red";
function changeButtonColor(){
if(this.buttonColor === "red"){
this.buttonColor = "green";
}
else{
this.buttonColor = "red";
}
}
return (
<View style={styles.container}>
<Button
title="Press me!"
color={buttonColor}
onPress={() => {changeButtonColor(buttonColor)}}
/>
</View>
);
}
}
You should keep track of the color in the component state. As a side, make sure to understand what the keyword this really means. Do a console.log(this) and see it for yourself.
Anyway, you can
(1) set the initial state in the constructor;
(2) access value from the state using this.state.someProp
then (3) adjust the state later using this.setState({ someProp: someValue }).
1) Initial State
constructor(props) {
super(props);
this.state = {
buttonColor: 'red'; // default button color goes here
};
}
2) Accessing the State &
3) Setting New State
onButtonPress = () => {
this.setState({ buttonColor: 'someNewColor' });
}
render() {
// ...
return (
{/* ... */}
<Button
color={this.state.buttonColor}
onPress={onButtonPress}
/>
)
Note some parts of the code were omitted to focus on the question at hand.

react-native navigator not updating scene

I'm trying to get navigating working with react-native. I am testing it with 2 pages (HomePage & OtherPage) and want HomePage to be able to nav to OtherPage when I submit some text.
My basic navigator index.android.js file...
class MyApp extends Component {
_renderScene = function(route, nav) {
switch (route.index) {
case 'HomePage':
console.log('homepage nav')
return <HomePage navigator={nav} />
case 'OtherPage':
console.log('otherpagenav')
return <OtherPage navigator={nav} />
}
}
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 'HomePage' }}
renderScene={ (route, nav) => { return this._renderScene(route, nav) } }
/>
)
}
}
Both pages HomePage and OtherPage work if I set them manually via the index and initialRoute.
initialRoute={{ title: 'My Initial Scene', index: 'HomePage' }}
or
initialRoute={{ title: 'My Initial Scene', index: 'OtherPage' }}
My HomePage.js file which is loaded fine, looks like...
class HomePage extends Component {
constructor(props) {
super(props);
}
_onSubmit() {
console.log( 'submit2', this);
this.props.navigator.push({
title: "Other Page",
index: 'OtherPage',
component: OtherPage,
passProps: { testprop: 'someuser'},
});
}
render() {
return (
<View>
<Text>Current Scene: {this.props.title}</Text>
<TextInput
onChangeText = {( keywords ) => this.setState({ keywords })}
defaultValue = 'Type a keyword'
onSubmitEditing = { this._onSubmit.bind( this ) }
/>
</View>
)
}
}
My Test OtherPage is just
class OtherPage extends Component {
constructor( props ) {
super( props );
}
componentDidMount() {
console.log( 'otherpage component mounted');
}
render() {
return (
<View><Text>Other Page</Text>
</View>
);
}
}
When I enter some text and submit. I see the submit console.log from _onSubmit() So I know the navigator is getting called ok.
I also see 'otherpage component mounted'.
So everything looks ok log wise, but I don't see the scene/page updated, all I see is the updated console.logs.
Is there something I need to do to force it to update the scene, or shouldn't it happen automatically with the navigator push ? (I'm also a little unsure of the need for navigator component, as I've seen it in some example, but not really referenced anywhere).
Looks like there was something odd with the avd. I ended up upgrading react-native to 0.39.2 (which I don't think made a difference, but including for completeness). Then I deleted the avd, created a mew one (target name Android 6.0 Platform 6.0 API 23 Intel Atom x86_64) selected delete all user data, and then it seems to work.
Wish I knew specifically why, so if anyone has a better answer, I will gladly accept.

Categories

Resources