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

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

Related

How to change value of input react native

Sorry I am new to React Native, and want to know how to change current input value?
As in my case, if I enter a new word directly into input the previous word or the previous value in the value will continue to appear without changing or replacing the new one.
class component:
Keep the value of the input in state of your component which holds this TextInput component.
class ParentComponent extends React.Component {
constructor (props) {
super(props)
this.state = { queryText: '' }
}
handleInputTextChange = (newText) => {
this.setState({ queryText: newText })
}
render () {
return (<View>
<TextInput
onChangeText={this.handleInputTextChange}
value={this.state.queryText}
/>
</View>)
}
}
Notice How I have used onChangeText and handleInputTextChange to handle new values.
Functional Component:
in the functional components, we use hooks. To hold and update text value we use useState
export default () => {
const [text, setText] = useState("");
return <TextView value={text} onChangeText={setText} />;
};
Hello you can use this method :
this.state = {
email: '13119165220',
}
onChangeText={text => this.setState({ email: text })}
In functional components use
export default () => {
const [text,setText] = React.useState("")
return <TextView
value={text}
onChangeText={setText} />
}
TextInput needs value that it is the value that is gonna be shown inside the TextInput.
And to update that value you use onChangeText that is gonna call whatever function you specify every time the text into the TextInput change.
Depending if you are learning React with hooks or without your code will change:
with hooks:
import React,{useState} from 'react'
//others import
function MyTextInput (props){
const [userInput,setUserInput] = useState()
return (
<TextInput
value = {userInput}
onTextChange = {(text) => setUserInput(text)} /> //is always better call another function
) // where you can validate the input
if your using class and coding without hooks, the logic is the same, just change the syntax:
import React,{Component} from 'react'
//other imports
class MyTextInput extends Component{
constructor(props){
super(props)
this.state = {
userInput:''
}
render(){
return (
<TextInput value = {this.state.userInput}
onChangeText = { text => this.setState({userInput:text}) />
)
}
}
Here the links for the docs, where you can find all the props that TextInput receive with explanation: https://reactnative.dev/docs/textinput

Map object to generate Picker.Item instances in React Native triggers a "TypeError: undefined is not an object"

My code is based off of this Stack Overflow question: React Native apply array values from state as Picker items
I used Manjeet Singh's first solution (for mapping an Object to Picker.Item instances) since I'm storing my data in an Object.
However, when I use his method, I'm getting a "TypeError: undefined is not an object (evaluating 'child.props.value')" error.
My code looks like:
class View extends React.Component{
state = { exercise: '' };
arrItems = {"Badminton":"7", "Basketball":"9.3", "Biking":"8"};
render(){
<Picker selectedValue={this.state.exercise}
onValueChange={(itemValue, itemIndex)=>this.setState({ exercise: itemValue })>
{Object.keys(this.arrItems).map((key) => {
return (<Picker.Item label={key} value={key} key={key}/>)
})}
}
</Picker>
}
I'm pretty new to React Native -- what is the issue causing this error?
some small tips:
Try to set your variables to the local state (this.state), at the
moment I don't think you can reach your variables. You set state inside the construtor, the constructor (as shown below) and super() lines are necessary.
You are missing a closing tag at the end of the onValueChange prop.
{ return(statement) } equals (statement), this only works if single
line.
You can use .map((item, index) => to get the index of the item as well, so that you can use it as a key prop in the Item.
Try this:
class View extends React.Component {
constructor() {
super();
this.state = {
exercise: '',
arrItems: { "Badminton": "7", "Basketball": "9.3", "Biking": "8" }
}
}
render() {
return (
<Picker selectedValue={this.state.exercise}
onValueChange={(itemValue, itemIndex) => this.setState({ exercise: itemValue })}>
{Object.keys(this.state.arrItems).map((key, index) =>
<Picker.Item label={key} value={key} key={index} />
)}
</Picker>
)
}
}
Also you might want to rename your Class to something different than View, as you will probably use the built in View a lot (can only have one).

`componentDidMount()` function is not called after navigation

I am using stackNavigator for navigating between screens. I am calling two API's in componentDidMount() function in my second activity. When i load it first time, it gets loaded successfully. Then i press back button to go back to first activity. Then, if i am again going to second activity, the APIs are not called and I get render error. I am not able to find any solution for this. Any suggestions would be appreciated.
If anyone coming here in 2019, try this:
import {NavigationEvents} from 'react-navigation';
Add the component to your render:
<NavigationEvents onDidFocus={() => console.log('I am triggered')} />
Now, this onDidFocus event will be triggered every time when the page comes to focus despite coming from goBack() or navigate.
If the upvoted syntax that uses NavigationEvents component is not working, you can try with this:
// no need to import anything more
// define a separate function to get triggered on focus
onFocusFunction = () => {
// do some stuff on every screen focus
}
// add a focus listener onDidMount
async componentDidMount () {
this.focusListener = this.props.navigation.addListener('didFocus', () => {
this.onFocusFunction()
})
}
// and don't forget to remove the listener
componentWillUnmount () {
this.focusListener.remove()
}
The React-navigation documentation explicitly described this case:
Consider a stack navigator with screens A and B. After navigating to
A, its componentDidMount is called. When pushing B, its
componentDidMount is also called, but A remains mounted on the stack
and its componentWillUnmount is therefore not called.
When going back from B to A, componentWillUnmount of B is called, but
componentDidMount of A is not because A remained mounted the whole
time.
Now there is 3 solutions for that:
Subscribing to lifecycle events
...
React Navigation emits events to screen components that subscribe to
them. There are four different events that you can subscribe to:
willFocus, willBlur, didFocus and didBlur. Read more about them in the
API reference.
Many of your use cases may be covered with the withNavigationFocus
higher-order-component, the <NavigationEvents /> component, or the
useFocusState hook.
the withNavigationFocus
higher-order-component
the <NavigationEvents />
component
the useFocusState hook (deprecated)
withNavigationFocus
higher-order-component
import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';
class FocusStateLabel extends React.Component {
render() {
return <Text>{this.props.isFocused ? 'Focused' : 'Not focused'}</Text>;
}
}
// withNavigationFocus returns a component that wraps FocusStateLabel and passes
// in the navigation prop
export default withNavigationFocus(FocusStateLabel);
<NavigationEvents /> component
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus', payload)}
onDidFocus={payload => console.log('did focus', payload)}
onWillBlur={payload => console.log('will blur', payload)}
onDidBlur={payload => console.log('did blur', payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
useFocusState hook
First install library yarn add react-navigation-hooks
import { useNavigation, useNavigationParam, ... } from 'react-navigation-hooks'
function MyScreen() { const focusState = useFocusState(); return <Text>{focusState.isFocused ? 'Focused' : 'Not Focused'}</Text>; }
Here is my personal solution, using onDidFocus() and onWillFocus() to render only when data has been fetched from your API:
import React, { PureComponent } from "react";
import { View } from "react-native";
import { NavigationEvents } from "react-navigation";
class MyScreen extends PureComponent {
state = {
loading: true
};
componentDidMount() {
this._doApiCall();
}
_doApiCall = () => {
myApiCall().then(() => {
/* Do whatever you need */
}).finally(() => this.setState({loading: false}));
};
render() {
return (
<View>
<NavigationEvents
onDidFocus={this._doApiCall}
onWillFocus={() => this.setState({loading: true})}
/>
{!this.state.loading && /*
Your view code
*/}
</View>
);
}
}
export default MyScreen;
Solution for 2020 / React Navigation v5
Inside your screen's ComponentDidMount
componentDidMount() {
this.props.navigation.addListener('focus', () => {
console.log('Screen.js focused')
});
}
https://reactnavigation.org/docs/navigation-events/
Alternatively: Put the addListener method in constructor instead to prevent duplicated calls
React-navigation keeps the component mounted even if you navigate between screens. You can use the component to react to those events :
<NavigationEvents
onDidFocus={() => console.log('hello world')}
/>
More info about this component here.
According to react-navigation docs we can use as below
componentDidMount () {
this.unsubscribe= this.props.navigation.addListener('focus', () => {
//Will execute when screen is focused
})
}
componentWillUnmount () {
this.unsubscribe()
}
Similar to vitosorriso`s answer but should changed didFocus to focus according to docs
You want to perform something after every time navigating to a component using drawernavigator or stacknavigator ; for this purpose NavigationEvents fits better than componentDidMount .
import {NavigationEvents} from "react-navigation";
<NavigationEvents onDidFocus={()=>alert("Hello, I'm focused!")} />
Note : If you want to do the task every time after focusing on a component using drawer navigation or stack navigation then using NavigationEvents is better idea. But if you want to do the task once then you need to use componenetDidMount .
//na pagina que você quer voltar
import {NavigationEvents} from 'react-navigation';
async atualizarEstado() {
this.props.navigation.setParams({
number: await AsyncStorage.getItem('count'),
});}
render() {
return (
<View style={styles.container}>
<NavigationEvents onDidFocus={() => this.atualizarEstado()} />
</View>
);
}
I have face this issue, the problem is when you navigate a page, the first time it call constructor, componentWillmount, render componentDidmount,
but in second time when navigate to the same page it only call render, so if you do any API call or something from componentDidmount it would not be called,
and also componentWillunmount never called.
You can use this method, if you are using react-navigation 5.x with class component, it can solve your problem.
for every class component page add this method and call this method once from the constructor
constructor(props) {
super(props);
this.state = {
...
};
...
this.navigationEventListener(); // call the function
}
navigationEventListener = () => { // add this function
let i = 0;
const initialState = this.state;
this.props.navigation.addListener('focus', () => {
if (i > 0) {
this.setState(initialState, () => {
//this.UNSAFE_componentWillMount(); // call componentWillMount
this.componentDidMount(); // call componentDidMount
});
}
});
this.props.navigation.addListener('blur', () => {
this.componentWillUnmount(); //call componentWillUnmount
++i;
});
}
https://reactnavigation.org/docs/navigation-events/
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
// do something
// Your apiCall();
});
return unsubscribe;
}, [props.navigation]);
In React, componentDidMount is called only when component is mounted.I think what you are trying to do is call your API on going back in StackNavigator. You can pass a callback function as parameter when you call navigate like this on Parent Screen:
navigate("Screen", {
onNavigateBack: this.handleOnNavigateBack
});
handleOnNavigateBack = () => {//do something};
And on Child Screen
this.props.navigation.state.params.onNavigateBack();
this.props.navigation.goBack();

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: Switch Component onValueChange doesnt get called in 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.

Categories

Resources