React native navigator stripe - react-native

i'm trying to navigate from once a user inputs their Credit card details and presses to the pay button, it takes them to another screen within my app. I've set the navigation path to a screen called products. However my payment function takes the payment via Stripe but doesn't navigate to the next screen.what am i doing wrong?
Here is my payment function
import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View,ImageBackground } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
import stripe from 'tipsi-stripe';
import axios from 'axios'
stripe.setOptions({
publishableKey:
'pk_test_XXXXX'
})
export default function PaymentScreen({navigation}) {
const [ccNumber, setCCNumber] = useState('')
const [expDate, setExpDate] = useState('')
const [zipCode, setZipCode] = useState('')
const [cvv, setCvv] = useState('')
const [ loading, setLoading] = useState(false)
const [token, setToken] = useState(null)
const onPaymentPress = async () => {
if ( ccNumber && expDate && zipCode && cvv == null) {
alert("All fields must be completed")
return
};
const token = await stripe.paymentRequestWithCardForm({
smsAutofillDisabled: true,
requiredBillingAddressFields: 'full',
prefilledInformation: {
billingAddress: {
name: 'Gunilla Haugeh',
line1: 'Canary Place',
line2: '3',
city: 'Macon',
state: 'Georgia',
country: 'US',
postalCode: '31217',
email: 'ghaugeh0#printfriendly.com',
},
},
})
axios({
method:'POST',
url: 'https://us-central1-lvc2-73300.cloudfunctions.net/completePaymentWithStripe',
data: {
amount: 50000,
currency:'usd',
token: token
},
})
.then(response => {
console.log(response);
loading:false
})
navigation.navigate('Products',{token})
}
here is my on press function to activate the payment
<TouchableOpacity
style={styles.button}
onPress={() => onPaymentPress()}>
<Text style={styles.buttonTitle}>Submit Payment</Text>
</TouchableOpacity>
also i'm using firebase to store the data
Here is my database firebase backend
const stripe = require('stripe')('sk_test_XXXX)
exports.completePaymentWithStripe = functions.https.onRequest(
(request,response) => {
stripe.charges.create({
amount: request.body.amount,
currency: request.body.currency,
source: request.body.token.tokenId,
})
.then(charge => {
response.send(charge)
return null
})
.catch(error => {
console.log(error)
})
}
)

Related

React Native app crashing with no error when selecting a value from RNPickerSelect

I'm working in an older codebase using React-Native 0.63 and I'm trying to add RNPickerSelect to the login page of an app. The app uses Redux for State management, I've created Types, Reducers, Selectors, Actions, and an Initial State for the Redux. After Importing the RNPickerSelect and the Picker appears on the screen, and allows interaction but whenever a value is selected the app crashes and gives no error.
Here's the LoginScreen.js Page
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Text, StatusBar, View, ScrollView, Platform, Image } from 'react-native'
import Config from 'react-native-config'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { Colors } from '../Themes/'
import LoginActions, { LoginSelectors } from '../Redux/LoginRedux'
import RNPickerSelect from 'react-native-picker-select'
// Styles
import styles from './Styles/LoginScreenStyle'
import Logo from '../Components/Logo'
import LoginForm from '../Components/LoginForm'
import { withNavigation } from 'react-navigation'
import I18n from '../I18n/'
class LoginScreen extends Component {
static propTypes = {
userName: PropTypes.string,
password: PropTypes.string,
pickerValue: PropTypes.string,
updateUserName: PropTypes.func,
updatePassword: PropTypes.func,
updatePickerValue: PropTypes.func,
navigation: PropTypes.object,
doLogin: PropTypes.func,
siteUrl: PropTypes.string,
updateSiteUrl: PropTypes.func,
isLoggingIn: PropTypes.bool,
initialize: PropTypes.func
}
componentDidMount () {
const { userName, password, siteUrl, initialize } = this.props
if (!userName && !password && !siteUrl) {
initialize(Config.USER_NAME, Config.PASSWORD, Config.SITE_URL)
}
}
handlePressLogin = () => {
const { userName, password, siteUrl } = this.props
this.props.doLogin(userName, password, siteUrl)
}
render () {
const { userName, password, siteUrl, updateUserName, updatePassword, updateSiteUrl, isLoggingIn, updatePickerValue, pickerValue } = this.props
return (
<ScrollView contentContainerStyle={styles.mainScrollViewContainer}>
<View style={styles.contentArea}>
<View style={{backgroundColor: 'red'}}>
<StatusBar
barStyle={Platform.OS == 'ios' ? 'dark-content' : 'light-content'}
backgroundColor={Colors.primaryColor}
/>
</View>
<View style={styles.formViewContainer}>
<Logo size='small' />
<View style={styles.welcomeTextContainer}>
<Text style={styles.LogoText}>{I18n.t('loginPageWelcomeText')}</Text>
</View>
<RNPickerSelect
onValueChange={updatePickerValue}
items={[
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
]}
value={pickerValue}
/>
<LoginForm
userName={userName}
onUserNameChange={updateUserName}
password={password}
onPasswordChange={updatePassword}
siteUrl={siteUrl}
onSiteUrlChange={updateSiteUrl}
handlePressLogin={this.handlePressLogin}
isLogginIn={isLoggingIn}
/>
</View>
</View>
<View style={{marginBottom: 20}}><Image resizeMode='contain' style={{width: '100%'}} source={require('../Images/footer.jpeg')} /></View>
</ScrollView>
)
}
}
const mapStateToProps = (state) => {
return {
userName: LoginSelectors.getUserName(state),
password: LoginSelectors.getPassword(state),
pickerValue: LoginSelectors.getPickerValue(state),
siteUrl: LoginSelectors.getSiteUrl(state),
isLoggingIn: LoginSelectors.isLoggingIn(state),
}
}
const mapDispatchToProps = {
updateUserName: LoginActions.updateUserName,
updatePassword: LoginActions.updatePassword,
updateSiteUrl: LoginActions.updateSiteUrl,
updatePickerValue: LoginActions.updatePickerValue,
doLogin: LoginActions.loginRequest,
initialize: LoginActions.initialize
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withNavigation
)(LoginScreen)
And here's my redux js
import { createReducer, createActions } from 'reduxsauce'
import { StartupTypes } from './StartupRedux'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
initialize: ['userName', 'password', 'siteUrl'],
updateSiteUrl: ['siteUrl'],
updateUserName: ['userName'],
updatePassword: ['password'],
updatePickerValue: ['pickerValue'],
loginRequest: ['userName', 'password', 'siteUrl'],
loginSuccess: ['data'],
loginFailure: null,
logout: null
})
export const LoginTypes = Types
export default Creators
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
userName: '',
password: '',
siteUrl: '',
pickerValue: '',
accessToken: null,
displayName: '',
vendorId: null,
fetching: null,
payload: null,
error: null,
})
/* ------------- Selectors ------------- */
export const LoginSelectors = {
getData: state => state.data,
getSiteUrl: state => state.login.siteUrl,
getUserName: state => state.login.userName,
getPassword: state => state.login.password,
getPickerValue: state => state.login.pickerValue,
isLoggingIn: state => state.login.fetching,
getAccessToken: state => state.login.accessToken,
getDisplayName: state => state.login.displayName,
getVendorId: state => state.login.vendorId,
}
/* ------------- Reducers ------------- */
export const initialize = (state, { userName, password, siteUrl }) =>
state.merge({ userName, password, siteUrl })
export const request = (state, { data }) =>
state.merge({ fetching: true, data, payload: null })
export const success = (state, action) => {
const { data: { token: accessToken, store_name: displayName, store_id: vendorId } } = action
//console.log(vendorId)
return state.merge({ fetching: false, error: null, password: '', accessToken, displayName, vendorId })
}
export const updateUserName = (state, action) => {
const { userName } = action
return state.merge({ userName })
}
export const updatePassword = (state, action) => {
const { password } = action
return state.merge({ password })
}
export const updatePickerValue = (state, action) => {
const { pickerValue } = action
return state.merge({ pickerValue })
}
export const updateSiteUrl = (state, action) => {
const { siteUrl } = action
return state.merge({ siteUrl })
}
// Something went wrong somewhere.
export const failure = state =>
state.merge({ fetching: false, error: true, payload: null })
export const logout = state =>
INITIAL_STATE
export const onStartup = state =>
state.merge({ fetching: false })
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.LOGIN_REQUEST]: request,
[Types.LOGIN_SUCCESS]: success,
[Types.LOGIN_FAILURE]: failure,
[Types.UPDATE_USER_NAME]: updateUserName,
[Types.UPDATE_PASSWORD]: updatePassword,
[Types.UPDATE_SITE_URL]: updateSiteUrl,
[Types.UPDATE_PICKER_VALUE]: updatePickerValue,
[Types.LOGOUT]: logout,
[Types.INITIALIZE]: initialize,
[StartupTypes.STARTUP]: onStartup,
})
I've tried different pickers but RNPickerSelect seems to be the only one that even starts to display when I use it.
I've also tried putting it a level lower in the <LoginForm> component but for the sake of trying to make it easier on myself I've moved it up into LoginScreen instead.
I'm very new to React/React Native as well as Redux so I apologize if this is a bit of a noob question. Any help is greatly appreciated!

Stripe Integration with React Native

I have to integrate Stripe with my React Native Application this phone application is a phone version of a desktop application where stripe is already being integrated and works fine so the backend is already been implemented. I wanted to use the context i used i nthe react js application however the useElementwas not available in stripe/stripe-react-native however after some reasearch i figured it didn't matter if i used the #stripe/stripe-js library my problem now is i keep getting the error: Could not find Elements context; You need to wrap the part of your app that calls useElements() in an <Elements> provider. However i already wrapped the form in an provider this is my checkoutForm.js:
import { useNavigation } from '#react-navigation/native';
import
React,
{ useState,
useEffect, useRef }
from 'react'
import {
View,
Text,
SafeAreaView,
StatusBar,
StyleSheet,
TouchableOpacity,
ScrollView,
Image,
Pressable ,
TextInput,
Alert}
from 'react-native';
import Icon from '../components'
import { COLORS } from '../constants';
import { useStateContext } from '../context/StateContext';
import { ProductCarousel } from '../../components';
import { useElements, Elements} from "#stripe/react-stripe-js"
import { CardField, useConfirmPayment, useStripe, } from '#stripe/stripe-react-native';
import { Button } from 'react-native-elements';
const CheckoutForm = () => {
const navigation = useNavigation();
const [isDeliveryAddressOpen, setIsDeliveryAddressOpen] = useState(false);
const [isContactNumberOpen, setIsContactNumberOpen] = useState(false);
const [isDeliveryInstructionsOpen, setIsDeliveryInstructionsOpen] = useState(false);
const [isCartItemsOpen, setIsCartItemsOpen] = useState(false);
const [isPaymentInfoOpen, setIsPaymentInfoOpen] = useState(false);
//const { confirmPayment, loading } = useConfirmPayment();
const [success, setSuccess ] = useState(false)
const stripe = useStripe()
const elements = useElements()
const cardElement = useRef(null);
const { totalPrice, cartItems } = useStateContext();
const [fullName, setFullName] = useState('');
const [email, setEmail] = useState('');
const [address, setAddress] = useState('');
const [address2, setAddress2] = useState('');
const [state, setState] = useState('');
const [city, setCity] = useState('');
const [zipCode, setZipCode] = useState('');
const [primaryNumber, setPrimaryNumber] = useState('');
const [SecondaryNumber, setSecondaryNumber] = useState('');
const [DeliveryInstructions, setDeliveryInstructions] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
const [isError, setIsError] = useState(false);
//const [cardDetails, setCardDetails] = useState('')
const toggleDeliveryAddress = () => {
setIsDeliveryAddressOpen(!isDeliveryAddressOpen);
};
const toggleContactNumber = () => {
setIsContactNumberOpen(!isContactNumberOpen);
};
const toggleDeliveryInstructions = () => {
setIsDeliveryInstructionsOpen(!isDeliveryInstructionsOpen);
};
const toggleCartItems = () => {
setIsCartItemsOpen(!isCartItemsOpen);
};
const togglePaymentInfo = () => {
setIsPaymentInfoOpen(!isPaymentInfoOpen);
};
//const navigate = useNavigate();
const carts = {
status: 'pending',
items: (cartItems),
address,
fullName,
zipCode,
state,
city,
DeliveryInstructions,
primaryNumber,
SecondaryNumber,
totalPrice
}
const handleCheckout = async () => {
const response = await fetch('/create_cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(carts),
});
if(response.statusCode === 500) return;
// eslint-disable-next-line
const data = await response.json();
console.log('presseddddd', data)
}
/*========================================================*/
const handleSubmit = async () => {
const {error, paymentMethod} = await stripe.createPaymentMethod({
type: "card",
card: elements.getElement(CardField),
billing_details: {
name: fullName,
phone: primaryNumber,
email: email,
address: {
city: city,
line1: address,
state: state,
postal_code: zipCode
}
},
})
if(!error) {
try {
const {id} = paymentMethod
const carts = {
id,
amount: totalPrice,
confirm: true,
currency: 'CAD'
}
setIsProcessing(true);
const response = await fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(carts),
});
if(response.status === 200) {
console.log(response.status)
console.log("Successful payment")
setSuccess(true)
}
const data = await response.json();
if(response.status >= 400) {
console.log(response.status)
console.log(data.error.split(':').pop())
setIsError(data.error)
}
} catch (error) {
console.log("Error", error)
}
} else {
console.log(error.message)
}
setIsProcessing(false)
//navigate('/');
}
return (
<View style={styles.container}>
<View style={styles.insideContainer}>
<TouchableOpacity style={styles.titleContainer} onPress={togglePaymentInfo}>
<Text style={styles.title}>Payment Info</Text>
{!isPaymentInfoOpen ?
<Icon icon ='add-outline' color='#000' size={20}/>
: <Icon icon ='remove-outline' color='#000' size={20}/>
}
</TouchableOpacity>
{isPaymentInfoOpen && (
<View style={styles.containercollapsed}>
<View style={{width: '98%'}}>
<CardField
ref={cardElement}
postalCodeEnabled={false}
placeholders={{
number: '4242 4242 4242 4242',
}}
cardStyle={{
backgroundColor: '#FFFFFF',
textColor: '#000000',
borderColor: COLORS.lightGray2,
borderWidth: 1,
borderRadius: 4
}}
style={{
width: '100%',
height: 50,
}}
onCardChange={(cardDetails) => {
}}
onFocus={(focusedField) => {
}}
/>
</View>
</View>
)}
</View>
<View style={styles.PaybuttonView}>
<Button
title="Pay"
onPress={ () => handleSubmit()}
/*disabled={loading}
loading={loading}*/
/>
{/*<Pressable style={styles.Paybutton} onPress={() => {handleCheckout(); handlePayPress()} }>
<Text style={{textAlign: 'center', fontSize: 20, color: '#FFFF', textTransform: 'uppercase'}}>Checkout</Text>
</Pressable>*/}
</View>
</View>
)
}
export default CheckoutForm
this is my StripeForm.js :
import React from 'react'
import {Elements} from "#stripe/react-stripe-js"
import CheckoutForm from './CheckoutForm';
import { loadStripe } from "#stripe/stripe-js"
const stripePromise = loadStripe(i removed the key but this is the key's place);
export const StipeForm = () => {
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
)
}
To initialize Stripe to be used with the Stripe React Native SDK you need to use either StripeProvider or initStripe. See the docs here. You can't use the React Stripe.JS library initialize Stripe for the React Native SDK.

i can't connect redux with react-native app

I have a problem with redux and react-native because I can't connect the state of the "login" view.
I would like to upload the user data and the token in the store.
In "AppState" View, I manage the actions :
import * as type from "../redux/actionTypes";
import {initialState} from "../redux/initialState";
export default function LoginStateReducer(state = initialState, action) {
switch( action.type ) {
case type.SET_LOGIN_STATE:
return {
...state,
user: action.user,
token: action.token,
isLoading: false,
isLoggedIn: true,
};
case type.SET_LOGOUT_STATE:
return {
...state,
user: null,
token: null,
isLoading: false,
isLoggedIn: false,
};
default:
return state;
}
};
In "AppView", I manage the state :
import React, { useState, useEffect, useReducer, useMemo } from 'react';
import { View, ActivityIndicator } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Login from './login/LoginViewContainer';
import { AuthContext } from './config/Context';
import * as type from '../redux/actionTypes'
import { initialState } from '../redux/initialState';
import LoginStateReducer from './AppState';
import Navigator from './navigation/Navigator';
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [loginState, dispatch] = useReducer(LoginStateReducer, initialState);
const authContext = useMemo(() => ({
signIn: async(data) => {
setIsLoading(false);
const token = String(data.token);
const user= data.user;
try {
await AsyncStorage .setItem('token', token);
} catch(e) {
console.log(e);
}
dispatch({ type: type.SET_LOGIN_STATE, user: user, token: token });
},
signOut: async() => {
setIsLoading(false);
try {
await AsyncStorage .removeItem('token');
} catch(e) {
console.log(e);
}
dispatch({ type: type.SET_LOGOUT_STATE });
},
}), []);
if( loginState.isLoading ) {
return(
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<AuthContext.Provider value={authContext}>
{ loginState.token !== null ? (
<Navigator onNavigationStateChange={() => {}} uriPrefix="/app" />
)
:
<Login />
}
</AuthContext.Provider>
);
}
export default App;
In "AppViewContainer", I connect the reducer with the state :
import { connect } from 'react-redux';
import { compose, lifecycle } from 'recompose';
import { Platform, UIManager } from 'react-native';
import AppView from './AppView';
import LoginStateReducer from './AppState';
export default compose(
connect(
state => ({
user: state.user,
token: state.token,
}),
dispatch => ({
LoginStateReducer: () => dispatch(LoginStateReducer()),
})
),
lifecycle({
componentDidMount() {
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
this.props.LoginStateReducer();
},
}),
)(AppView);
In the "reducer", I have :
import { combineReducers } from 'redux';
// ## Generator Reducer Imports
import LoginStateReducer from '../modules/AppState';
export default combineReducers({
login: LoginStateReducer,
});
When I call the store once connected, it has not changed and I don't understand why :
"login": {"isLoading": true, "isLoggedIn": false, "token": "", "user": ""}
Can you tell me if i am missing something?
Thank you in advance for your feedback

Connect react-redux to stateless component. Properties inside the function are not updated

Problem:
I have very simple todo app. There is one action - add todo. When I add a task, I simulate sending it to the server using a setTimeout.
When I receive a response from the server, I immediately check to see if there is an error to avoid further action. In stateful component, everything works, and in stateless component it doesn't.
See the code to better understand the problem.
Environment:
"react": "16.8.6",
"react-native": "0.60.5",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
№ 1. Stateful component:
import React, {Component} from 'react';
import {View, Button, ActivityIndicator} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
class MainScreen extends Component {
todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
sendTodoToServer = async () => {
const todo = this.todoGenerator();
const {addTodo} = this.props;
await addTodo(todo);
// this
const {error} = this.props;
if (error) {
console.log('error', error);
}
};
render() {
const {isLoading} = this.props;
return (
<View>
<Button title="Generate todo" onPress={this.sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
}
}
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreen);
№ 1. Stateful component. Console:
As you can see,
const {error} = this.props;
if (error) {
console.log('error', error);
}
it's work. Okay, let's move on to functional components
№ 2. Stateless component with redux connect:
import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
const MainScreenFC = ({isLoading, addTodo, error}) => {
const todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
const sendTodoToServer = async () => {
const todo = todoGenerator();
await addTodo(todo);
if (error) {
console.log('error', error);
}
};
return (
<View>
<Button title="Generate todo" onPress={sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
};
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreenFC);
№ 2. Stateless component with redux connect. Console:
The error did not display in the console, although it is in the reducer
№ 3. Stateless component with redux HOOKS:
import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect, shallowEqual, useDispatch, useSelector} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
const MainScreenReduxHooks = () => {
const todos = useSelector((state: AppState) => state.todos, shallowEqual);
const error = useSelector((state: AppState) => state.error, shallowEqual);
const isLoading = useSelector(
(state: AppState) => state.isLoading,
shallowEqual,
);
const dispatch = useDispatch();
const todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
const sendTodoToServer = async () => {
const todo = todoGenerator();
await dispatch(addTodoAction(todo));
if (error) {
console.log('error', error);
}
};
return (
<View>
<Button title="Generate todo" onPress={sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
};
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreenReduxHooks);
№ 3. Stateless component with redux HOOKS. Console:
It's the same here, as in the second example.
Questions:
Can redux be connected to a stateless component?
How do you make the second and third example work the same way as the first?
Other code:
App.js
import React from 'react';
import {Provider} from 'react-redux';
import {MainScreen, MainScreenFC, MainScreenReduxHooks} from './src/screens';
import store from './src/redux';
const App = () => {
return (
<Provider store={store}>
<MainScreenFC />
</Provider>
);
};
export default App;
store.js:
import {applyMiddleware, createStore} from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from './reducer';
export default createStore(rootReducer, applyMiddleware(thunk, logger));
reducer.js:
const ADD_TODO_REQUEST = 'ADD_TODO_REQUEST';
const ADD_TODO_SUCCESS = 'ADD_TODO_SUCCESS';
const ADD_TODO_FAILURE = 'ADD_TODO_FAILURE';
const initialState = {
todos: [],
isLoading: false,
error: undefined,
};
export const addTodo = data => async dispatch => {
dispatch({
type: ADD_TODO_REQUEST,
payload: {
isLoading: true,
},
});
try {
const todo = await new Promise((resolve, reject) => {
setTimeout(() => {
reject('Ooops, error');
}, 3000);
});
dispatch({
type: ADD_TODO_SUCCESS,
payload: {
todo,
isLoading: false,
},
});
} catch (e) {
dispatch({
type: ADD_TODO_FAILURE,
payload: {
isLoading: false,
error: e,
},
});
}
};
export default function(state = initialState, {type, payload}) {
switch (type) {
case ADD_TODO_REQUEST: {
return {
...state,
isLoading: true,
};
}
case ADD_TODO_SUCCESS: {
return {
...state,
isLoading: false,
todos: [...state.todos, payload.todo],
};
}
case ADD_TODO_FAILURE: {
return {
...state,
isLoading: false,
error: payload,
};
}
default:
return state;
}
}
I think the problem with your code is that you try to wait for a response in the component, it's a bad idea for both stateful and stateless components. What would I recommend you to do, is to handle error somewhere in your middleware (redux-thunk, redux-saga, etc). In this case, your component should just represent data, and if you have to display an error, just take it from props, I believe it is stored somewhere in redux.
In any way, stateless components shouldn't be an async function, because the result of an async function is a promise but not a component. There are some libraries like async-reactor, but personally, I prefer to go with another approach.
If you tell me your use case, what would you like to do once you got an error, I'll give you a more useful answer.
Update:
export const addTodo = data => dispatch => {
// tell your application that request is sending,
// so you can handle it in UI (show a progress indicator)
dispatch({
type: ADD_TODO_REQUEST,
payload: data
});
try {
const response = await createTodo(data);
dispatch({
type: ADD_TODO_SUCCESS,
payload: response
});
// here you can dispatch navigation action as well
} catch (error) {
dispatch({
type: ADD_TODO_FAILURE,
error
});
// and here you can dispatch action with a toast
// to notify users that something went wrong
}
};

Dispatching an action does not re-render the app automatically

I have deleted an item from the redux store. but it does not re-render the application. if I manually rerender the app, then deleted item went. I am doing this method for reading and un-reading the message. but it works fine. deleting also work, but it is not re-render the app automatically. if I trigger the re-render manually then the message was gone.
Reducer.js
import { combineReducers } from 'redux'
import {
DELETE_MESSAGE,
ADD_MESSAGE,
READ_MESSAGE,
UN_READ_MESSAGE,
ARCHIVED_MESSAGE,
UN_ARCHIVED_MESSAGE
} from './Actions'
// reducers
const messageReducer = (state = [], action) => {
switch (action.type) {
case DELETE_MESSAGE:
return state.filter(value => value.key != action.payload.key)
case ADD_MESSAGE:
return [...state, action.payload]
case READ_MESSAGE:
return state.map(value => {
if (value.key == action.payload.key) {
value.read = 'true'
return value
}
return value
})
case UN_READ_MESSAGE:
return state.map(value => {
if (value.key == action.payload.key) {
value.read = 'false'
return value
}
return value
})
case ARCHIVED_MESSAGE:
return state.map(value => {
if (value.key == action.payload.key) {
value.archived = 'true'
return value
}
return value
})
case UN_ARCHIVED_MESSAGE:
return state.map(value => {
if (value.key == action.payload.key) {
value.archived = 'false'
return value
}
return value
})
default:
return state
}
}
// combine reducer
const reducer = combineReducers({
message: messageReducer
})
export default reducer
Actions.js
// action types
export const DELETE_MESSAGE = 'DELETE_MESSAGE'
export const ADD_MESSAGE = 'ADD_MESSAGE'
export const READ_MESSAGE = 'READ_MESSAGE'
export const UN_READ_MESSAGE = 'UN_READ_MESSAGE'
export const ARCHIVED_MESSAGE = 'ARCHIVED_MESSAGE'
export const UN_ARCHIVED_MESSAGE = 'UN_ARCHIVED_MESSAGE'
// action creators
export const deleteMessage = (message) => ({
type: DELETE_MESSAGE,
payload: message
})
export const addMessage = (message) => ({
type: ADD_MESSAGE,
payload: message
})
export const readMessage = (message) => ({
type: READ_MESSAGE,
payload: message
})
export const unReadMessage = (message) => ({
type: UN_READ_MESSAGE,
payload: message
})
export const archivedMessage = (message) => ({
type: ARCHIVED_MESSAGE,
payload: message
})
export const unArchivedMessage = (message) => ({
type: UN_ARCHIVED_MESSAGE,
payload: message
})
deleting page .js
import React from 'react'
import { StyleSheet, View, TouchableHighlight, Alert } from 'react-native'
import { readMessage } from '../../../Redux/Actions'
import { unReadMessage } from '../../../Redux/Actions'
import { deleteMessage } from '../../../Redux/Actions'
import { connect } from 'react-redux'
import Icons from './Icon' // trash-alt
class HiddenRight extends React.Component {
delete = (data) => {
Alert.alert(
'Would you like to Delete?',
'You will permanently remove this message from your mobile local storage',
[
{text: 'Cancel', onPress: () => this.props.delete({})},
{text: 'Delete', onPress: () => this.props.delete(data)}
]
)
}
render () {
return (
<View style={styles.container}>
<TouchableHighlight onPress={() => this.delete(this.props.data) }>
<Icons iconName='trash' title='Delete' backgroundColor='#f80101' />
</TouchableHighlight>
{this.props.data.read == 'false'
? <TouchableHighlight onPress={() => this.props.read(this.props.data)}>
<Icons iconName='envelope' title='Read' backgroundColor='#007AFF' />
</TouchableHighlight>
: <TouchableHighlight onPress={() => this.props.unRead(this.props.data)}>
<Icons iconName='envelope-open' title='UnRead' backgroundColor='gray' />
</TouchableHighlight>
}
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end'
}
})
export default connect(null, { delete: deleteMessage, read: readMessage, unRead: unReadMessage})(HiddenRight)
Store.js
import { createStore } from 'redux'
import reducer from './Reducer'
const DEFAULT_STATE = {
message: [{
'key': '0',
'title': 'Hello, It about my self. Keep watch',
'date': '02/89/3456',
'body': 'Nive to here you all, I am not crazy, Well will you find. yes Of course. I will be there on 56-78-2',
'read': 'false',
'archived': 'false'
}, {
'key': '1',
'title': 'Hello, It about my self. Keep watch',
'date': '02/89/3456',
'body': 'Nive to here you all, I am not crazy, Well will you find. yes Of course. I will be there on 56-78-2',
'read': 'false',
'archived': 'false'
}]
}
// store
const store = createStore(reducer, DEFAULT_STATE)
export default store
MessageScreen.js
...
...
function mapStateToProps(state){
return {
listViewData:state
}
}
export default connect(mapStateToProps)(MessageScreen)
I am expecting that if press delete button, then the message should remove.
Note: read and unread are working fine
I have solved this issue. The mistake I did is, I was storing the prop value which comes from the store directly inside the component state. So it's not re-render because when props change only the render & componentPropsWillChange run. so storing the value in the state means data not change the state first time when a prop changes. Thus the case I have faced.