React Native Execute a Function on State Change - reactjs

I am wanting to fire a function when state changes in my app. I have a modal with a button that changes state.
My home component (edited)
...
constructor(props) {
super(props);
this.state = {
color:'red',
modalVisible: false,
}
}
...
render() {
return (
<View>
<Modal
animationType={"slide"}
transparent={false}
visible={this.state.modalVisible}
>
<View style={{marginTop: 22}}>
<TouchableOpacity onPress={()=>this.setState({color:'blue'})}>
<Text>Blue</Text>
<TouchableOpacity>
<TouchableOpacity onPress={()=>this.setModalVisible(false)}>
<Text>Blue</Text>
<TouchableOpacity>
</View>
</Modal>
<Text style={{color:this.state.color}}>
Foo
</Text>
</View>
)
...
However when I close the modal, the color of my text is not changed to blue. It stays red until I open the modal back up, and then close again. Its like the state of 'color' is not being updated. I have ran into this before using redux and 'componentWillReceiveProps(nextProps)' but this has stumped me being in the same component.
I am using the native modal component from the docs. https://facebook.github.io/react-native/docs/modal.html

Touchable? TouchableHighlight?
I have not found any problem in your code except TouchaleHighlight tag.
https://rnplay.org/apps/fDGs3A

Related

How to Change Title Attribute in React Native Button Component

I have a few React components that look something like this within an app:
<View style={{margin: 5}}>
<Button title='Enable Headlights' color='#00759A' onClick={doChanges}/>
</View>
I want the Button component to change its color and text whenever its clicked. I was planning on having the function doChanges do this, but I can't figure out a way to modify the title and color attributes. I've seen custom made components that make these kinds of changes in state by calling setState within the component, but Button isn't a component that I made. I tried doing something along the lines of:
var text = "Enable headlights"
function doChanges(text) {
text = "Disable headlights"
}
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
But that doesn't seem to work the way I want it to. I think it's because even though the variable text is changing, the actual component isn't rerendering, so the changes in text never actually appear.
There doesn't seem to be a way to force the component to rerender, and I don't see how there's a way I can modify the title and color attributes of the Button component.
Do I have to make a custom component that's subclassed from Button in order to get the functionality I want? Or is there a specific way for how I'm supposed to go about doing something like this in React?
Any help would be appreciated!
If you are supposed to make changes at the UI level then you should use the state to render the updated view.
like:
If you are using Class Component:
class Demo extends Component {
constructor(props) {
super(props)
this.state = {
buttonText: "Enable headlights"
}
this.doChanges = this.doChanges.bind(this);
}
function doChanges(text) {
this.setState({ buttonText: "Disable headlights" });
}
render() {
return (
<View style={{margin: 5}}>
<Button title={this.state.} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
}
If you are using Functional Component:
const Demo = props => {
const [buttonText, setButtonText] = useState("Enable headlights")
function doChanges(text) {
setButtonText("Disable headlights");
}
return (
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
check out more about the state:
https://reactjs.org/docs/state-and-lifecycle.html

React change class attributes

I'm trying to change class attribute in a function, I have a button and I want to change the text as soon as the user clicks it.
I wrote a class and everything but when the user clicks the button nothing happens!
I console logged to see if the variables actually changed and they did so why does it happen?
Here is my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
buttonStatus: 'Post room',
value: '',
}
}
PostANewRoomScreen = () => {
let {data} = '';
return (
<View style={{padding: 15}}>
<TouchableOpacity
style={styles.button}
disabled={this.state.loading}
onPress={() => this.postroom(data)}
>
<Text style={{color: "white" , fontSize: hp('2.5%'), padding: 5}}>
{this.state.loading && <Animated.Image style={{height: hp('3%'), width: wp('2%')}} source={{uri: 'https://media0.giphy.com/media/JTVkOqJ1RyYEBnyoRb/giphy.gif'}} />}
{this.state.buttonStatus}
</Text>
</TouchableOpacity>
</View>
postroom = (value) => {
this.state.loading = true;
this.state.buttonStatus = "button clicked!";
}
this.state.loading = true;
this.state.buttonStatus = "button clicked!";
This does not re render component. You have to use useState Hook (in funcation component) or setState() (in class component) in order to update the state. I suggest converting component to functional component and use hooks to manage state.
const [buttonStatus, setButtonStatus] = useState('Post Room');
then use setButtonStatus(value) inside your postroom function.
Please refer react doc for useSate Hooks
Further, Your existing code has some issues. you are trying to access and mutate state of App component. if you need to share the component state try to lift the state up. If you need to pass properties to component use props.

TouchableOpacity onPress not working(emulator test)

So just getting started with react-native. I have a stateful component built as below :
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
inProgress: false
};
}
processLogin(){
this.setState({
inProgress: true
})
}
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.loginContainer}>
<TextInput style={styles.input} placeholder="Password" />
<TouchableOpacity style={styles.button} onPress={() => this.processLogin()}>
{!this.state.inProgress && <Text style={styles.button_text}>Log in</Text>}/>}
</TouchableOpacity>
</View>
</View>
);}}
As you can see, i am binding the onPress event of the touchable opacity to the function processLogin. But for some reason, the function isn't fired!
The state doesn't change, also tried console.log but didn't fire either. Made sure that TouchableOpacity is imported from React-Native.
I am guessing that i messed up the binding. Any leads would be appreciated!
Note: Have only been trying this in the emulator, not in a real device yet.
instead of using the arrow function on onPress prop, use it when you are declaring the business function processLogin
So concretly, replace the declaration of this method by :
processLogin = () => {
this.setState({
inProgress : true
})
}
Then on onPress method, just change make it like this :
onPress = {this.processLogin}
Contrarily, you refer to the wrong this
Hope it's gonna help you sir
Regards.

React Native: Invariant Violation: Text strings must be rendered within a <Text> component

Alright, so I'm a bit new to react, and I've made a sample login screen like so:
export default class App extends Component{
constructor(props){
super(props);
this.state = {
login: '',
password: '',
// exists: false,
Button: './submitButton.png',
}
}
render() {
return (
<View>
//Login form here
<View style={styles.container}>
<TextInput
styles = {styles.container}
placeholder = "Login"
onChangeText = {(login) => this.setState({login})}
/>
<TextInput
styles = {styles.container}
placeholder = "Password"
onChangeText = {(password) => this.setState({password})}
/>
</View>
//Button here
<View style={styles.container}>
<TouchableHighlight
onPress = {() => Alert.alert("Alert!")}
>
<Image
style={styles.button}
source={require('./submitButton.png')}
/>
</TouchableHighlight>
</View>
</View>
);
}
}
But I get the error below:
Originally I had used a Button instead of TouchableHighlight because as I understand it, react-native 0.56 (the version I'm using) has a bug regarding buttons, and apparently gives the same issue as above, and the workaround was supposedly to use a TouchableHighlight or TouchableOpacity. But switching to a TouchableOpacity didn't really seem to fix anything.
What do? I've scoured google but I can't seem to find a solution.
(If there's no hope, is there a way to downgrade my installation of react without breaking nearly everything?)
In JSX, comment like //Login form here doesn't work. It will be treated as a text.
You have to change your comments into the shape like below
{/* Login form here */}

props data is not working in componentDidMount using react-native

I have been struggling for the last 24 hrs about this one.
The problem is that, I am passing props in my parent class and calling the props data in my child component. When I check in render method the data shows correctly, but when calling it in componentDidMount it shows null.
Here is the sample of my code
class parent extends React.Component{
constructor(props){
super(props);
this.state = {
isOpen: false,
day:'monday',
data:[],
data1:[]
}
}
back () {
this.props.navigator.pop();
}
Mon(){
var record=[];
this.state.data.map((data) => {
if(data.frequency[0].day == 'Monday'){
record.push(data);
}
});
this.setState({data1:record});
}
componentWillMount() {
this.fetchData();
}
fetchData() {
//here i am getting the data from service and set the data to the state
}
render(){
return(
<View style={styles.Maincontainer}>
<View style={styles.navbar}>
<View style={styles.navbarContainer}>
<View style={styles.navbarIcon}>
<TouchableOpacity style={styles.button} onPress={() => this.back()}>
<Image
style={styles.menu2}
source={require('image!nav_arrow')} />
<Text style={styles.navbuttonText}>Back</Text>
</TouchableOpacity>
</View>
<View style={styles.navbarText}>
<Text style={styles.title}>Title</Text>
</View>
<View style={styles.navbarButton}>
</View>
</View>
</View>
<View style={styles.subMainContainer}>
<View style={styles.weekHeader}>
<View style={styles.weekRow}>
<Text style={styles.text} onPress={this.Mon.bind(this)}>Mon</Text>
</View>
</View>
<View style={styles.listBody}>
{this.state.data1.length == 0 ?(
<List data={this.state.data} />
) : (
<List data={this.state.data1}/>
)}
</View>
</View>
</View>
);
}
}
In my child component, I want to display the data in a ListView which is taken from the parent component
class List extends React.Component{
constructor(props){
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
isLoading: true
};
}
componentDidMount(){ //**here props data shows empty**
alert("didMount:"+JSON.stringify(this.props.data));
var data=this.props.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
isLoading: false
});
}
render() { //**In this alert it shows correct data**
alert("data:"+JSON.stringify(this.props.data));
if (this.state.isLoading) {
return this.renderLoadingView();
}
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSchedule.bind(this)}
automaticallyAdjustContentInsets={false}/>
);
}
renderLoadingView(){
return(
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading Schedule...
</Text>
</View>
);
}
renderSchedule(data){
return(
<View style={styles.ListContainer}>
<View style={styles.subContainer}>
<Text style={styles.classText}>Class Name: {data.name}</Text>
{data.frequency[0].day == null ? (
<Text style={styles.Day}>Day: {data.frequency[0].date}</Text>
):
<Text style={styles.Day}>Day: {data.frequency[0].day}</Text>
}
</View>
<View style={styles.subContainer1}>
<Text style={styles.startTime}>Start: {data.frequency[0].from}</Text>
<Text style={styles.endTime}>End: {data.frequency[0].to}</Text>
</View>
</View>
)
}
}
Here what i want is that initially i need to display the total data,when click on the text parent class the data need to be changed.I want to show the data in ListView.I already explain above about my problem,So can any one give me suggestions that how to solve my problem, Any help much appreciated
My guess would be that the alert in componentDidMount() is empty because it only called once, at a time when data is still empty, while render() is called on each change of state or props.
I would expect the flow of alerts to look something like this:
Alert called from child's render: "data: (empty)"
Alert called from child's didMount: "didMount:(empty)"
User clicks something in parent, state change, re-render triggered
Alert called from child's render: "data: FOO-content"
React does NOT call componentDidMount() on the second pass. If you want your datasource to be populated on EACH update of the child, you will need to add a componentDidUpdate() method to your child, with copy of the code you have inside componentDidMount().
As a general note: I would advise you to read React's explanation of component lifecycle and methods. I have a feeling that the preparation you are doing inside componentDidMount() is probably better placed elsewhere:
creating a DataSource and initial fill with data from props: in getInitialState()
updating Datasource with data from new props: in componentWillReceiveProps()
I found in React generally that if I want to prepare the initial state of my component based on this.props then I should do it in componentDidUpdate instead of componentDidMount. Components aren't mounted with populated props from the parent component, only updated with them.
I noticed this when doing normal React and doing console.log(this.props) in the render method of my child component. It would log initally as empty, then log again populated with the props. If I called a this.setState in componentDidMount then the population of this.props occurs in the third render. This is because calling this.setState in componentDidMount triggered a pre-update render.
I hope this makes sense and applies to your situation - I arrived at your question looking for a solution to similar behaviour.

Resources