How to build a dating app using React Native and Firebase – part 1

In this first part of “build a dating app using React Native and Firebase” article we will see how we can filter user by their age. We will not be discussing the basics of building this app in this article, as, I have already discussed the basics in my previous articles. So. lets start with the age filter feature.

First lets see whats already working in the app. The users login with Facebook and user data is getting saved in firebase database (http://calsob.in/how-to-use-facebook-login-in-react-native-using-firebase). In the user settings screen we have a Age button which navigates to Age filter component/screen. So, when user clicks on the button it navigates to Age screen with his login data as parameter. Here is the code for that :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Button
} from 'react-native';
import firebase from 'firebase'
export default class Profile extends Component {
  constructor(props) {
   super(props)
   this.state = {
     user: this.props.screenProps   
   }
  }
  componentDidMount(){ 
    const { uid } = this.state.user
       this.firebaseRef = firebase.database().ref('users')
       this.firebaseRef.child(uid).on('value', snap => {
         const user = snap.val()
         this.setState({ user: user })
     })
  }
  render() {

    return (
      <View style={styles.container}>       
        <Button
          onPress={() => this.props.navigation.navigate('Age', { user: this.state.user })}
          title={ "Age("+this.state.user.ageRange[0]+"yrs "+"to"+this.state.user.ageRange[1]+"yrs)" }
        />        
      </View>
    );
  }

}


const styles = StyleSheet.create({
 container: {
 flex: 1,
 justifyContent: 'center',
 alignItems: 'center',
 backgroundColor: '#F5FCFF',
 },
 welcome: {
 fontSize: 20,
 textAlign: 'center',
 margin: 10,
 },
 instructions: {
 textAlign: 'center',
 color: '#333333',
 marginBottom: 5,
 },
});

In the Age screen, we have two slider component. These slider components allows user to set a age range between which members of the app will be filtered. Also we validate that bottom slider value is never greater than the top slider value. Here is the code for Age component where all age filtering is taking place :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Slider
} from 'react-native';
import firebase from 'firebase'
export default class Age extends Component {
  constructor(props) {
   super(props)
   this.state = { user: this.props.navigation.state.params.user, agefrom: 18, ageto:71}

  }
  componentDidMount(){
    const { ageRange } = this.state.user
    this.setState({ agefrom: ageRange[0],ageto: ageRange[1] })
    this.agefrom = ageRange[0]
    this.ageto = ageRange[1]
  }
  getValfrom(val){
      if(val<this.ageto){
      const { uid } = this.state.user
      const userData = {
          ageRange: [val, this.ageto ]
        }
      this.agefrom = val
      firebase.database().ref('users').child(uid).update({ ...userData})
    }else{
      this.setState({ agefrom: this.agefrom })
    }
  }
  getValto(val){
    if(val>this.agefrom){
      const { uid } = this.state.user
      const userData = {
          ageRange: [this.agefrom, val]
        }
      this.ageto = val
      firebase.database().ref('users').child(uid).update({ ...userData})
    }else{
      this.setState({ ageto: this.ageto })
    }
  }
  render() {
    return (
      <View style={styles.container}>
      <Text style={styles.instructions}>
        age from :
      </Text>
        <Slider
          style={{ width: 300 }}
          step={1}
          minimumValue={18}
          maximumValue={71}
          value={this.state.agefrom}
          onValueChange={val => val<this.ageto ? this.setState({ agefrom: val }) :  this.no = 0 }
          onSlidingComplete={ val => this.getValfrom(val)}
        />

        <Text style={styles.welcome}>
          {this.state.agefrom}
        </Text>
        <Text style={styles.instructions}>
          age to :
        </Text>
        <Slider
          style={{ width: 300 }}
          step={1}
          minimumValue={18}
          maximumValue={71}
          value={this.state.ageto}
          onValueChange={val => val>this.agefrom ? this.setState({ ageto: val }) : this.no = 0 }
          onSlidingComplete={ val => this.getValto(val)}
        />
        <Text style={styles.welcome}>
          {this.state.ageto}
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

Now, in the Match screen we are listing the filtered user using a list view and we are using lodash component to filter the user by there age. Also, to convert date of birth to number of years we have used component momentjs.  The list is bound to firebase on even so that as soon as user make changes in age range,  there is change in  members list. Here is the code for Match screen :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
  Image
} from 'react-native';
import _ from 'lodash'
import moment from 'moment'
import firebase from 'firebase'
export default class Match extends Component {
  constructor(props) {
   super(props)
   const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
   this.state = {
     user: this.props.screenProps,
     dataSource: ds.cloneWithRows([]),
    }
  }
    componentDidMount(){

    const { uid } = this.state.user
    this.firebaseRef = firebase.database().ref('users')
    this.firebaseRef.on('value', snap => {

       let users = snap.val();

        const rejectMe = _.reject(users, user => user.uid === uid)
        const filterMe = _.filter(users, user => user.uid === uid)
        const user = filterMe[0];
        /* Age filter start */
        const userBday = moment(user.birthday, 'MM/DD/YYYY')
        const userAge = moment().diff(userBday, 'years')
        const filterByAge = _.filter(rejectMe, profile => {
        const profileBday = moment(profile.birthday, 'MM/DD/YYYY')
        const profileAge = moment().diff(profileBday, 'years')
        const inRangeUser = _.inRange(profileAge, user.ageRange[0], user.ageRange[1] + 1)
        const inRangeProfile = _.inRange(userAge, profile.ageRange[0], profile.ageRange[1] + 1)
        return inRangeUser && inRangeProfile
      })
      const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
      this.setState({ dataSource: ds.cloneWithRows(filterByAge) })
    })
  }
  renderRow(rowData, sectionID, rowID) {
    return (
    <View style={styles.rowContainer}>
    <Image source={{ uri: rowData.dp }} style={styles.photo} />
    <Text style={styles.text}>
      {`${rowData.first_name} ${rowData.last_name}`}
    </Text>
  </View>
)
  }
  render() {

    return (
      <ListView
        enableEmptySections={true}
        removeClippedSubviews={false}
        style={styles.container}
        dataSource={this.state.dataSource}
        renderRow={this.renderRow.bind(this)}
      />
    );
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 20,
  },
  rowContainer: {
    flex: 1,
    padding: 12,
    flexDirection: 'row',
    alignItems: 'center',
  },
  text: {
    marginLeft: 12,
    fontSize: 16,
  },
  photo: {
    height: 40,
    width: 40,
    borderRadius: 20,
  },
});

Here is the demo of how the age filter works :