React Native and GraphQL CRUD Operation

This is second part of React Native and GraphQL series. If you are new to GraphQL make sure you check first part here  . In this part we will see how we can Create, Read, Write and Delete data with React Native App  as front end and GraphQL as back-end. Also we will see, how we can use Apollo Client’s new imperative store API and UI up-to-date with back-end data.

In first part of tutorial, we created Listuser component, in which we were listing users by making query to graphql server. Now, we will add some more code to perform edit and delete operation. Below is the updated code of Listuser.js for delete and edit operation :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ActivityIndicator,
  TouchableOpacity
} from 'react-native';
import { graphql, gql } from 'react-apollo'
class Listuser extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isLoading: false     
   }
  }
  _edit(row){
    this.props.startEditMode(row)  
  }  
  _delete = async (id) => {

    this.setState({ isLoading: true})

    let ret = await this.props.deleteUserMutation({
      variables: {
        id
      },
      update: (store, { data: { createUser } }) => {

        const data = store.readQuery({ query: ALL_USERS_QUERY })
        let index = -1;
        const newUserList = data.allUsers.find((user,i ) => {
          if(user.id === id){
            index = i;
            return i;
          }
        })
        if (index > -1) {
            data.allUsers.splice(index, 1);
        }
     
        store.writeQuery({ query: ALL_USERS_QUERY, data })
      }
    })
    this.setState({ isLoading: false})

  }
  renderLoading(){
    return(  <View style={styles.container}>
        <ActivityIndicator styleAttr='Large'/>
      </View>)
  }
  renderError(){
    return(  <View style={styles.container}>
                <Text style={styles.welcome}>
                  An error occured
                </Text>
      </View>)
  }
  renderList(dataArray){

    return(  <View style={styles.container}>
        {
              dataArray.map(row => (
                <View key={row.id} style={{flexDirection : "row"}}>
                <Text style={styles.instructions}>{ row.firstName} {row.lastName}</Text>
                <TouchableOpacity onPress={ this.state.editMode ? () => this._edit(row) : () => this._edit(row) }>
                 <Text>Edit</Text>
                 </TouchableOpacity>
                 <TouchableOpacity onPress={() => this._delete(row.id)}>
                 <Text style={{marginLeft: 10}}>Delete</Text>
                </TouchableOpacity>

                </View>
              ))
        }
      </View>)
  }

  render() {

    if(this.props.allUsersQuery && this.props.allUsersQuery.loading || this.state.isLoading) {
      return this.renderLoading()
    }else if(this.props.allUsersQuery && this.props.allUsersQuery.error) {
      return this.renderError()
    }else{
    const userList = this.props.allUsersQuery.allUsers
    return this.renderList(userList)
   }
  }
}

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

export const ALL_USERS_QUERY = gql`
  query AllUsersQuery {
    allUsers {
      id
      createdAt
      firstName
      lastName
    }
  }
`

const DELETE_USER_MUTATION = gql`
  mutation DeleteUserMutation($id: ID!) {
    deleteUser(id: $id){
      id
    }
  }
`

export default graphql(DELETE_USER_MUTATION, { name: 'deleteUserMutation' })(graphql(ALL_USERS_QUERY, { name: 'allUsersQuery' })(Listuser))

In the above code, we have added edit and delete link and also added delete mutation. Now, when user taps delete link, delete mutation is executed for id of that row. Also, following delete operation, we update the cache so that data deleted is immediately reflected to the user. We are initiating the edit operation in Listuser component but we will complete it in Createuser component.

We have also made changes to the Createuser component. Below is the updated code for the same :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TextInput,
  Button,
  ActivityIndicator
} from 'react-native';
import { ALL_USERS_QUERY } from './Listuser'
import { graphql, gql } from 'react-apollo'
class Createuser extends Component {
  constructor(props) {
    super(props)
    this.state = {
      firstName: '',
      lastName: '',
      isLoading: false,
      confirmedit: false
   }

  }
  update = async () => {
    this.props.setRowNull(null);
  }
  updateUser = async (row) => {
    this.setState({ isLoading: true})
    const id = row.id
    const firstName = this.state.firstName
    let ret = await this.props.updateUserMutation({
      variables: {
        id,
        firstName
      },
      update: (store, { data: { createUser } }) => {

        const data = store.readQuery({ query: ALL_USERS_QUERY })

        const user = data.allUsers.find(user => user.id === row.id)
        user.firstName = this.state.firstName
        store.writeQuery({ query: ALL_USERS_QUERY, data })
      }
    })
    this.props.setRowNull(null);
    this.setState({ isLoading: false, firstName: '', lastName: '',confirmedit: false})
   }
  createUser = async () => {
    this.setState({ isLoading: true})

    const { firstName, lastName } = this.state
    const _this = this
    let ret = await this.props.createUserMutation({
      variables: {
        firstName,
        lastName
      },
      update: (store, { data: { createUser } }) => {
        const data = store.readQuery({ query: ALL_USERS_QUERY })
        data.allUsers.push(createUser)
        store.writeQuery({ query: ALL_USERS_QUERY, data })
      }
    })
    this.setState({ isLoading: false, firstName: '', lastName: ''})
   }  
  _confirmEdit(row){
    this.setState({ firstName: row.firstName, lastName: row.lastName, confirmedit: true})
  }
  _returnButton(row){

    return  (<Button
        onPress={ () => this._confirmEdit(row)}
        title="Confirm Edit"
        color="#841584"
      />)
  }
  render() {
    const row = this.props.editModeRow
    //console.log(row);
    return (
      <View style={styles.container}>
      <Text style={styles.welcome}>
        Welcome to GraphQL!
      </Text>

      <Text style={styles.label}>
        Enter first name
      </Text>
      <TextInput
        style={{height: 40,width: 200, borderColor: 'gray', borderWidth: 1, borderRadius: 10, margin: 10, alignSelf: "center"}}
        underlineColorAndroid={"transparent"}
        onChangeText={(firstName) => this.setState({firstName})}
        value={this.state.firstName}
      />
      <Text style={styles.label}>
        Enter last name
      </Text>
      <TextInput
        style={{height: 40,width: 200, borderColor: 'gray', borderWidth: 1, borderRadius: 10, margin: 10, alignSelf: "center"}}
        underlineColorAndroid={"transparent"}
        onChangeText={(lastName) => this.setState({lastName})}
        value={this.state.lastName}
      />
      { this.state.isLoading ? ( <ActivityIndicator styleAttr='Large'/> ) :
        row ?  this.state.confirmedit? (<Button
            onPress={ () => this.updateUser(row)}
            title="updateUser"
            color="#841584"
          />) : (<Button
            onPress={ () => this._confirmEdit(row)}
            title="Confirm Edit"
            color="#841584"
          />)
       :
      (<Button
        onPress={ () => this.createUser()}
        title="Create User"
        color="#841584"
      />)}

      </View>
    );
  }
}

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

const UPDATE_USER_MUTATION = gql`
  mutation UpdateUserMutation($id: ID!,$firstName: String) {
    updateUser(id: $id,firstName: $firstName){
      id
      firstName
    }
  }
`
const CREATE_USER_MUTATION = gql`
  mutation CreateUserMutation($firstName: String, $lastName: String) {
    createUser(firstName: $firstName,lastName: $lastName) {
      id
      createdAt
      firstName
      lastName
    }
  }
`

export default graphql(UPDATE_USER_MUTATION, { name: 'updateUserMutation' })(graphql(CREATE_USER_MUTATION, { name: 'createUserMutation' })(Createuser))

We take the id of data, of which user taps the edit link, in Listuser component and using that id we update the data in the above code. Update mutation syntax is very similar to Delete mutation syntax.

Finally, we are using Mainuser component to establish communication between Createuser component and Listuser component. Below is the code for Mainuser component :

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';
import { withApollo } from 'react-apollo';
import Createuser from './Createuser'
import Listuser from './Listuser'
class Mainuser extends Component {
  constructor(props) {
    super(props)
    this.state = {
      store : null,
      newu : null,
      row: null,
    }
  }
  _setRowNull(row){
    this.setState({row})
  }
  _startEditMode(row){
      this.setState({row})
  }
  render() {
    return (
      <View style={styles.container}>
        <Createuser editModeRow={this.state.row} setRowNull={(rowVal) => this._setRowNull(rowVal) } />
        <Listuser startEditMode={(row) => this._startEditMode(row) } />
      </View>
    );
  }
}

const styles = StyleSheet.create({
container: {
  flex:1
}
});

export default Mainuser

In the above code, using the state’s of the Main user component, we are passing the row to be edited to Createuser component on edit event of Listuser  component.

Video Tutorial :

 

 

 

2 thoughts on “React Native and GraphQL CRUD Operation”

  1. I’m still learning from you, while I’m making my way to the top as well. I certainly love reading all that is written on your website.Keep the posts coming. I enjoyed it!

Comments are closed.