How to prevent duplicate data in React-Native Hooks with Redux - react-native

I'm new in redux, I'm using react native hooks and redux. my problem is that after opening the articleScreen.js page then clicking the back button and opening the articleScreen.js page again, the data is rendered again so that there is the same data and display the same data repeatedly when clicked on the articleScreen.js page. is there something wrong with my code below?.
=> ArticleScreen.js
const ArticleScreen = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.articleReducer.data);
const isLoading = useSelector((state) => state.articleReducer.isLoading);
const [loadingMore, setLoadingMore] = useState(false);
const [page, setPage] = useState(1);
const currentPage = useSelector((state) => state.articleReducer.currentPage);
const totalPage = useSelector((state) => state.articleReducer.totalPage);
const nextPage = useSelector((state) => state.articleReducer.nextPage);
useEffect(() => {
dispatch(fetchingArticle({ page: 1 }));
}, [])
const _renderItem = ({ item, index }) => {
return (
<TouchableOpacity
key={`${item.id} ${page}`}
style={{ marginBottom: 16 }}
>
<View style={{ flexDirection: 'row' }}>
<View style={{ flex: 1 }}>
<Text>{item.title}</Text>
</View>
</View>
</TouchableOpacity>
);
};
const handleLoadMoreData = () => {
if (!isLoading) {
setLoadingMore(true)
if (nextPage <= totalPage) {
dispatch(fetchingArticle(nextPage));
} else {
setLoadingMore(false)
}
}
}
return (
<>
<View>
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={item => `${item.id}`}
onEndReached={handleLoadMoreData}
onEndReachedThreshold={0.01}
ListFooterComponent={
loadingMore && (
<View
style={{
marginVertical: 30,
}}>
<ActivityIndicator
size="large"
color={Colors.onSurface}
/>
</View>
)
}
/>
</View>
</>
);
};
export default ArticleScreen;
=> store.js
const middleware = applyMiddleware(thunk);
// Root Reducer
const rootReducer = combineReducers({
articleReducer: ArticleReducer,
});
// Redux Store
const store = createStore(
rootReducer,
middleware
)
export default store;
=> ArticleAction.js
export const fetchArticleRequest = () => ({
type: 'FETCH_ARTICLE_REQUEST',
});
export const fetchArticleSuccess = (data) => ({
type: 'FETCH_ARTICLE_SUCCESS',
payload: data.data
});
export const fetchArticleFailure = (error) => ({
type: 'FETCH_ARTICLE_FAILURE',
payload: error
});
export const fetchingArticle = (page) => {
return async dispatch => {
dispatch(fetchArticleRequest());
return apiRequest.get(URL.articles + '?page=' + page).then((response) => {
dispatch(fetchArticleSuccess(response.data));
})
.catch((error) => {
dispatch(fetchArticleFailure("Request Data Error"))
})
}
}
=> ArticleReducer.js
const initialState = {
data: [],
error: '',
isLoading: false,
refreshing: false,
currentPage: 1,
totalPage: 1,
nextPage: 0,
totalData: 0
};
const ArticleReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_ARTICLE_REQUEST':
return {
...state,
isLoading: true,
refreshing: true,
};
case 'FETCH_ARTICLE_SUCCESS':
return {
...state,
data: state.data.concat(action.payload.data),
isLoading: false,
refreshing: false,
currentPage: action.payload.current_page,
totalPage: action.payload.last_page,
nextPage: action.payload.current_page + 1,
};
case 'FETCH_ARTICLE_FAILURE':
return {
...state,
error: action.error,
isLoading: false,
refreshing: false,
};
default:
return state;
}
};
export { ArticleReducer };

I do not know Redux but maybe you need to load your data only one time and not every time you call your page unless you clear that you have before fetching them.
I will let other people who know better that me redux (I can't comment so do not count this answer as answer!)

The problem is with you reducer. You are concating previous data with new data
as
data: state.data.concat(action.payload.data)
In case 'FETCH_ARTICLE_SUCCESS': Change above line to
data: action.payload.data
Like
case 'FETCH_ARTICLE_SUCCESS':
return {
...state,
data: action.payload.data,
isLoading: false,
refreshing: false,
currentPage: action.payload.current_page,
totalPage: action.payload.last_page,
nextPage: action.payload.current_page + 1,
};

Related

React Native bottom sheet opens when TextInput has autofocus prop

I am using react-native-bottom-sheet for choosing a category and when the Amount input has the autofocus property the bottom sheet appears on the screen. And it looks like this
The bottom sheet appears behind the keyboard, and it doesn't follow the snap point I want, it should be at the middle of the screen but it is always slightly above the keyboard. This doesn't happen all the time, like 2/5 tries, and only when I open this screen for the first time. The sheet should only open when I tap on the category and it should be hidden when entering the screen.
I tried to use some variable render to check if the bottom sheet even needs to render or not, so I don't render it at all but then I could see it just flicker for a second.
This is my bottom sheet component. Because I want to create a reusable component I used ref to pass the function for opening the bottom sheet to the parent.
const TransactionBottomSheet: React.ForwardRefRenderFunction<refProps, Props> = (props, ref) => {
const { onSelect } = props;
const sheetRef = useRef<BottomSheet>(null);
const [data, setData] = useState<Transaction[]>(categoriesData);
const [selectedCategory, setSelectedCategory] = useState<Category | null>(null);
const openSheet = useCallback(() => {
sheetRef.current?.expand();
}, []);
const closeSheet = useCallback(() => {
sheetRef.current?.close();
}, []);
useImperativeHandle(ref, () => ({
openSheet: () => openSheet(),
}));
const setTypeData = (id: number) => {
const types = transactionCategories[id].types ?? [];
setData(Object.values(types));
};
const clearCategory = () => {
setData(categoriesData);
setSelectedCategory(null);
};
const onRowPress = (item: Transaction | Category) => {
if (!selectedCategory) {
setTypeData(item.id);
setSelectedCategory(item as Category);
} else {
onSelect(selectedCategory, item);
closeSheet();
}
};
const onClose = () => {
setData(categoriesData);
setSelectedCategory(null);
};
const renderItem = ({ item }: { item: Transaction | Category }) => (
<TransactionRowSelect item={item} hideIcon={!!selectedCategory} onPress={onRowPress} />
);
const renderBackdrop = useCallback(
(props) => <BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} />,
[]
);
return (
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
enablePanDownToClose
index={-1}
onClose={onClose}
backdropComponent={renderBackdrop}
handleStyle={styles.handle}
>
<Label style={styles.title}>{"Pick category"}</Label>
{!!selectedCategory && (
<>
<TransactionRowSelect item={selectedCategory} onPress={clearCategory} />
<Separator />
</>
)}
<BottomSheetFlatList
data={data}
keyExtractor={(item) => `${item.id}`}
renderItem={renderItem}
/>
</BottomSheet>
);
};
const styles = StyleSheet.create({
title: {
textAlign: "center",
fontSize: 16,
fontWeight: "bold",
paddingBottom: 10,
backgroundColor: colors.grey3,
},
handle: {
backgroundColor: colors.grey3,
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
});
export default React.forwardRef(TransactionBottomSheet);
And this is Add transaction screen
const TransactionForm: React.FC<Props> = ({ navigation }) => {
const [date, setDate] = useState(new Date());
const sheetRef = useRef<TransactionBottomSheetType>(null);
const [category, setCategory] = useState<Category | null>(null);
const [type, setType] = useState<Transaction | null>(null);
const [amount, setAmount] = useState("");
const [description, setDescription] = useState("");
const [tryCreateNewTransaction, { isLoading }] = useCreateNewTransactionMutation();
const onAdd = async () => {
Keyboard.dismiss();
try {
if (type && category) {
await tryCreateNewTransaction({
amount: Number(amount),
description,
date: formatIsoDate(date),
user_id: 1,
type_id: type.id,
category_id: category.id,
}).unwrap();
navigation.goBack();
}
} catch (error) {
Alert.alert("An error occurred while adding transaction", "Please try again");
}
};
const onSelectCategory = (category: Category, type: Transaction) => {
setCategory(category);
setType(type);
};
const setCategoryText = () => {
if (!category && !type) {
return "";
}
return `${category?.label}, ${type?.label}`;
};
const openSheet = () => {
if (sheetRef?.current) {
Keyboard.dismiss();
sheetRef?.current?.openSheet();
}
};
return (
<View style={styles.container}>
<DatePickerInput date={date} maximumDate={new Date()} onDateSelect={setDate} />
// This is just TextInput component with some custom style, here is the autofocus props
<LabelInput
value={amount}
placeholder='Amount'
onChangeText={setAmount}
keyboardType='decimal-pad'
style={styles.marginTop}
icon={<FontAwesome5 name='coins' size={24} color={colors.greenMint} />}
autoFocus
/>
{/* <InputErrorLabel text={errors.amount} isVisible={!!errors.amount} /> */}
<TouchableOpacity onPress={openSheet}>
<LabelInput
value={setCategoryText()}
icon={<MaterialIcons name='category' size={24} color={colors.greenMint} />}
disabled
placeholder='Category'
style={styles.marginTop}
inputStyle={styles.category}
/>
</TouchableOpacity>
<TextBox
placeholder='Transaction comment'
style={styles.marginTop}
numberOfLines={6}
maxLength={300}
value={description}
onChangeText={setDescription}
/>
<CustomButton title='Submit' onPress={onAdd} style={styles.marginTop} />
<AppActivityIndicator isLoading={isLoading} />
<TransactionBottomSheet ref={sheetRef} onSelect={onSelectCategory} />
</View>
);
};
export default TransactionForm;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 16,
paddingTop: 20,
},
marginTop: {
marginTop: 20,
},
category: {
color: colors.black,
},
});
Found a fix for the problem, it looks like it had to do with the keyboard and expo. I noticed that when the keyboard is shown or dismissed there is a white background showing behind it for a split second. I fixed this by adding this to app.json
"android": {
"softwareKeyboardLayoutMode": "pan"
},
This fixed problem with the keyboard and bottom sheet also. Didn't see it appear anymore.
Listen to the keyboard show/hide and do whatever you want with the bottom sheet.
for example, here I'm changing the snapping point of the bottom sheet.
React.useEffect(() => {
let keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => {
if (isBottomSheetOpen.current) {
bottomSheetRef.current?.snapToIndex(0);
}
});
let keyboardShowListener = Keyboard.addListener('keyboardDidShow', () => {
if (isBottomSheetOpen.current) {
bottomSheetRef.current?.snapToIndex(1);
}
});
return () => {
keyboardHideListener.remove();
keyboardShowListener.remove();
};
}, []);

how to use react native redux in react navigation

I am using redux persist with asyncstorage save items in a bookmarks list. The items are in a flatlist and when I click on one item, it navigates me to another screen. I would like to implement the functional bookmark in the header of that screen.
When I tried doing this, and clicked the bookmark in the header, and go back to the bookmarks, it just shows a blank card. It looks like it is not updating the state properly. How can I fix this?
StackNavigator.tsx
const MainStackNavigator = () => {
const { books, bookmarks } = useAppSelector((state) => state.booksReducer);
const dispatch = useDispatch();
const fetchBooks = () => dispatch(getBooks());
const addToBookmarkList = (book) => dispatch(addBookmark(book));
const removeFromBookmarkList = (book) => dispatch(removeBookmark(book));
useEffect(() => {
fetchBooks();
}, []);
const handleAddBookmark = (book) => {
addToBookmarkList(book);
};
const handleRemoveBookmark = (book) => {
removeFromBookmarkList(book);
};
const handleSwapBookmark = (book) => {
removeFromBookmarkList(book);
};
const RenderItem = () => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(i) ? handleRemoveBookmark(i) : handleAddBookmark(i)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};
return (
<AppStack.Navigator>
<AppStack.Screen
name="BookmarksScreen"
component={BookmarksScreen}
options={{
title: "Search",
statusBarColor: isDarkMode ? "white" : "black",
headerLargeTitle: true,
headerTranslucent: true,
headerLargeTitleHideShadow: true,
}}
/>
<AppStack.Screen
name="Screen2"
component={Screen2}
options={({ route }) => ({
headerLargeTitle: false,
title: route.params.name,
headerTranslucent: true,
headerRight: () => <RenderItem item={route.params.name} />,
})}
/>
</AppStack.Navigator>
);
};
actions.js
import axios from "axios";
import { BASE_URL } from "../config";
// Define action types
export const GET_BOOKS = "GET_BOOKS";
export const ADD_TO_BOOKMARK_LIST = "ADD_TO_BOOKMARK_LIST";
export const REMOVE_FROM_BOOKMARK_LIST = "REMOVE_FROM_BOOKMARK_LIST";
export const SWAP_IN_BOOKMARK_LIST = "SWAP_IN_BOOKMARK_LIST";
export const getBooks = () => {
try {
return async (dispatch) => {
const response = await axios.get(`${BASE_URL}`);
if (response.data) {
dispatch({
type: GET_BOOKS,
payload: response.data,
});
} else {
console.log("Unable to fetch data from the API BASE URL!");
}
};
} catch (error) {
console.log(error);
}
};
export const addBookmark = (book) => (dispatch) => {
dispatch({
type: ADD_TO_BOOKMARK_LIST,
payload: book,
});
};
export const removeBookmark = (book) => (dispatch) => {
dispatch({
type: REMOVE_FROM_BOOKMARK_LIST,
payload: book,
});
};
hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
reducers.ts
import {
GET_BOOKS,
ADD_TO_BOOKMARK_LIST,
REMOVE_FROM_BOOKMARK_LIST,
} from "./actions";
const initialState = {
books: [],
bookmarks: [],
};
function booksReducer(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
return { ...state, books: action.payload };
case ADD_TO_BOOKMARK_LIST:
return { ...state, bookmarks: [...state.bookmarks, action.payload] };
case REMOVE_FROM_BOOKMARK_LIST:
return {
...state,
bookmarks: state.bookmarks.filter(
(book) => book.id !== action.payload.id
),
};
default:
return state;
}
}
export default booksReducer;
store.ts
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { persistStore, persistReducer } from "redux-persist";
import booksReducer from "./reducers";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["bookmarks"],
};
const rootReducer = combineReducers({
booksReducer: persistReducer(persistConfig, booksReducer),
});
export const store = createStore(rootReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
BookmarksScreen.tsx
const BookmarksScreen = () => {
return (
<View>
<FlatList
data={bookmarks}
keyExtractor={(item) => item.id}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
</View>
);
}
renderItem
const renderItem = ({ item }) => {
return (
<TouchableOpacity
onPress={() =>
navigation.navigate("Screen2", {name: item.name})}
>
<View style={{ flexDirection: "row", flex: 1 }}>
<View>
<Text
style={{
fontSize: 22,
paddingRight: 16,
color: "black",
fontFamily: "Medium",
left: 45,
top: 6,
}}
>
{item.country}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
};
I think you forgot to access props that's the reason booked-marked not working i change some code please check it's working or not.
StackNavigator.tsx
const RenderItem = (item) => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(item) ? handleRemoveBookmark(item) : handleAddBookmark(item)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};

React-Native Searchable FlatList with Local Json File

I am trying to create a searchable flatlist on this new app I was working on over quarantine. I followed an article on the internet on how to create it and my code so far looks like this:
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;
This works and all, but how could I alter the makeRemoteRequest function so that I got data from a local json file instead of a url? For example:
makeRemoteRequest = () => {
const url = `../data/userData.json`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
I tried this with no success as none of the json data rendered and appeared in the flatlist
The Fetch API is a promise-based JavaScript API for making asynchronous HTTP requests in the browser similar to XMLHttpRequest. If you want to load data from a local JSON file.
First import that file
import Data from '../data/userData.json'
Then assign imported value to your state inside your makeRemoteRequest function
makeRemoteRequest = () => {
this.setState({
data: Data,
});
}
Hope this helps you. Feel free for doubts.

How to expand and collapse specify section using SecionList?

I call an api https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=Miaoli&sTime=21&eTime=24 to my react-redux action and use SectionList to show the data.
With official tutorial, i use SectionList it will just show all of the section, i try to find the way when click title that can expand or collapse the section.
I find my sectionComp and renderSectionItem use the same title so i try use this.state{ title: '', expand: false }
When i click 國興戲院 use this.setState({ title: '國興戲院', expand: true }) and use like if(this.state.expand) {} in renderSectionItem
Obviously its not working because i may have a lot of section.
I have no idea what next step should i try.
Any help would be appreciated. Thanks in advance.
Here is my SectionList data:
Here is my class component:
import React, { Component } from 'react';
import { Text, SectionList, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { View } from 'react-native-animatable';
import { fetchSearchTime } from '../actions';
import { Spinner } from './common';
import GetUserTime from '../function/common/GetUserTime';
class MovieCloseTime extends Component {
constructor(props) {
super(props);
const { selectedCity, firstSliderValue, secondSliderValue }
= this.props.navigation.state.params;
this.state = {
selectedCity,
firstSliderValue,
secondSliderValue,
};
}
componentDidMount() {
const { selectedCity, firstSliderValue, secondSliderValue } = this.state;
this.props.fetchSearchTime({
selectedCity, firstSliderValue, secondSliderValue
});
}
sectionComp = (info) => {
const theaterCn = info.section.title;
console.log('Title info is =>');
console.log(info);
return (
<TouchableOpacity
onPress={() => console.log('Hot to expand and collapse specify section when click here?')}
>
<View style={{ flex: 1, backgroundColor: '#DCDCDC' }}>
<Text style={styles.sectionTitle}>{theaterCn}</Text>
</View>
</TouchableOpacity>
);
}
renderSectionItem = (info) => {
const cnName = info.item.cnName;
console.log('Section info is =>');
console.log(info);
return (
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('MovieDetail', {
enCity: this.state.selectedCity,
cnName
});
}
}
>
<View style={{ flex: 1, flexDirection: 'column' }}>
<Text style={styles.sectionSubTitle}>{cnName}</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', backgroundColor: '#F8F8FF' }}>
{info.item.releasedTime.map((value, index) => {
const theTime = GetUserTime.getAsiaTime(value, 'YYYY/MM/DD HH:mm:ss');
const hour = theTime.getHours();
const minute = (theTime.getMinutes() < 10 ? '0' : '') + theTime.getMinutes();
return (
<Text style={styles.sectionTimeTitle} key={index}>{`${hour}:${minute}`}</Text>
);
})
}
</View>
</View>
</TouchableOpacity>
);
}
render() {
const movieData = this.props.searchTime;
if (this.props.loading) {
return <Spinner text='Loading...' />;
}
console.log('movieData is =>');
console.log(movieData);
return (
<View style={{ flex: 1 }}>
<SectionList
renderSectionHeader={this.sectionComp}
renderItem={this.renderSectionItem}
sections={movieData}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const searchTime = state.searchTime.searchList;
const loading = state.searchTime.loading;
return { searchTime, loading };
};
const styles = {
// some styles
};
export default connect(mapStateToProps, { fetchSearchTime })(MovieCloseTime);
Here is my action fetchSearchTime:
export const fetchSearchTime = ({ selectedCity, firstSliderValue, secondSliderValue }) => {
return (dispatch) => {
dispatch({ type: SEARCH_TIME_REQUEST });
console.log(`https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=${selectedCity}&sTime=${firstSliderValue}&eTime=${secondSliderValue}`);
fetch(`https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=${selectedCity}&sTime=${firstSliderValue}&eTime=${secondSliderValue}`)
.then(response => response.json())
.then(responseData => {
const movieData = responseData.reduce((r, s) => {
r.push({ title: s.theaterCn, id: s._id, expand: true, data: s.movie });
return r;
}, []);
//dispatch({ type: SEARCH_TIME, payload: responseData });
dispatch({ type: SEARCH_TIME, payload: movieData });
})
.catch((error) => console.log(error));
};
};
about type SEARCH_TIME reducer:
// with others type
import {
SEARCH_TIME_REQUEST,
SEARCH_TIME
} from '../actions/types';
const INITIAL_STATE = {
searchList: [],
loading: true,
selectedCity: '',
firstSliderValue: '',
secondSliderValue: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case SEARCH_TIME_REQUEST:
return {
searchList: [],
loading: true,
};
case SEARCH_TIME:
return {
searchList: action.payload,
loading: false
};
default:
return state;
}
};

Navigation is not called right after user is unauthenticated

I am trying to logout the user and then send him to SignedOut screen but when I press on Sign Out it is calling the unauthUser function but then it restart the switch navigator to dashboard and I have to go in profile screen to tap again on Sign Out to go out. Any idea what am I doing wrong in all this?
Here is my button from Profile.js
<TouchableOpacity
onPress={() => onSignOut().then(() => {
this.props.dispatch(unauthUser)
navigation.navigate("SignedOut")
}
)}
>
<View style={styles.signOutButton}>
<Text style={styles.button}>SIGN OUT</Text>
</View>
</TouchableOpacity>
onSignOut() from Auth.js
export let USER_KEY = 'myKey';
export const onSignIn = async () => { await AsyncStorage.setItem(USER_KEY, 'true') };
export const onSignOut = async () => { await AsyncStorage.removeItem(USER_KEY) };
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(USER_KEY)
.then(res => {
if (res !== null) {
// console.log('true')
resolve(true);
} else {
resolve(false);
// console.log('false')
}
})
.catch(err => reject(err));
});
};
unauthUser from authActions.js
exports.unauthUser = {
type: 'UNAUTH_USER'
}
and from authReducer.js
case 'UNAUTH_USER':
return {
user_id: undefined,
token: undefined
};
and here is my switch navigator
export const createRootNavigator = (signedIn = false) => {
return SwitchNavigator(
{
SignedIn: {
screen: SignedIn
},
SignedOut: {
screen: SignedOut
}
},
{
initialRouteName: signedIn ? "SignedIn" : "SignedOut"
}
);
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
signedIn: false,
checkedSignIn: false
};
}
async componentWillMount() {
await isSignedIn()
.then(res => this.setState({ signedIn: res, checkedSignIn: true}))
.catch(err => alert("An error occurred"));
}
render() {
const { checkedSignIn, signedIn } = this.state;
// If we haven't checked AsyncStorage yet, don't render anything (better ways to do this)
if (!checkedSignIn) {
return null;
}
const Layout = createRootNavigator(signedIn);
return (
<SafeAreaView style={styles.safeArea}>
<View style={{flex: 1, backgroundColor: '#ffffff'}}>
<StatusBar barStyle="light-content"/>
<Layout />
<AlertContainer/>
</View>
</SafeAreaView>
)
}
};