React-Native

How to getting started with Joyfill in a React-Native or Expo application

Overview

We currently support any React-Native bare and Expo setup.

Guide

Setup

Getting Started Guide

Project Requirements and Dependencies

View Package README

Implement your code

🚧

Do not wrap JoyDoc component inside of a ScrollView. JoyDoc rendering optimizations will not working properly inside of ScrollView and will introduce unintended bugs.

Below is a usable example of our react-native SDK. This will show a readonly or fillable form view depending on the mode you use. Learn more about modes here .

Make sure to replace the userAccessToken and documentId at the top of the Document.js file. documentId represents the ID of your Joyfill mobile form. Note that the documentId can be retrieved using the Joyfill Manager or by using the List all documents API.

import React from 'react';
import {SafeAreaView, StatusBar, StyleSheet} from 'react-native';

import Document from './Document';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <StatusBar />
      <Document />
    </SafeAreaView>
  );
}

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

export default App;

import React, {useState, useEffect} from 'react';
import {
  Dimensions,
  StyleSheet,
  ActivityIndicator,
  Text,
  View,
  TouchableOpacity,
} from 'react-native';

import {JoyDoc} from '@joyfill/components-react-native';
import {joyfillSave, joyfillGenerate, joyfillRetrieve} from './api.js';

const screenWidth = Dimensions.get('window').width;

const userAccessToken = '<REPLACE_ME>';
const documentId = '<REPLACE_ME>';

const FormModes = {
  fill: 'fill',
  readonly: 'readonly',
};

function Document() {

  const [mode, setMode] = useState(FormModes.fill);
  const altMode = mode === FormModes.readonly ? FormModes.fill : FormModes.readonly;

  const [doc, setDoc] = useState(null);
  const [loading, setLoading] = useState(false);


  /**
   * Retrieve document from Joyfill api. Don't forget to add documentId and userAccessToken
   */
  useEffect(() => {
    retrieveJofillDocument();
  }, []);

  const retrieveJofillDocument = async () => {
    setLoading('retrieving document...');
    const response = await joyfillRetrieve(documentId, userAccessToken);
    setDoc(response);
    setLoading(null);
  };

  /**
   * Handle saving document changes to Joyfill api. 
   */
  const saveJoyfillDocument = async () => {

    setLoading('saving document & generating pdf...');

    const response = await joyfillSave(doc, userAccessToken);
    console.log('>>>>>>>>>>>>>>>> repsonse: ', response);

    setDoc(response);
    setLoading(null);

    //Uncomment below to see pdf download
    //const downloadableLink = await joyfillGenerate(doc.identifier, userAccessToken);
    //console.log(downloadableLink);

  };

  return (
    <>
      {loading && (
        <View style={styles.loading}>
          <ActivityIndicator />
          <Text>Loading...</Text>
        </View>
      )}
      <View style={styles.row}>
        <Text style={styles.title}>{doc?.name || 'Joyfill Form'}</Text>
      </View>
      <View style={styles.row}>
        <TouchableOpacity
          style={styles.button}
          onPress={() => setMode(altMode) }
        >
          <Text style={styles.buttonText}>{`Set to ${altMode}`}</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={saveJoyfillDocument} style={styles.button}>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableOpacity>
      </View>

      {doc && (
        <View style={styles.form}>
          <JoyDoc
            mode={mode}
            doc={doc}
            width={screenWidth}
            onChange={(params, changes, doc) => {
              console.log('changes: ', params, changes, doc);
              setDoc(doc);
            }}
          />
        </View>
      )}
    </>
  );
}

const styles = StyleSheet.create({
  loading: {
    margin: 20,
    justifyContent: 'center',
    flexDirection: 'column',
    alignItems: 'center',
  },
  form: {
    flex: 1,
    backgroundColor: 'white',
    borderRadius: 6,
    borderWidth: 1,
    borderColor: '#E6E6FA',
    padding: 2,
  },
  button: {
    backgroundColor: 'white',
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#1E90FF',
    padding: 10,
  },
  buttonText: {
    color: '#1E90FF',
  },
  row: {
    margin: 10,
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  highlight: {
    fontWeight: '700',
  },
  title: {
    fontSize: 24,
    fontWeight: '600',
    color: 'black',
  },
});

export default Document;



export const joyfillGenerate = async (identifier, userAccessToken) => {

  const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${identifier}/exports/pdf`, {
    method: 'POST',
    mode:'cors',
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ fields: [] })
  });
  

  const data = await response.json();
  return data.download_url;

}


export const joyfillRetrieve = async (documentIdentifier, userAccessToken) => {

  const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${documentIdentifier}`, {
    method: 'GET',
    mode: 'cors',
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
      'Content-Type': 'application/json'
    },
  });

  const data = await response.json();
  return data;
}


export const joyfillSave = async (doc, userAccessToken) => {

  const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${doc.identifier}`, {
    method: 'POST',
    mode:'cors',
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(doc)
  });

  const data = await response.json();
  return data;

};


export const getDataUriForFileUpload = async (fileUpload) => {

  return new Promise((ful, rej) => {

    const reader = new FileReader();

    reader.readAsDataURL(fileUpload);
    
    reader.onloadend = async () => {
      ful(reader.result);
    };

  });

};

export const uploadFileAsync = async (documentId, dataUri, userAccessToken) => {

  const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${documentId}/files/datauri`, {
    method: 'POST', 
    mode: 'no-cors',
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ file: dataUri })
  });

  const data = await response.json();
  return data;

};

export const convertPDFPagesToBase64Async = async (dataUri, userAccessToken) => {

  try {

    const response = await fetch(`https://api-joy.joyfill.io/v1/utilities/pdf/to/png/datauri`, {
      method: 'POST', 
      mode: 'no-cors',
      headers: {
        Authorization: `Bearer ${userAccessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ file: dataUri })
    });

    return await response.json();

  } catch (error) {

    throw error;

  }

};

JoyDoc Properties

NameTypeDescriptionRequired
mode'fill' | 'readonly'Enables and disables certain JoyDoc functionality and features.
• fill is the mode where you simply input the field data into the form
• readonly is the mode where everything in the form is set to read-only.
docObjectThe default JoyDoc JSON starting object to load into the component view. Must be in the JoyDoc JSON data structure.
onChangeFunction: (params: object, changes: object, doc: object) => {}Used to listen to any changes to the style, layout, values, etc. across all modes.
• params: Object: Contains information about what field has been changed.
changes: Object: Can contain any of the JoyDoc JSON structure-supported properties.
doc: Object: Fully updated JoyDoc JSON structure with changes applied.