Skip to content

React Integration Guide for VimOS.js

If your application uses the React framework, you can easily wrap the VimOS.js with React like code. We recommend doing it using a Context Provider and Hooks, but you are welcome to do it in whichever way works best for you.

Context Provider

First, define a context to hold the vimOS instance

jsx
import { createContext } from 'react';

export const VimOSContext = createContext();
tsx
import { createContext } from "react";
import type { SDK } from "vim-os-js-browser/types";

export const VimOSContext = createContext<SDK>({} as SDK);

Wrap your main component with a wrapper that will provide a VimOS instance to VimOSContext

jsx
import React, { useEffect, useState } from 'react';
import { VimOSContext } from './vimOsContext';

export const AppWrapper = ({children}) => {
  const [vimOS, setVimOS] = useState(undefined);
  useEffect(() => {
    (async () => {
      setVimOS(await window.vimSdk.initializeVimSDK());
    })()
  }, []);

  if (!vimOS) {
    return <div>Loading VimSDK...</div>;
  }
  return (
    <VimOSContext.Provider value={vimOS}>
      {children}
    </VimOSContext.Provider>
  )
}
jsx
import React, { useEffect, useState } from "react";
import { loadSdk } from "vim-os-js-browser";
import { VimOSContext } from "./vimOsContext";

export const AppWrapper = ({ children }) => {
  const [vimOS, setVimOS] = useState(undefined);
  useEffect(() => {
    (async () => {
      setVimOS(await loadSdk());
    })();
  }, []);

  if (!vimOS) {
    return <div>Loading VimSDK...</div>;
  }
  return (
    <VimOSContext.Provider value={vimOS}>{children}</VimOSContext.Provider>
  );
};
tsx
import React, { useEffect, useState } from "react";
import type { SDK } from "vim-os-js-browser/types";
import { loadSdk } from "vim-os-js-browser";
import { VimOSContext } from "./vimOsContext";

export const AppWrapper: React.FC = ({ children }: React.PropsWithChildren) => {
  const [vimOS, setVimOS] = useState<SDK | undefined>(undefined);
  useEffect(() => {
    (async () => {
      setVimOS(await loadSdk());
    })();
  }, []);

  if (!vimOS) {
    return <div>Loading VimSDK...</div>;
  }
  return (
    <VimOSContext.Provider value={vimOS}>{children}</VimOSContext.Provider>
  );
};

You can now easily access the VimOS instance anywhere in your component tree. For example, here is how you can access data from the VimOS User:

jsx
import React, { useContext } from 'react';
import { VimOSContext } from './vimOsContext';

export const Username = () => {
  const vimOS = useContext(VimOSContext);
  const user = vimOS.sessionContext.user;
  return (
    <div>{`${user.demographics?.firstName} ${user.demographics?.lastName}`}</div>
  );
}

Custom Hooks

We will show examples for a few use cases of using our VimOSContext with Custom Hooks.

Decoupling

The simplest form of a Hook decouples the VimOS logic from the components. For example, if you don't want your components to depend on the exact path to the user object, or on the fact that you are using React Context, you can create a custom useVimOSUser Hook:

jsx
import { useContext } from 'react';
import { VimOSContext } from './vimOsContext';

export const useVimOSUser = () => {
  const vimOS = useContext(VimOSContext);
  return vimOS.sessionContext.user;
}

Your Username component will now look like this:

jsx
import React from "react";
import { useVimOSUser } from "./useVimOSUser";

export const Username = () => {
  const user = useVimOSUser();
  return (
    <div>{`${user.demographics?.firstName} ${user.demographics?.lastName}`}</div>
  );
};

Subscribe for ehr changes

A more complex hook can subscribe to changes on the VimOS ehr state and provide easy access to the updated data. For example, here is a hook that subscribes to EHR state changes:

jsx
import { useContext, useState, useEffect } from 'react';
import { VimOSContext } from './vimOsContext';

export const useVimOSPatient = () => {
  const vimOS = useContext(VimOSContext);
  const [patient, setPatient] = useState(undefined);
  useEffect(() => {
    vimOS.ehr.subscribe('patient', setPatient);
    return () => {
      vimOS.ehr.unsubscribe('patient', setPatient);
    }
  }, [vimOS]);
  return patient;
}
tsx
import { useContext, useState, useEffect } from 'react';
import type { EHR } from 'vim-os-js-browser/types';
import { VimOSContext } from './vimOsContext';

export const useVimOSPatient = () => {
  const vimOS = useContext(VimOSContext);
  const [patient, setPatient] = useState<EHR.Patient | undefined>(undefined);
  useEffect(() => {
    vimOS.ehr.subscribe('patient', setPatient);
    return () => {
      vimOS.ehr.unsubscribe('patient', setPatient);
    }
  }, [vimOS]);
  return patient;
}

You can now easily work with the latest patient data in your components:

jsx
import React from "react";
import { useVimOSPatient } from "./useVimOSPatient";

export const PatientName = () => {
  const patient = useVimOSPatient();
  if (!patient) {
    return null;
  }
  return (
    <div>{`${patient.demographics?.firstName} ${patient.demographics?.lastName}`}</div>
  );
};

Writebacks

Let's see how we can use our VimOSContext to use the VimOS EHR Writeback functionality to easily update an Encounter.

We'll start by writing a custom hook to expose the encounter writeback functions:

jsx
import { useContext, useEffect, useMemo, useState } from "react";
import { VimOSContext } from "./vimOsContext";

export const useEncounterWriteback = (paramsToCheck) => {
  const vimOS = useContext(VimOSContext);
  const [canUpdate, setCanUpdate] = useState(
    vimOS.ehr.resourceUpdater.canUpdateEncounter(paramsToCheck).canUpdate
  );

  useEffect(() => {
    const cb = () => {
      setCanUpdate(
        vimOS.ehr.resourceUpdater.canUpdateEncounter(paramsToCheck).canUpdate
      );
    };
    vimOS.ehr.resourceUpdater.subscribe("encounter", cb);
    return vimOS.ehr.resourceUpdater.unsubscribe("encounter", cb);
  }, [vimOS.ehr, paramsToCheck]);

  return useMemo(
    () => ({
      canUpdate,
      doUpdate: vimOS.ehr.resourceUpdater.updateEncounter,
    }),
    [vimOS.ehr.resourceUpdater.updateEncounter, canUpdate]
  );
};
tsx
import { useContext, useEffect, useMemo, useState } from "react";
import { VimOSContext } from "./vimOsContext";
import type { EHR } from "vim-os-js-browser/types";

export const useEncounterWriteback = (
  paramsToCheck: EHR.CanUpdateEncounterParams
) => {
  const vimOS = useContext(VimOSContext);
  const [canUpdate, setCanUpdate] = useState<boolean>(
    vimOS.ehr.resourceUpdater.canUpdateEncounter(paramsToCheck).canUpdate
  );

  useEffect(() => {
    const cb = () =>
      setCanUpdate(
        vimOS.ehr.resourceUpdater.canUpdateEncounter(paramsToCheck).canUpdate
      );
    vimOS.ehr.resourceUpdater.subscribe("encounter", cb);
    return vimOS.ehr.resourceUpdater.unsubscribe("encounter", cb);
  }, [vimOS.ehr, paramsToCheck]);

  return useMemo(
    () => ({
      canUpdate,
      doUpdate: vimOS.ehr.resourceUpdater.updateEncounter,
    }),
    [vimOS.ehr.resourceUpdater.updateEncounter, canUpdate]
  );
};

Now, based on the example in the EHR Writeback section, we can use our hook to easily perform a writeback:

jsx
import React, { useCallback, useState } from "react";
import { useEncounterWriteback } from "./useEncounterWritebackJS";

const paramsToCheck = {
  assessment: {
    generalNotes: true,
    diagnosisCodes: true,
  },
  plan: {
    procedureCodes: true,
    generalNotes: true,
  },
};

export const UpdateEncounter = ({
  assessmentsGeneralNotes,
  plansGeneralNotes,
  diagnosisCodes,
  procedureCodes,
}) => {
  const [isUpdating, setIsUpdating] = useState(false);
  const { canUpdate, doUpdate } = useEncounterWriteback(paramsToCheck);

  const updateEncounter = useCallback(async () => {
    if (canUpdate) {
      try {
        setIsUpdating(true);
        await doUpdate({
          assessment: {
            generalNotes: assessmentsGeneralNotes,
            diagnosisCodes,
          },
          plan: {
            procedureCodes,
            generalNotes: plansGeneralNotes,
          },
        });
      } catch (error) {
        console.error("update failed", error);
      } finally {
        setIsUpdating(false);
      }
    }
  }, [
    canUpdate,
    doUpdate,
    assessmentsGeneralNotes,
    diagnosisCodes,
    procedureCodes,
    plansGeneralNotes,
  ]);

  return (
    <div>
      <button disabled={!canUpdate || isUpdating} onClick={updateEncounter}>
        Click to update
      </button>
    </div>
  );
};
tsx
import React, { useCallback, useState } from "react";
import type { EHR } from "vim-os-js-browser/types";
import { useEncounterWriteback } from "./useEncounterWriteback";

interface UpdateProps {
  assessmentsGeneralNotes: string;
  plansGeneralNotes: string;
  diagnosisCodes: EHR.UpdatableDiagnosis[];
  procedureCodes: EHR.UpdatableProcedures[];
}

export const UpdateEncounter: React.FC<UpdateProps> = ({
  assessmentsGeneralNotes,
  plansGeneralNotes,
  diagnosisCodes,
  procedureCodes,
}) => {
  const [isUpdating, setIsUpdating] = useState<boolean>(false);
  const { canUpdate, doUpdate } = useEncounterWriteback({
    assessment: {
      generalNotes: true,
      diagnosisCodes: true,
    },
    plan: {
      procedureCodes: true,
      generalNotes: true,
    },
  });

  const updateEncounter = useCallback(async () => {
    if (canUpdate) {
      try {
        setIsUpdating(true);
        await doUpdate({
          assessment: {
            generalNotes: assessmentsGeneralNotes,
            diagnosisCodes,
          },
          plan: {
            procedureCodes,
            generalNotes: plansGeneralNotes,
          },
        });
      } catch (error) {
        console.error("update failed", error);
      } finally {
        setIsUpdating(false);
      }
    }
  }, [
    canUpdate,
    doUpdate,
    assessmentsGeneralNotes,
    diagnosisCodes,
    procedureCodes,
    plansGeneralNotes,
  ]);

  return (
    <div>
      <button disabled={!canUpdate || isUpdating} onClick={updateEncounter}>
        Click to update
      </button>
    </div>
  );
};