React Native CRUD with RESTful API & Redux state management - react-native

I'm new in react native and trying to create an app with CRUD operation & RESTful API, but I'm stuck in the UPDATE/PUT, Can anyone help in this matter?? Below are my frontend + backend codes:
Backend side
// update data by id
router.put('/:id', validate, (req, res) => {
const bookId = req.params.id
const errors = validationResult(req)
if(!errors.isEmpty()){
return res.status(422).send({errors: errors.array()})
}
Book.findById(bookId)
.then(book => {
book.bookTitle = req.body.bookTitle,
book.ImgURL = req.body.ImgURL,
book.bookDescription = req.body.bookDescription,
book.bookAuthor = req.body.bookAuthor,
book.bookPrice = req.body.bookPrice,
book.bookTypes = req.body.bookTypes,
book.bookYear = req.body.bookYear,
book.bookRating = req.body.bookRating,
book.bookPages = req.body.bookPages
return book.save()
})
.then(result => res.send(result))
.catch(errors => console.log(errors))
})
Result in POSTMAN, I changed title from how to brew -> how to brew like professional
ReduxAction.js
export const editBook = ({id, bookTitle, ImgURL, bookDescription, bookAuthor, bookPrice, bookTypes, bookYear, bookRating, bookPages}) => {
return async dispatch => {
const response = await fetch(`http://localhost:3000/api/books/${id}`, {
method: 'PUT',
body: JSON.stringify({id, bookTitle, ImgURL, bookDescription, bookAuthor, bookPrice, bookTypes, bookYear, bookRating, bookPages})
})
const responseData = await response.json()
dispatch({
type: EDIT_BOOKS,
payload: responseData
})
}
}
EditScreen.js
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, ScrollView, TextInput, Button, KeyboardAvoidingView, Alert,
ActivityIndicator } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import * as bookAction from '../redux/actions/bookAction'
import { Formik } from 'formik'
import * as Yup from 'yup'
import DropDownPicker from 'react-native-dropdown-picker'
const formSchema = Yup.object({
bookTitle: Yup.string().required('*required').min(5, '*must be between 5 to 50 characters').max(50, '*must be between 5 to 50 characters'),
ImgURL: Yup.string().required('*required'),
bookDescription: Yup.string().required('*required').min(30, '*must be at least 30 characters'),
bookAuthor: Yup.string().required('*required'),
bookPrice: Yup.number().required('*required'),
bookTypes: Yup.string().required('*required'),
bookYear: Yup.number().required('*required'),
bookRating: Yup.number().required('*required'),
bookPages: Yup.number().required('*required')
})
const AddBookScreen = props => {
const {id} = props.route.params
const book = useSelector(state => state.book.books.find(book => book._id === id))
const [isLoading, setIsLoading] = useState(false)
if(isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" />
</View>
)
}
const dispatch = useDispatch()
return (
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={100}
style={{flex: 1}}>
<ScrollView>
<Formik
initialValues={{
id: id,
bookTitle: book.bookTitle,
ImgURL: book.ImgURL,
bookDescription: book.bookDescription,
bookAuthor: book.bookAuthor,
bookPrice: book.bookPrice.toString(),
bookTypes: book.bookTypes,
bookYear: book.bookYear.toString(),
bookRating: book.bookRating.toString(),
bookPages: book.bookPages.toString()
}}
validationSchema={formSchema}
onSubmit={(values) => {
console.log(values)
setIsLoading(true)
dispatch(bookAction.editBook(values))
.then(() => {
setIsLoading(false)
Alert.alert('book edited successfrully')
})
.catch(() => {
setIsLoading(false)
Alert.alert('an error occurred, please try again!')
})
}}>
{(props) =>
(
<View style={styles.form}>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Title</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookTitle')}
onBlur={props.handleBlur('bookTitle')}
value={props.values.bookTitle}
/>
<Text style={styles.error}>{props.touched.bookTitle && props.errors.bookTitle}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Imgae URL</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('ImgURL')}
onBlur={props.handleBlur('ImgURL')}
value={props.values.ImgURL}
/>
<Text style={styles.error}>{props.touched.ImgURL && props.errors.ImgURL}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Description</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookDescription')}
onBlur={props.handleBlur('bookDescription')}
value={props.values.bookDescription}
/>
<Text style={styles.error}>{props.touched.bookDescription && props.errors.bookDescription}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Author</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookAuthor')}
onBlur={props.handleBlur('bookAuthor')}
value={props.values.bookAuthor}
/>
<Text style={styles.error}>{props.touched.bookAuthor && props.errors.bookAuthor}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Price</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookPrice')}
onBlur={props.handleBlur('bookPrice')}
value={props.values.bookPrice}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookPrice && props.errors.bookPrice}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Types</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookTypes')}
onBlur={props.handleBlur('bookTypes')}
value={props.values.bookTypes}
/>
<Text style={styles.error}>{props.touched.bookTypes && props.errors.bookTypes}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Year</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookYear')}
onBlur={props.handleBlur('bookYear')}
value={props.values.bookYear}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookYear && props.errors.bookYear}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Rating</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookRating')}
onBlur={props.handleBlur('bookRating')}
value={props.values.bookRating}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookRating && props.errors.bookRating}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Pages</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookPages')}
onBlur={props.handleBlur('bookPages')}
value={props.values.bookPages}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookPages && props.errors.bookPages}</Text>
</View>
<View style={styles.buttonContainer}>
<Button title='save edit' onPress={props.handleSubmit} color='steelblue' />
</View>
</View>
)}
</Formik>
</ScrollView>
</KeyboardAvoidingView>
)
}
export default AddBookScreen
const styles = StyleSheet.create({
form: {
backgroundColor: "#ffffff",
padding: 20,
borderRadius: 10,
},
formGroup: {
width: "100%",
},
label: {
marginVertical: 10,
},
input: {
paddingHorizontal: 2,
paddingVertical: 8,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
},
buttonContainer: {
marginTop: 20,
},
error: {
color: 'red'
},
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
React Native Debugger
Edited
Manage to fix the error in debugger, by modifying the ReduxReducer.js
case EDIT_BOOKS:
return {
...state,
books: state.books.map(book => action.payload.find(item => item.id === book._id) || book)
}
The error is gone, but it didn't update the data

So I found the solution of my issue by Editing the ReduxReducer.js to
case EDIT_BOOKS:
return {
...state,
books: [...state.books]
}

Related

Expo: ‘Withnavigation’ & ‘gorhom/bottom-sheet’ is causing the app to get stuck at Splash Screen on the production mode & app build

Our app is running fine on the development mode but the app build is getting stuck at the splash screen.
To identify the root cause we ran the app on production mode with ‘expo start --no-dev --minify’ & identified that the ‘withnavigation’ & our ‘gorhom/bottom-sheet’ is causing the issue.
our environment details:
“expo”: “^45.0.0”,
“react”: “17.0.2”,
“react-dom”: “17.0.2”,
“react-icons”: “^4.3.1”,
“react-native”: “0.68.2”,
“react-native-reanimated”: “~2.8.0”,
“react-native-gesture-handler”: “~2.2.1”,
“#gorhom/bottom-sheet”: “^3.0.0”,
“react-navigation”: “^4.4.4”,
Following is our implementation of Bottom sheet modal:
const bottomSheetModalRef = useRef(null);
const handlePresentModalPress = useCallback(() => {
bottomSheetModalRef.current?.present();
}, []);
const handleDismissModalPress = useCallback(() => {
bottomSheetModalRef.current?.dismiss();
}, []);
const handleSheetChanges = useCallback((index) => {
console.log("handleSheetChanges", index);
}, []);
const snap = Platform.OS == "ios" ? "34%" : "36%";
const snapPoints = useMemo(() => [snap, snap], []);
const renderBottomSheet = () => {
return (
<BottomSheetModal
ref={bottomSheetModalRef}
index={1}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View style={Styles.sheetWrapper}>
<TouchableOpacity
onPress={() => {
// handleActionButtonNavigation("CollabPage");
handleActionButtonNavigation("AddParticipants");
}}
>
<View style={[Styles.sheetTab, { marginTop: 0 }]}>
<View
style={[
Styles.iconContainer,
{ backgroundColor: COLORS.primaryTeal500 },
]}
>
<SvgUri
svgXmlData={SVGS.CHEVRON_UP_DOWN}
width={RFValue(24, 844)}
height={RFValue(24, 844)}
/>
</View>
<Text style={[Styles.sheetText, { color: COLORS.monoBlack700 }]}>
Start a Project
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
handleActionButtonNavigation("OppurtunityPage");
}}
>
<View style={Styles.sheetTab}>
<View
style={[
Styles.iconContainer,
{ backgroundColor: COLORS.teritiaryWarning },
]}
>
<SvgUri
svgXmlData={SVGS.WHITE_BRIEFCASE}
width={RFValue(24, 844)}
height={RFValue(24, 844)}
/>
</View>
<Text style={Styles.sheetText}>Create Opportunity</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
handleActionButtonNavigation("FolioPage");
}}
>
<View style={Styles.sheetTab}>
<View
style={[
Styles.iconContainer,
{ backgroundColor: COLORS.teritiaryPurple },
]}
>
<SvgUri
svgXmlData={SVGS.WHITE_OPEN_FOLDER}
width={RFValue(24, 844)}
height={RFValue(24, 844)}
/>
</View>
<Text style={Styles.sheetText}>Create Folio</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
handleActionButtonNavigation("SavedDrafts");
}}
>
<View style={Styles.sheetTab}>
<View
style={[
Styles.iconContainer,
{ backgroundColor: COLORS.primaryTeal400 },
]}
>
<SvgUri
svgXmlData={SVGS.WHITE_DRAFT}
width={RFValue(24, 844)}
height={RFValue(24, 844)}
/>
</View>
<Text style={Styles.sheetText}>Saved Drafts</Text>
</View>
</TouchableOpacity>
</View>
</BottomSheetModal>
);
};
Following is our implementation of ‘Withnavigation’:
import { withNavigationFocus } from "react-navigation";
export default connect(
mapStateToProps,
mapDispatchToProps
)(withNavigationFocus(Discover));
import { withNavigation } from "react-navigation";
export default withNavigation(BottomNavBar);
Would really appreciate if someone could help us identify if this issue is being caused due to package support or a syntax error.
#gorhom/bottom-sheet is in v4. Try and update it.

React Native - TextInput does not get updated and is stuck with its initial state which is empty onSubmitEditing

The setSearchTerm does not get updated and is stuck with empty string all the time when i hit "done" on the keyboard. Any suggestions?
// React Native Bottom Navigation
// https://aboutreact.com/react-native-bottom-navigation/
import React, { useState } from 'react';
import { View, Text, SafeAreaView, Dimensions, FlatList, TouchableOpacity, TextInput } from 'react-native';
import { WebView } from 'react-native-webview';
import
MaterialCommunityIcons
from 'react-native-vector-icons/MaterialCommunityIcons';
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
const SearchScreen = ({navigation}) => {
const [searchTerm, setSearchTerm] = useState('');
const [data, dataSet] = useState({})
const onSearchText = async() => {
alert(searchTerm);
dataSet({});
let txtSearch = searchTerm;
if(txtSearch.length > 0){
//alert(searchTerm);
let response = await fetch('https://xxxxxx',{
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json;charset=UTF-8'
},
method: 'POST',
body: JSON.stringify({ searchTerm: searchTerm }),
mode: 'no-cors',
})
response = await response.json();
alert(JSON.stringify(response.results));
//alert(JSON.stringify(response.results));
dataSet(response.results);
}else{
}
}
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () =>
<View style={{flexDirection:'row'}}>
<TextInput
placeholder='Search Here'
onChangeText={term => setSearchTerm(term)}
onSubmitEditing={onSearchText} style={{backgroundColor:'#B2DFDB',marginRight:20,width:windowWidth - 125, padding:5, borderRadius:5,}}/>
</View>
,
});
}, [navigation, ]);
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate("Display", { url: item.url, youtube: item.youtube })}>
<View style={{flexDirection:'row', marginBottom:20,padding:10,backgroundColor:'#B2DFDB',borderRadius:10,}}>
<View pointerEvents="none" style={{}}>
<WebView style={{width:200, height:130, }} source={{ uri: item.url }} />
</View>
<View style={{ marginLeft:10, }}>
<Text style={{color:'#00796B', flexWrap: 'wrap',}}>{item.source}</Text>
<View style={{flexDirection:'row', flex:1,}}>
<Text style={{color:'#000000', fontWeight:'bold', flex:1, flexWrap: 'wrap'}}>{item.title}</Text>
</View>
<Text style={{color:'#00838F', }}>{item.category}</Text>
<Text style={{color:'#00838F', }}>{item.country}</Text>
</View>
</View>
</TouchableOpacity>
);
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 , padding: 16, backgroundColor:'#E0F2F1',}}>
{
data.length > 0 ? (
<FlatList
style={{flex:1,}}
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
/>) : <View style={{flex:1,justifyContent:'center',alignItems:'center', }}><Text style={{color:'#80CBC4', fontSize:20, textAlign:'center'}}>Nothing found, please try and search again.</Text></View>
}
</View>
</SafeAreaView>
);
}
export default SearchScreen;
You don't currently have a value assigned to the textInput field. Adding a value of searchTerm should resolve it.
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () =>
<View style={{flexDirection:'row'}}>
<TextInput
placeholder='Search Here'
onChangeText={term => setSearchTerm(term)}
value={searchTerm}
onSubmitEditing={onSearchText} style={{backgroundColor:'#B2DFDB',marginRight:20,width:windowWidth - 125, padding:5, borderRadius:5,}}/>
</View>
,
});
}, [navigation, ]);

React native WARN Possible Unhandled Promise Rejection (id: 2): Error: [AsyncStorage] Passing null/undefined as value is not supported

In react native cli i was trying to login but not working nd same thing working fine in expo nd this error is showing please let me know what can i do to do this???? i have added the action ,reducer,component page
getting error this //
WARN Possible Unhandled Promise Rejection (id: 2):
Error: [AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.
Passed value: undefined
Passed key: userToken
checkValidInput#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.easylab&modulesOnly=false&runModule=true:146791:24
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.easylab&modulesOnly=false&runModule=true:146835:24..........................
my action is like this // actions>index.js page
export const login = (formValues, actions) => {
return async dispatch => {
dispatch(startSubmitting());
const url = `/auth/login`;
var formdata = new FormData();
formdata.append('email', formValues.email);
formdata.append('password', formValues.password);
const response = await api
.post(url, formdata)
.then(res => {
return res;
})
.catch(error => {
actions.setErrors(error.response.data.error);
return error.response;
});
dispatch({
type: 'LOGIN',
payload: response,
});
dispatch(stopSubmitting());
await AsyncStorage.setItem('userToken', response.data.access_token);
};
};
//my component page login.js
import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect } from "react";
import {
Field,
Form,
Formik,
FormikProps,
ErrorMessage,
useFormik,
} from "formik";
import { connect } from "react-redux";
import { login } from "../../../actions";
import { Button } from "react-native-paper";
import ErrorMsg from "./ErrorMsg";
class Login extends React.Component {
submitLogin = (values, actions) => {
this.props.login(values, actions);
};
render() {
const { onChangeText, text, navigation } = this.props;
const { isSubmitting ,isLoading} = this.props.commonData;
const { login, loginLoading, isLoggedIn } = this.props.loginForm;
{
login && this.props.navigation.navigate("Nav1");
}
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.container}
>
{/* {verifyOtp ? <Loader loading={verifyOtp} /> : null} */}
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.inner}>
<View style={{ alignItems: "center" }}>
<Image
source={require("../../../assets/images/diabetes-awareness-month-1440x810.jpg")}
style={{
height: 110,
// top: -20,
resizeMode: "contain",
}}
/>
</View>
<View style={{}}>
<Text onPress={() => this.props.navigation.navigate("Register")}>
New User ? Register
</Text>
</View>
<View style={{}}>
<Text style={{ fontSize: 24, fontWeight: "bold" }}>LOGIN</Text>
</View>
<View style={{}}>
<Formik
initialValues={{
email: "",
password: "",
}}
validate={(values) => {
const error = {};
if (!values.password) {
error.password = (
<Text style={{ color: "red", fontSize: 10 }}>
Password Required
</Text>
);
}
if (!values.email) {
error.email = (
<Text style={{ color: "red", fontSize: 10 }}>
Email Type Required
</Text>
);
}
return error;
}}
onSubmit={(values, actions) => {
this.submitLogin(values, actions);
}}
enableReinitialize={true}
>
{(props: FormikProps<any>) => (
<>
<View style={{ paddingBottom: 10 }}>
<Text>Login To Check Your Account</Text>
</View>
<View>
{/* email */}
<View style={{ marginBottom: 20 }}>
<View style={styles.textInputContainer}>
<MaterialCommunityIcons
style={{ alignSelf: "center", paddingLeft: 10 }}
name="email"
size={28}
/>
<TextInput
style={styles.input}
onChangeText={props.handleChange("email")}
value={props.values.email}
autoFocus={true}
placeholder="Email"
/>
</View>
</View>
<View style={{ marginBottom: 20 }}>
<View style={styles.textInputContainer}>
<MaterialCommunityIcons
style={{ alignSelf: "center", paddingLeft: 10 }}
name="security"
size={28}
/>
<TextInput
style={styles.input}
onChangeText={props.handleChange("password")}
maxLength={10}
secureTextEntry={true}
value={props.values.password}
placeholder="Password"
/>
</View>
</View>
{/* {props.touched.password && props.errors.password && ( */}
<ErrorMsg msg={props.errors.password} />
{/* )} */}
<TouchableHighlight
underlayColor="white"
onPress={props.handleSubmit}
>
<Text color="white" style={styles.buttonStyle}>
{/* {isSubmitting ? (
<ActivityIndicator
size="small"
color="#4DB2F8"
/>
) : ( */}
<Text>LOGIN </Text>
{/* )} */}
</Text>
</TouchableHighlight>
</View>
</>
)}
</Formik>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
}
}
const mapStateToProps = (state) => {
return {
loginForm: state.loginData,
commonData: state.commonFunctions,
};
};
export default connect(mapStateToProps, { login })(Login);
and in redux trying to store like this// reducers>index.js page
const loginReducer = (
state = {
otp: false,
mobile: null,
login: false,
loginLoading: true,
verifyOtp: false,
isLoggedIn: false,
},
action
) => {
switch (action.type) {
case "LOGIN": {
if (action.payload.status === 200) {
let newState = { ...state, login: true, isLoggedIn: true };
return newState;
} else {
let newState = { ...state, login: false, isLoggedIn: false };
return newState;
}
}
default:
return state;
}
};
export default combineReducers({
loginData: loginReducer,
});
Your problem is that you are trying to set userToken to null on this line:
await AsyncStorage.setItem('userToken', response.data.access_token);
You need to do a null check before setting the token, since Async Storage does not support setting null values. Try:
if (response && response.data && response.data.access_token) {
await AsyncStorage.setItem('userToken', response.data.access_token);
}

Error: Element type is invalid when creating Input in Component

I have created a sub component for updating values of recipes I fetch from API. However when I do so I get:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `UpdateCourse`.
Navigator to the Update function component:
<Button
title="UPDATE RECIPE"
style={Styles.searchBtn}
onPress={() =>
navigation.navigate("Update_course", {
id: "60c9a32cb076563888b5782a",
})
}
/>
</View>
Update Component:
import React, { useState, useEffect } from "react";
import { Button, Image, TextInput, Text, Linking, View } from "react-native";
import Styles from "../../styles/Styles";
import Alert from "../elements/Alert";
import Axios from "axios";
import APIRequest from "../elements/APIRequest";
export default function UpdateCourse(id) {
const [dishName, setdishName] = useState("");
const [category, setcategory] = useState("");
const [author, setauthor] = useState("");
const [ingredients, setingredients] = useState([]);
const [cookingTime, setcookingTime] = useState("");
const [sourceUrl, setsourceUrl] = useState("");
const [imageUrl, setimageUrl] = useState("");
const [isPublished, setisPublished] = useState("true");
const [price, setprice] = useState("");
const [tags, settags] = useState([]);
const [alert, setAlert] = useState("");
const url = `http://x.x.x.x:1234/api/courses/find/${id}`;
useEffect(() => {
async function getData() {
const result = await Axios.get(url);
setdishName(result.data.dishName);
setcategory(result.data.category);
setauthor(result.data.author);
setingredients(result.data.ingredients);
setcookingTime(result.data.cookingTime);
setsourceUrl(result.data.sourceUrl);
setimageUrl(result.data.imageUrl);
setisPublished(result.data.isPublished);
setprice(result.data.price);
settags(result.data.tags);
}
getData();
}, [url]);
async function update() {
let item = {
dishName,
category,
author,
ingredients,
cookingTime,
sourceUrl,
imageUrl,
isPublished,
price,
tags,
};
const result = await APIRequest(
`http://x.x.x.x:1234/api/courses/${id}`,
"PUT",
JSON.stringify(item)
);
if (result.status !== 200) {
return setAlert(
result.status +
" " +
result.statusText +
" - Please check TextInput fields"
);
}
}
const handleIngredientsChange = (event, index) => {
const shallowCopy = [...ingredients];
shallowCopy[index] = {
...shallowCopy[index],
[event.target.id]: event,
};
setingredients(shallowCopy);
};
return (
<View style={{ minWidth: 70 }}>
<View style={(Styles.bigBar, { textAlign: "center" })}>
<Text> Update recipe</Text>
</View>
<View
style={{
backgroundColor: "#f1f1f1",
padding: 3,
borderRadius: 1,
marginBottom: 7,
}}
>
<View style={{}}>
{alert !== "" && (
<View style={{ display: "flex", justifyContent: "center" }}>
<Alert alert={alert}></Alert>
</View>
)}
<View>
<Text>Name of the course</Text>
<TextInput
style={Styles.formControl}
value={dishName}
onChangeText={e => setdishName(e)}
/>
</View>
<View>
<Text>Category</Text>
<TextInput
style={Styles.formControl}
value={category}
onChangeText={e => setcategory(e)}
/>
</View>
<View>
<Text>Author</Text>
<TextInput
onChangeText={e => setauthor(e)}
style={Styles.formControl}
value={author}
/>
</View>
<View style={{ marginBottom: 2 }}>
<Text>Ingredients</Text>
</View>
{ingredients.map(({ quantity, unit, description }, index) => {
return (
<View style="ing" key={"key" + index}>
<Text>Quantity </Text>
<TextInput
key={"quantity" + index}
style={
(Styles.formControl,
{
marginLeft: 1,
marginRight: 1,
width: 5,
})
}
onChangeText={event => handleIngredientsChange(event, index)}
value={quantity}
/>
<View style={{ marginRight: 2 }}> Unit </View>
<View>
<TextInput
key={"unit" + index}
style={(Styles.formControl, { width: 5 })}
onChangeText={event =>
handleIngredientsChange(event, index)
}
value={unit}
/>
</View>
<View style={{ marginLeft: 1 }}>
{" "}
<Text>Description</Text>{" "}
</View>
<TextInput
key={"description" + index}
style={(Styles.formControl, { marginLeft: 1 })}
onChangeText={event => handleIngredientsChange(event, index)}
value={description}
/>
</View>
);
})}
<View>
<Text>Cooking time</Text>
<TextInput
onChangeText={e => setcookingTime(e)}
style={Styles.formControl}
value={cookingTime}
/>
</View>
<View>
<Text>Source url</Text>
<TextInput
onChangeText={e => setsourceUrl(e)}
style={Styles.formControl}
value={sourceUrl}
/>
</View>
<View>
<Text>Image url</Text>
<TextInput
onChangeText={e => setimageUrl(e)}
style={Styles.formControl}
value={imageUrl}
/>
</View>
<View>
<Text>Publish state</Text>
<TextInput
onChangeText={e => setisPublished(e)}
style={Styles.formControl}
placeholder={"default: true"}
/>
</View>
<View>
<Text>Price</Text>
<TextInput
onChangeText={e => setprice(e)}
style={Styles.formControl}
value={price}
/>
</View>
<View>
<Text>Tags</Text>
<TextInput
onChangeText={e => settags(e)}
style={Styles.formControl}
value={tags}
/>
</View>
<View style={(Styles.prettyB, { textAlign: "center" })}>
<Button onPress={update} title="Submit"></Button>
</View>
</View>
</View>
</View>
);
}
Sample data:
{"isPublished":true,"tags":["pizza","greek"],"_id":"60c9e360b076563888b578a2","dishName":"Greek pizza","category":"pizza","author":"Paolo","ingredients":[{"_id":"60c9e360b076563888b578a3","quantity":0.75,"unit":"cup","description":"warm water"},{"_id":"60c9e360b076563888b578a4","quantity":0.5,"unit":"cup","description":"bread flour"},{"_id":"60c9e360b076563888b578a5","quantity":1.5,"unit":"tea spoon","description":"sea salt"},{"_id":"60c9e360b076563888b578a6","quantity":1,"unit":"cup","description":"baby arugula"},{"_id":"60c9e360b076563888b578a7","quantity":0.67,"unit":"cup","description":"grape tomatoes halved"},{"_id":"60c9e360b076563888b578a8","quantity":1,"unit":"drizzle","description":"olive oil"},{"_id":"60c9e360b076563888b578a9","quantity":1.5,"unit":"tea spoon","description":"dry active yeast"},{"_id":"60c9e360b076563888b578aa","quantity":2,"unit":"tea spoon","description":"sugar"},{"_id":"60c9e360b076563888b578ab","quantity":1,"unit":"lb","description":"hummus any variety"},{"_id":"60c9e360b076563888b578ac","quantity":0.67,"unit":"cup","description":"good pitted greek olives"},{"_id":"60c9e360b076563888b578ad","quantity":0.25,"unit":"cup","description":"crumbled feta cheese"}],"cookingTime":75,"sourceUrl":"https://www.crete.pl/kuchnia-grecka-dania-glowne-wegetarianskie/grecka-pizza-z-feta.html","imageUrl":"https://www.crete.pl/zdjecia/grecka-pizza-z-feta/pizza1.jpg","price":35.99,"date":"2021-06-16T11:41:20.508Z","__v":0}
However it does work fine when I use . It then displays text I created. I do not understand why I cannot put Input tags on this subpage however. Thank you for all help!
You have to use the TextInput , there is no component with the name InputText or Input
https://reactnative.dev/docs/textinput
<Input style={Styles.searchInput} onChangeText={onChange} value={query} />
Should be
<TextInput style={Styles.searchInput} onChangeText={onChange} value={query} />

Retrieve a list of products and display them

I am trying to retrieve a list of products from my API. For the moment, nothing is displayed, the page is currently empty except for my back button and I do not understand why. The two functions I use are functional since both console.log works. Could you help me ?
Everything looks fine, the console.log work in the terminal but I can't display anything in the app.
I tried this snack : https://snack.expo.io/O4oPj8-Qz
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: null
}
};
initListData = async () => {
let list = await getProducts(1);
console.log(list)
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<View>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
extraData={this.selectedId}
onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
back
</Text>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
Back
</Text>
</View>
</TouchableOpacity>
</View>
)}
</View>
);
};
}
You were not adding flex: 1 and also not calling the API in the right way, here is the snack with the solution.
Link to snack
Thanks to your answers and help. I got it work this way : Thank you so much for yout time and help, really, sincerely.
import React, { Component } from "react";
import { FlatList, SafeAreaView, Button, Text, View, TouchableOpacity } from 'react-native';
import { getProducts } from '../../../src/common/Preferences';
import styles from '../../../assets/styles';
import i18n from '../../../src/i18n';
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: []
}
};
initListData = async () => {
let list = await getProducts(1);
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
console.log(this.state.displayArray, 'dans initListData')
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
console.log(this.state.displayArray, 'dans componentWillMount')
}
render() {
console.log('displayArray', this.state.displayArray)
return (
<View style={{flex: 1}}>
<Text>{"\n"}</Text>
<Text>{"\n"}</Text>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
//extraData={this.selectedId}
//onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.container}>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
)}
</View>
);
};
}