React Native - searchApi is not a function - react-native

I am new in React Native. I try to create a simple searching food restaurant with Yelp. Unfortunately, I get an error:
"searchApi is not a function. (in 'searchApi(term)', 'searchApi' is
"")
Below my code.
useResults.js
import React, { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [result, setResult] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async (searchTerm) => {
console.log("hi there");
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term: searchTerm,
location: 'san jose'
}
});
setErrorMessage(null);
setResult(response.data.businesses);
} catch (err) {
setErrorMessage('Something Went Wrong');
}
};
/*
useEffect(() => {}); //Run the arrow function everytime the component is rendered
useEffect(() => {}, []); // Run the arrow function only when the component is first rendered
useEffect(() => {}, [value]); // Run the arrow function only when the component is first rendered, and when the value is changes
*/
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, result, errorMessage];
};
SearchScreen.js
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import ResultList from '../components/ResultList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = () => {
const [term, setTerm] = useState('');
const [searchApi, result, errorMessage] = useResults();
console.log(result);
return (
<View>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={() => searchApi(term)}
/>
<View>{errorMessage ? <Text>{errorMessage}</Text> : null}</View>
<Text>We have found {result.length} results</Text>
<ResultList title="Cost Effective" />
<ResultList title="Bit Pricier" />
<ResultList title="Big Spender"/>
</View>
);
};
const styles = StyleSheet.create({
});
export default SearchScreen;
edit :
SearchBar.js
import React from 'react';
import { View, Text, StyleSheet, TextInput } from 'react-native';
import { Feather } from '#expo/vector-icons';
const SearchBar = ({ term, onTermChange, onTermSubmit }) => {
return (
<View style={styles.backgroundStyle}>
<Feather style={styles.iconStyle} name="search" size={30} color="black" />
<TextInput style={styles.inputStyle}
autoCapitalize="none"
autoCorrect={false}
placeholder="Search"
value={term}
onChangeText={onTermChange}
onEndEditing={onTermSubmit}
/>
</View>
)
};
const styles = StyleSheet.create({
backgroundStyle: {
marginTop: 10,
backgroundColor: '#F0EEEE',
height: 50,
borderRadius: 5,
marginHorizontal: 15,
flexDirection: 'row'
},
inputStyle: {
flex: 1,
fontSize: 18,
marginHorizontal: 10
},
iconStyle: {
fontSize: 35,
alignSelf: 'center'
}
});
export default SearchBar;
When I type in search bar and hit done button, I got the error above.
Seems in useResults.js file this: return [searchApi, result, errorMessage]; does not properly return the function. But the result and errorMessage return successfully.
And in this file: SearchScreen.js the error line is shown in here: onTermSubmit={() => searchApi(term)}.
How to fix this?

Try adding a callback to onChangeText.
<TextInput style={styles.inputStyle}
autoCapitalize="none"
autoCorrect={false}
placeholder="Search"
value={term}
onChangeText={() => onTermChange()} // Add fat arrow function here
onEndEditing={onTermSubmit}
/>

Related

Can't find variable: queriesString - React Native Context

The app is for looking up information about product from an api, either by article or ean code.
User can input Article code or EAN in TextInput or scan a barcode and get the information about the product from the api.
The problem im having is i'm not able to pass the search queries from the Textinput to the queriesString in the Context Provider.
This is the error code im getting.
> ReferenceError: Can't find variable: queriesString
For what i'm able to find out the code should work, but since its my first app in react native im most likely missing something.
App
App.js
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import HomeScreen from "./app/screens/HomeScreen";
import ScannerScreen from "./app/screens/ScannerScreen";
import { queriesContext } from "./app/global/queriesContext";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<>
<queriesContext.Provider value={{ queriesString: "123456789" }}>
{console.log(queriesString + " - From App")}
<NavigationContainer>
<Stack.Navigator initialRouteName='Home' screenOptions={{ headerShown: false }}>
<Stack.Screen name='Home' component={HomeScreen} />
<Stack.Screen name='Scanner' component={ScannerScreen} />
</Stack.Navigator>
</NavigationContainer>
</queriesContext.Provider>
</>
);
}
HomeScreen
HomeScreen.js
import { StyleSheet, Text, View, Image, SafeAreaView, Platform, TextInput, Button, TouchableWithoutFeedback, Keyboard } from "react-native";
import { useNavigation } from "#react-navigation/native";
import React, { useContext, useState, useEffect } from "react";
import ProductScreen from "../global/GetData";
import { queriesContext } from "../global/queriesContext";
export default function HomeScreen() {
const [queriesString, setQueriesString] = useContext(queriesContext);
const [queries, setQueries] = useState(queriesString);
const navigation = useNavigation();
const handleSearch = () => {
setQueriesString(queries);
};
ProductScreen();
//console.log(useState.q);
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<SafeAreaView style={styles.MainScreen}>
<ProductScreen />
<TextInput style={styles.TextField} editable={true} maxLength='30' numberOfLines='1' placeholder='Art no.' keyboardType='numeric' onChange={(text) => setQueries(text)} />
<Button color='red' title='Search' onPress={handleSearch} />
<View style={styles.BarcodeBox}>
<TouchableWithoutFeedback onPress={() => navigation.navigate("Scanner")}>
<Image style={styles.BarcodeImage} source={require("../assets/barcode.png")} />
</TouchableWithoutFeedback>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
);
}
const styles = StyleSheet.create({
MainScreen: {.............},
});
ScannerScreen
Not complete since i can't get to pass the value from HomeScreen to context.
ScannerScreen.js
import React, { Component, useState, useEffect } from "react";
import { Text, View, Alert, StyleSheet } from "react-native";
import { BarCodeScanner } from "expo-barcode-scanner";
export default function ScannerScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
const getBarCodeScannerPermissions = async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
};
getBarCodeScannerPermissions();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
Alert.alert("Strekkode", data, [{ text: "Yes", onPress: () => setScanned(false) }, { text: "No" }]);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<BarCodeScanner onBarCodeScanned={scanned ? undefined : handleBarCodeScanned} style={[StyleSheet.absoluteFill, styles.container]}>
<View style={styles.layerTop} />
<View style={styles.layerCenter}>
<View style={styles.layerLeft} />
<View style={styles.focused} />
<View style={styles.layerRight} />
</View>
<View style={styles.layerBottom} />
</BarCodeScanner>
);
}
const opacity = "rgba(0, 0, 0, .5)";
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
},
layerTop: {
flex: 2,
backgroundColor: opacity,
},
layerCenter: {
flex: 1,
flexDirection: "row",
},
layerLeft: {
flex: 1,
backgroundColor: opacity,
},
focused: {
flex: 10,
},
layerRight: {
flex: 1,
backgroundColor: opacity,
},
layerBottom: {
flex: 2,
backgroundColor: opacity,
},
});
GetData
GetData.js
import { StyleSheet, Text, View, Image, SafeAreaView, Platform, TextInput, Button, TouchableWithoutFeedback, Keyboard } from "react-native";
import React, { useContext, useState, useEffect } from "react";
import { queriesContext } from "./queriesContext";
export default ProductScreen = () => {
const [queriesString] = useContext(queriesContext);
const [isLoading, setLoading] = useState(true);
const [isPromotion, setPromotion] = useState(false);
const [data, setData] = useState({
results: [BEFORE API IS CALLED NOT...],
});
console.log(queriesString + " - From GetData");
useEffect(() => {
fetch(
"SERVER.........",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"x-algolia-agent": "Algolia for JavaScript (4.13.1); Browser; JS Helper (3.10.0)",
"x-algolia-application-id": "***********",
"x-algolia-api-key": "************************",
},
body: JSON.stringify({
requests: [
{
indexName: "prod_products",
params: "query=" + queriesString,
},
],
}),
}
)
.then((respnse) => respnse.json())
.then((json) => setData(json), console.log(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
//console.log(data);
//console.log(quaryString);
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<SafeAreaView style={styles.ProductScreen}>
<View style={styles.LogoBox}>
<Image source={require("../assets/Logo.png")} />
<Image
style={styles.ProductImage}
resizeMode='contain'
source={
isLoading
? require("../assets/fallback_266x266.png")
: {
uri: "https://................/" + data.results[0].hits[0].picture.product,
}
}
/>
</View>
<View>
<View>
<Text style={styles.Title}> {data.results[0].hits[0].name.no} </Text>
<Text style={styles.Desciption}>{data.results[0].hits[0].description.no}</Text>
<Text style={[styles.Text, styles.color]}>{data.results[0].hits[0].potentialPromotionsLabels[5110].text.no}</Text>
<Text style={styles.Text}> Price: {data.results[0].hits[0].code}</Text>
<Text style={styles.Text}> Article No: {data.results[0].hits[0].code}</Text>
<Text style={styles.Text}> EAN: {data.results[0].hits[0].ean}</Text>
</View>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
);
};
const styles = StyleSheet.create({
color: {................},
});
queriesContext
queriesContext.js
import { createContext } from "react";
export const queriesContext = createContext();
Error
ERROR ReferenceError: Can't find variable: queriesString
This error is located at:
in App (created by withDevTools(App))
in withDevTools(App)
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in main(RootComponent)
ERROR ReferenceError: Can't find variable: queriesString
This error is located at:
in App (created by withDevTools(App))
in withDevTools(App)
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in main(RootComponent)
If anyone could help i would greatly appreciate it
I finally got it to work.
Renamed a few things (queriesString, setQueriesString to Quary, setQuary) only to make it easier not to misspell.
I changed the brackets to curly braces in HomeScreen.js and GetData.js as suggested by Fanchen Bao.
In the HomeScreen.js from
export default function HomeScreen() {
const [ Quary, setQuary ] = useContext(queriesContext);
const [queries, setQueries] = useState("0");
To
export default function HomeScreen() {
const { Quary, setQuary } = useContext(queriesContext);
const [queries, setQueries] = useState("0");
And in GetData.js from
export default ProductScreen = () => {
const [ Quary ] = useContext(queriesContext);
to
export default ProductScreen = () => {
const { Quary } = useContext(queriesContext);
Then in the App.js i added a component and useState
export default function App() {
const [Quary, setQuary] = useState("95335");
In GetData i had also imported the context from
import { queriesContext } from "./queriesContext";
This was wrong and i changed this to the correct path
import { queriesContext } from "../global/queriesContext";
After this i still wasn't able to update the context correctly, and i found out that i should use onChangeText instead of onChange in the Textinput.
onChange resulted in the context updating, but the value was not the value in the Textinput field. insted it was returning [object Object].
LOG [object Object]

Customize Action Sheet Message (Expo)

I'm using #expo/react-native-action-sheet, and i want to show a button in the props message.
e.g
import { useActionSheet } from "#expo/react-native-action-sheet"
const { showActionSheetWithOptions } = useActionSheet()
const onPress = () => {
// Here
**const message = '<TouchableOpacity><Text>fsdf</Text></TouchableOpacity>'**
showActionSheetWithOptions(
{
message
},
(buttonIndex) => {
}
)
}
But it is not showing the button as i want
My purpose is to add a date picker in the action sheet.
Expecting answer:
In this case, you can use another library https://gorhom.github.io/react-native-bottom-sheet/ because Action Sheet is about the list of actions.
You can place any content you need for react-native-bottom-sheet and it also supports Expo
import React, { useCallback, useMemo, useRef } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import BottomSheet from '#gorhom/bottom-sheet';
const App = () => {
// ref
const bottomSheetRef = useRef<BottomSheet>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<View style={styles.container}>
<BottomSheet
ref={bottomSheetRef}
index={1}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View style={styles.contentContainer}>
<Text>Awesome 🎉</Text>
</View>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: 'grey',
},
contentContainer: {
flex: 1,
alignItems: 'center',
},
});
export default App;

AsyncStorage doesn't save onboarding when closing react native app

Well, I made a presentation onboarding screen, as soon as the user opens the app, the screen is shown, but I want this to be saved using AsyncStorage, if I close and open the app, the onboarding screen is not shown, but the screen of login.
I did all the code but nothing happens and the screen is displayed every time I close and open the app, I don't know what I'm doing wrong, code below.
App.js
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {AuthProvider} from './src/providers/Auth';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { OnBoarding } from './src/screen/OnBoarding';
import { SignIn } from './src/screen/SignIn';
import { HomeScreen } from './src/screen/HomeScreen';
import { ActivityIndicator } from 'react-native';
const Stack = createNativeStackNavigator();
const Loading = () => {
return (
<View>
<ActivityIndicator size="large" />
</View>
);
}
export function App(){
const [loading, setLoading] = useState(true);
const [viewedOnboarding, setViewedOnboarding] = useState(false);
useEffect(() => {
checkOnBoarding();
}, [])
const checkOnBoarding = async () => {
try{
const value = await AsyncStorage.getItem('#viewedOnboarding');
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}
return (
<AuthProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName={loading ? <Loading /> : viewedOnboarding ? <HomeScreen /> : <OnBoarding />}
screenOptions={
{headerShown: false}
}
>
<Stack.Screen
name="SignIn"
component={SignIn}
/>
</Stack.Navigator>
</NavigationContainer>
</AuthProvider>
);
}
Onboarding.js
import React, { useEffect, useState } from 'react';
import {Text, View, StyleSheet, Image, TouchableOpacity, Button} from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import Icon from 'react-native-vector-icons/FontAwesome';
import AsyncStorage from '#react-native-async-storage/async-storage';
const slides = [
{
key: 1,
title: 'Only Books Can Help You',
text: 'Books can help you to increase your knowledge and become more successfully.',
image: require('../../assets/imagem1.png'),
},
{
key: 2,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
{
key: 3,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
];
export function OnBoarding(){
function renderItem({item}){
return (
<View style={styles.container}>
<Image style={styles.image} source={item.image} />
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.content}>{item.text}</Text>
</View>
);
}
function _renderPrevButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-left"
color="#000"
size={40}
/>
</View>
);
};
function _renderNextButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-right"
color="#000"
size={40}
/>
</View>
);
};
const onPressFinish = async () => {
try{
await AsyncStorage.setItem('#viewedOnboarding', 'true')
navigation.navigate('SignIn');
}catch(err) {
console.log("Error #setitem ", err);
}
};
const renderDoneButton = () => {
return (
<TouchableOpacity onPress={onPressFinish}>
<Text style={{color: "#000"}}>Done</Text>
</TouchableOpacity>
);
};
return (
<AppIntroSlider
data={slides}
renderItem={renderItem}
keyExtractor={item => item.key}
renderPrevButton={_renderPrevButton}
renderNextButton={_renderNextButton}
renderDoneButton={renderDoneButton}
showPrevButton
showDoneButton
dotStyle={{backgroundColor: '#9D9D9D'}}
activeDotStyle={{backgroundColor: '#DE7773'}}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: "#fff"
},
title: {
color: "#292B38",
fontSize: 24,
fontWeight: 'bold',
marginTop: 50
},
content: {
color: "#4D506C",
textAlign: 'center',
padding: 25,
lineHeight: 18
},
image: {
width: 300,
height: 300,
},
button: {
color: "#000",
backgroundColor: "transparent"
}
})
Probably because of the async nature of AsyncStore when you open your app first you init viewedOnboarding with false, then start reading storage and then you check is onboarding done or not. And of course because storage is async first time you check in Navigation condition you always get
I have modified your checkOnBoarding function.
Here is the code:
const checkOnBoarding = () => {
try{
AsyncStorage.getItem('#viewedOnboarding').then(value => {
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
})
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}

How to properly navigate between screen with react-native-navigation?

In my react-native app I have a log-in screen and a home screen.
The logic is: of the log-in process (using Firebase) is successful, and we have a "user" object,
we want to navigate from the log-in screen to the home screen.
When I attempt to implement this, the auth / log-in part works fine, but the "navigation.navigate" part results into this error:
"TypeError: undefined is not an object (evaluating 'navigation.navigate')".
Here is my App.js:
import React, { useEffect, useState } from 'react';
import { Navigation } from 'react-native-navigation';
import LoginScreen from './src/screens/LoginScreen/LoginScreen';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
Button
} from 'react-native';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
const App = (props) => {
return (
<View style={styles.root}>
<LoginScreen />
</View>
);
};
App.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}
Navigation.registerComponent('Home', () => App);
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'Home'
}
}
]
}
}
});
});
const styles = StyleSheet.create({
root: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'whitesmoke'
}
});
export default App;
Here is my log-in screen, which has this error-inducing part:
navigation.navigate('Home', {user: user})
:
import React, { useState, useEffect } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
import { firebase } from '../../firebase/config'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
export default function LoginScreen({ navigation }) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(true)
const [user, setUser] = useState(null)
const Stack = createStackNavigator()
useEffect(() => {
const usersRef = firebase.firestore().collection('users');
firebase.auth().onAuthStateChanged(user => {
if (user) {
usersRef
.doc(user.uid)
.get()
.then((document) => {
const userData = document.data()
setLoading(false)
setUser(userData)
})
.catch((error) => {
setLoading(false)
});
} else {
setLoading(false)
}
});
}, []);
const onFooterLinkPress = () => {
navigation.navigate('Registration')
}
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user: user})
})
.catch(error => {
alert(error)
});
})
.catch(error => {
alert(error)
})
}
return (
<View style={styles.container}>
<KeyboardAwareScrollView
style={{ flex: 1, width: '100%' }}
keyboardShouldPersistTaps="always">
<Image
style={styles.logo}
source={require('../../../assets/icon.png')}
/>
<TextInput
style={styles.input}
placeholder='E-mail'
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setEmail(text)}
value={email}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholderTextColor="#aaaaaa"
secureTextEntry
placeholder='Password'
onChangeText={(text) => setPassword(text)}
value={password}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity
style={styles.button}
onPress={() => onLoginPress()}>
<Text style={styles.buttonTitle}>Log in</Text>
</TouchableOpacity>
<View style={styles.footerView}>
<Text style={styles.footerText}>Don't have an account? <Text onPress={onFooterLinkPress} style={styles.footerLink}>Sign up</Text></Text>
</View>
</KeyboardAwareScrollView>
</View>
)
}
What is the proper way of handling navigation in this scenario?
according to this link , you are trying to register the App component as the 'Home' screen in your navigation container. I don't think that's what you want.
Navigation.registerComponent('Home', () => App);
You will also need to register your Home screen, otherwise you won't be able to navigate to it. Your App component will automatically get injected with the navigation prop, so you just pass it along to the Login component. try the following:
const App = (props) => {
return (
<View style={styles.root}>
<LoginScreen ...props/>
</View>
);
};
App.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}
Navigation.registerComponent('Login', () => App);
Navigation.registerComponent('Home', () => YourHomeComponent);

Unable to store data in redux store

i am trying to build a react-native app in that user can add a routine or daily task in EditRoutine.js file and it can be seen in RoutineOverviewScreen.js and i am using redux for storing these data and using hooks for storing and fetching data.
Below is the EditRoutine.js code snippet
import React, { useState, useEffect, useCallback } from "react";
import { View, StyleSheet, Text, TextInput, ScrollView } from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import Card from "../components/Card";
import { useSelector, useDispatch } from "react-redux";
import * as routinesActions from "../store/actions/routine";
import Routine from "../models/routine";
import HeaderButton from "../components/HeaderButton";
const EditRoutine = (props) => {
const dispatch = useDispatch();
const [title, setTitle] = useState("");
const [detail, setDetail] = useState("");
const [time, setTime] = useState("");
const submitHandler = useCallback(() => {
dispatch(routinesActions.createRoutine(title, detail, time));
props.navigation.goBack();
}, [dispatch,title, detail, time]);
useEffect(() => {
props.navigation.setParams({ submit: submitHandler });
}, [submitHandler]);
return (
<Card style={styles.container}>
<Text>Title</Text>
<TextInput
style={styles.input}
value={title}
onChangeText={(text) => setTitle(text)}
/>
<Text>Details</Text>
<TextInput
style={styles.input}
multiline
numberOfLines={4}
value={detail}
onChangeText={(text) => setDetail(text)}
/>
<Text>Time</Text>
<TextInput
style={styles.input}
value={time}
onChangeText={(text) => setTime(text)}
/>
</Card>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
padding: 10,
width: "100%",
},
input: {
paddingHorizontal: 2,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
width: "100%",
marginVertical: 15,
},
});
EditRoutine.navigationOptions = (navData) => {
const submitFn = navData.navigation.getParam("submit");
return {
headerTitle: "Edit Routine",
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={
Platform.OS === "android" ? "md-arrow-back" : "ios-arrow-back"
}
onPress={() => {
navData.navigation.goBack();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === "android" ? "md-checkmark" : "ios-checkmark"
}
onPress={submitFn}
/>
</HeaderButtons>
),
};
};
export default EditRoutine;
and this is my RoutineOverviewScreen.js file where i am trying to show the created routine
import React from "react";
import { View, StyleSheet, Text, FlatList } from "react-native";
import Card from "../components/Card";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import { useSelector } from "react-redux";
const RoutineOverViewScreen = (props) => {
const routines = useSelector((state) => state.routines.myRoutine);
return (
<FlatList
data={routines}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<Card>
<View>
<Text>{itemData.item.id} </Text>
</View>
<View>
<Text>{itemData.item.title} </Text>
</View>
<View>
<Text>{itemData.item.detail} </Text>
<View>
<Text>{itemData.item.time} </Text>
</View>
</View>
</Card>
)}
/>
);
};
const styles = StyleSheet.create({});
RoutineOverViewScreen.navigationOptions = (navData) => {
return {
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Add"
iconName={
Platform.OS === "android" ? "md-add-circle" : "ios-add-circle"
}
onPress={() => {
navData.navigation.navigate("Edit");
}}
/>
</HeaderButtons>
),
};
};
export default RoutineOverViewScreen;
Below is my action file routine.js snippet
export const CREATE_ROUTINE= 'CREATE_ROUTINE';
export const deleteRoutine = routineId => {
return { type: DELETE_ROUTINE, pid: routineId };
};
export const createRoutine = (title, detail, time) => {
return {
type: CREATE_ROUTINE,
routineData: {
title,
detail,
time
}
};
};
Below is my reducer file reducer.js snippet
import {
DELETE_ROUTINE,
CREATE_ROUTINE,
UPDATE_ROUTINE,
} from "../actions/routine";
import Routine from "../../models/routine";
const initialState = {
myRoutine: {},
id: 1,
};
export default (state = initialState, action) => {
switch (action.type) {
case CREATE_ROUTINE:
const newRoutine = new Routine(
state.id,
action.routineData.title,
action.routineData.detail,
action.routineData.time
);
return {
...state,
items: { ...state.items, [state.id]: newRoutine },
id: state.id + 1,
};
default: {
return state;
}
}
return state;
};
and this is my app.js file snippet
import React, { useState } from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import routinesReducer from './store/reducers/routine';
import AppNavigator from './navigator/RoutineNavigator';
const rootReducer = combineReducers({
routines: routinesReducer,
});
const store = createStore(rootReducer);
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf')
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => {
setFontLoaded(true);
}}
/>
);
}
return (
<Provider store={store}>
<AppNavigator />
</Provider>
);
}
with these above code i am able to create a new routine but i am not knowing whether my input are getting stored in to the App central state because when i am trying to render those saved data i am unable to see in my RoutineOverviewScreen screen.please help me and
About Me: Govind Kumar Thakur ( iamgovindthakur )
email: iamgovindthakur#gmail.com
Thank You :)
You are trying to use the data of "myRoutine" but never save data to this property in the reducer.
I think that the issue you are having is due to the following line in your reducer:
items: { ...state.items, [state.id]: newRoutine },