diff --git a/example/package.json b/example/package.json index 030221a9..c245ee45 100644 --- a/example/package.json +++ b/example/package.json @@ -5,7 +5,7 @@ "private": true, "scripts": { "android": "react-native run-android", - "android:all": "concurrently \"yarn start\" \"yarn start:server\" \"yarn ios\"", + "android:all": "concurrently \"yarn start\" \"yarn start:server\" \"yarn android\"", "bootstrap": "cd .. && yarn bootstrap", "build:server": "babel server --out-dir dist --extensions '.ts,.tsx' --ignore '**/__tests__/**' --source-maps --copy-files --delete-dir-on-start", "ios": "react-native run-ios", diff --git a/example/src/App.tsx b/example/src/App.tsx index 029f6518..f159b929 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useMemo, useEffect, useState } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator, @@ -101,10 +101,21 @@ const screenOptions = { export default function App() { const [logs, setlogs] = useState([]); - const clearLogs = () => setlogs([]); + const clearLogs = useCallback(() => setlogs([]), []); const { initialize: initStripe } = useStripeTerminal(); useEffect(() => { + const handlePermissionsSuccess = async () => { + const { error } = await initStripe({ + logLevel: 'verbose', + }); + if (error) { + Alert.alert('StripeTerminal init failed', error.message); + } else { + console.log('StripeTerminal has been initialized properly'); + } + }; + async function handlePermissions() { try { const granted = await PermissionsAndroid.request( @@ -157,8 +168,7 @@ export default function App() { } else { handlePermissionsSuccess(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [initStripe]); const handlePermissionsError = () => { console.error( @@ -166,22 +176,11 @@ export default function App() { ); }; - const handlePermissionsSuccess = async () => { - const { error } = await initStripe({ - logLevel: 'verbose', - }); - if (error) { - Alert.alert('StripeTerminal init failed', error.message); - } else { - console.log('StripeTerminal has been initialized properly'); - } - }; - const hasGrantedPermission = (status: string) => { return status === PermissionsAndroid.RESULTS.GRANTED; }; - const addLogs = (newLog: Log) => { + const addLogs = useCallback((newLog: Log) => { const updateLog = (log: Log) => log.name === newLog.name ? { name: log.name, events: [...log.events, ...newLog.events] } @@ -191,16 +190,15 @@ export default function App() { ? prev.map(updateLog) : [...prev, newLog] ); - }; + }, []); + + const value = useMemo( + () => ({ logs, addLogs, clearLogs }), + [logs, addLogs, clearLogs] + ); return ( - + <> { + const handleDiscoverReaders = useCallback(async () => { setDiscoveringLoading(true); // List of discovered readers will be available within useStripeTerminal hook const { error: discoverReadersError } = await discoverReaders({ @@ -138,13 +138,12 @@ export default function DiscoverReadersScreen() { navigation.goBack(); } } - }; + }, [navigation, discoverReaders, discoveryMethod, simulated]); useEffect(() => { simulateReaderUpdate('none'); handleDiscoverReaders(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [handleDiscoverReaders, simulateReaderUpdate]); const handleConnectReader = async (reader: Reader.Type) => { let error: StripeError | undefined; diff --git a/example/src/screens/LocationListScreen.tsx b/example/src/screens/LocationListScreen.tsx index 4a754445..f1c1bdeb 100644 --- a/example/src/screens/LocationListScreen.tsx +++ b/example/src/screens/LocationListScreen.tsx @@ -28,8 +28,7 @@ export default function LocationListScreen() { } } init(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [getLocations]); const renderItem = (item: Location) => ( { - _readReusableCard(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const _readReusableCard = async () => { + const _readReusableCard = useCallback(async () => { clearLogs(); navigation.navigate('LogListScreen'); @@ -93,7 +88,11 @@ export default function ReadReusableCardScreen() { ], }); } - }; + }, [navigation, readReusableCard, addLogs, clearLogs]); + + useEffect(() => { + _readReusableCard(); + }, [_readReusableCard]); return ; } diff --git a/example/src/screens/SetupIntentScreen.tsx b/example/src/screens/SetupIntentScreen.tsx index c2037c1d..5d53e8a4 100644 --- a/example/src/screens/SetupIntentScreen.tsx +++ b/example/src/screens/SetupIntentScreen.tsx @@ -1,5 +1,5 @@ import { RouteProp, useNavigation, useRoute } from '@react-navigation/core'; -import React, { useContext, useEffect } from 'react'; +import React, { useCallback, useContext, useEffect } from 'react'; import { ScrollView, StyleSheet } from 'react-native'; import { SetupIntent, @@ -50,11 +50,6 @@ export default function SetupIntentScreen() { }, }); - useEffect(() => { - _createSetupIntent(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const createServerSetupIntent = async () => { try { const response = await fetch(`${API_URL}/create_setup_intent`, { @@ -71,7 +66,97 @@ export default function SetupIntentScreen() { } }; - const _createSetupIntent = async () => { + const _processPayment = useCallback( + async (setupIntentId: string) => { + addLogs({ + name: 'Process Payment', + events: [ + { + name: 'Process', + description: 'terminal.confirmSetupIntent', + metadata: { setupIntentId }, + }, + ], + }); + const { setupIntent, error } = await confirmSetupIntent(setupIntentId); + if (error) { + addLogs({ + name: 'Process Payment', + events: [ + { + name: 'Failed', + description: 'terminal.confirmSetupIntent', + metadata: { + errorCode: error.code, + errorMessage: error.message, + }, + }, + ], + }); + } else if (setupIntent) { + addLogs({ + name: 'Process Payment', + events: [ + { + name: 'Finished', + description: 'terminal.confirmSetupIntent', + metadata: { setupIntentId: setupIntent.id }, + }, + ], + }); + } + }, + [addLogs, confirmSetupIntent] + ); + + const _collectPaymentMethod = useCallback( + async (setupIntentId: string) => { + addLogs({ + name: 'Collect Setup Intent', + events: [ + { + name: 'Collect', + description: 'terminal.collectSetupIntentPaymentMethod', + metadata: { setupIntentId }, + }, + ], + }); + const { setupIntent, error } = await collectSetupIntentPaymentMethod({ + setupIntentId: setupIntentId, + customerConsentCollected: true, + }); + if (error) { + addLogs({ + name: 'Collect Setup Intent', + events: [ + { + name: 'Failed', + description: 'terminal.collectSetupIntentPaymentMethod', + metadata: { + errorCode: error.code, + errorMessage: error.message, + }, + }, + ], + }); + } else if (setupIntent) { + addLogs({ + name: 'Collect Setup Intent', + events: [ + { + name: 'Created', + description: 'terminal.collectSetupIntentPaymentMethod', + metadata: { setupIntentId: setupIntent.id }, + }, + ], + }); + await _processPayment(setupIntentId); + } + }, + [_processPayment, addLogs, collectSetupIntentPaymentMethod] + ); + + const _createSetupIntent = useCallback(async () => { clearLogs(); navigation.navigate('LogListScreen'); addLogs({ @@ -129,91 +214,19 @@ export default function SetupIntentScreen() { } else if (setupIntent) { await _collectPaymentMethod(setupIntent.id); } - }; - - const _collectPaymentMethod = async (setupIntentId: string) => { - addLogs({ - name: 'Collect Setup Intent', - events: [ - { - name: 'Collect', - description: 'terminal.collectSetupIntentPaymentMethod', - metadata: { setupIntentId }, - }, - ], - }); - const { setupIntent, error } = await collectSetupIntentPaymentMethod({ - setupIntentId: setupIntentId, - customerConsentCollected: true, - }); - if (error) { - addLogs({ - name: 'Collect Setup Intent', - events: [ - { - name: 'Failed', - description: 'terminal.collectSetupIntentPaymentMethod', - metadata: { - errorCode: error.code, - errorMessage: error.message, - }, - }, - ], - }); - } else if (setupIntent) { - addLogs({ - name: 'Collect Setup Intent', - events: [ - { - name: 'Created', - description: 'terminal.collectSetupIntentPaymentMethod', - metadata: { setupIntentId: setupIntent.id }, - }, - ], - }); - await _processPayment(setupIntentId); - } - }; + }, [ + _collectPaymentMethod, + createSetupIntent, + addLogs, + clearLogs, + discoveryMethod, + navigation, + retrieveSetupIntent, + ]); - const _processPayment = async (setupIntentId: string) => { - addLogs({ - name: 'Process Payment', - events: [ - { - name: 'Process', - description: 'terminal.confirmSetupIntent', - metadata: { setupIntentId }, - }, - ], - }); - const { setupIntent, error } = await confirmSetupIntent(setupIntentId); - if (error) { - addLogs({ - name: 'Process Payment', - events: [ - { - name: 'Failed', - description: 'terminal.confirmSetupIntent', - metadata: { - errorCode: error.code, - errorMessage: error.message, - }, - }, - ], - }); - } else if (setupIntent) { - addLogs({ - name: 'Process Payment', - events: [ - { - name: 'Finished', - description: 'terminal.confirmSetupIntent', - metadata: { setupIntentId: setupIntent.id }, - }, - ], - }); - } - }; + useEffect(() => { + _createSetupIntent(); + }, [_createSetupIntent]); return ; } diff --git a/src/components/StripeTerminalProvider.tsx b/src/components/StripeTerminalProvider.tsx index 8029024d..cb5963c0 100644 --- a/src/components/StripeTerminalProvider.tsx +++ b/src/components/StripeTerminalProvider.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useRef, useState, useMemo } from 'react'; import type { Reader, InitParams, @@ -237,9 +237,9 @@ export function StripeTerminalProvider({ useListener(CHANGE_PAYMENT_STATUS, didChangePaymentStatus); useListener(CHANGE_CONNECTION_STATUS, didChangeConnectionStatus); - const setUserCallbacks = (callbacks: UserCallbacks) => { + const setUserCallbacks = useCallback((callbacks: UserCallbacks) => { userCallbacks.current = callbacks; - }; + }, []); const _initialize = useCallback( async (params: InitParams) => { @@ -265,29 +265,38 @@ export function StripeTerminalProvider({ [setLoading, setConnectedReader, setIsInitialized, log] ); + const value = useMemo( + () => ({ + loading, + isInitialized, + connectedReader, + discoveredReaders, + setIsInitialized, + setLoading, + setConnectedReader, + setDiscoveredReaders, + log, + initialize: _initialize, + setUserCallbacks, + emitter, + }), + [ + _initialize, + loading, + isInitialized, + connectedReader, + discoveredReaders, + setIsInitialized, + setLoading, + setConnectedReader, + setDiscoveredReaders, + log, + setUserCallbacks, + ] + ); + return ( - { - setLoading(value); - }, - setConnectedReader: (value) => { - setConnectedReader(value); - }, - setDiscoveredReaders: (values) => { - setDiscoveredReaders(values); - }, - log, - initialize: _initialize, - setUserCallbacks, - emitter, - }} - > + {children} );