focus next input field in functional component react native - react-native

I've created a login page where it has 2 input fields (username, password). To generate those input fields i am using "Input" component from nativebase. Now i want to focus password field from username input field when user press next from keyboard. But i am having a hard time finding this solution, can anyone help me out with that? Here is my code:
//packages
import React, { createRef, useContext, useEffect, useState } from 'react';
import { Image, Keyboard, ScrollView, StyleSheet, Text, View } from 'react-native';
import { AuthContext } from './context';
// third pirty packages
import { Button, Item, Input, Root, Spinner } from 'native-base';
import FontAwesome from "react-native-vector-icons/FontAwesome";
import Feather from "react-native-vector-icons/Feather";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
listenOrientationChange as lor,
removeOrientationListener as rol
} from 'react-native-responsive-screen';
//assets and components
import logoImageSmall from './../android/app/src/main/res/drawable/logo_small.png';
export const LoginScreen = ({ route, navigation }) => {
const [site_url, setSiteUrl] = useState(route.params.site_url);
const [database_name, setDatabaseName] = useState(route.params.database_name);
const [institute_name, setInstituteName] = useState(route.params.institute_name);
const [logo_location, setLogoLocation] = useState(route.params.logo_location);
const [student_id, setStudentId] = useState('');
const [password, setPassword] = useState('');
const [showLogo, setShowLogo] = useState(true);
const { state, signInAction } = useContext(AuthContext);
let passwordRef = React.createRef();
useEffect(() => {
lor();
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
_keyboardDidShow();
}
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
_keyboardDidHide();
}
);
return () => {
rol();
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
let _keyboardDidHide = () => {
setShowLogo(true);
}
let _keyboardDidShow = () => {
setShowLogo(false);
}
const styles = StyleSheet.create({
............
// my styles
............
});
return (
<Root>
<ScrollView contentContainerStyle={styles.loginForm} keyboardShouldPersistTaps="handled">
<Item rounded style={{ borderColor: "#FF3C48" }}>
<FontAwesome name='user' style={styles.iconStyle} />
<Input
style={{ marginLeft: hp("3%") }}
placeholder='Username'
onChangeText={student_id => setStudentId(student_id)}
returnKeyType={"next"}
blurOnSubmit={false}
onSubmitEditing={el => passwordRef.current = el}
onFocus={() => setShowLogo(false)} />
</Item>
<Item rounded style={{ borderColor: "#FF3C48", marginTop: hp("3%") }}>
<FontAwesome name='unlock' style={styles.iconStyle} />
<Input
ref={passwordRef}
style={{ marginLeft: hp("3%") }}
secureTextEntry={true}
placeholder='Password'
onChangeText={password => setPassword(password)}
returnKeyType={"done"}
onFocus={() => setShowLogo(false)} />
</Item>
<Button full danger
style={{ marginTop: hp("3%") }}
onPress={() => {
// check App.js for further process
signInAction({ student_id, password, site_url, database_name });
}}>
{state.sendingRequest ? (<Spinner color='white' />) : (<Text style={{ color: '#ffffff', fontWeight: 'bold', width: '100%', textAlign: 'center', fontSize: hp("2.8%") }}> LOGIN </Text>)}
</Button>
</ScrollView>
</Root>
)
}

Use this passwordRef.current.focus()

Related

Nested lists - How to modify (delete, add, update) items from a nested list using item's buttons and useState?

My first idea was to try to delete items from a nested list. I've started using SectionList and a component that has an useState that manages the data changes of the SectionList. I'm trying to solve it using SectionList but it'd be great to display all possible alternatives (FlatList, ViewList, etc).
I figured out how to delete a whole section list (and his items) but not one item by one. I've read plenty of posts, but I did find nothing related to managing SectionList items. Maybe there's an answer out there for FlatList nested items.
Here I left an example code ready to use (without styles) based on React Native official docs:
import React, { useEffect } from "react";
import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar, Button } from "react-native";
import { useState } from "react";
const dataResource = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
n: "delete",
k: 1
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"],
n: "delete",
k: 2
},
];
function App() {
const [state, setState] = useState(dataResource)
const Item = ({ dish, obj, i}) => (
<View >
<Text >{dish} </Text>
<Button title={obj.n} onPress={() => {}} /> // add the handler
</View>
);
const SectionComponent = ({ t, k }) => (
<View>
<Text >{t} </Text>
<Button title={'section'} onPress={() => { setState(state.filter(e => e.k != k)) }} />
</View>
);
return (
<SafeAreaView >
<SectionList
sections={state}
keyExtractor={(item, index) => item + index}
renderItem={({ item, section, index}) => <Item dish={item} obj={section} i={index}/>}
renderSectionHeader={({ section: { title, k } }) => <SectionComponent k={k} t={title} />}
/>
</SafeAreaView>
);
}
export default App;
I think how you display the data is trivial. The component you use will just change how you access the data, not how you update it. What you need is helper functions for editing the data. With those in place you can do things like add/remove section items, and editing section items themselves:
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
SafeAreaView,
SectionList,
Button,
TextInput,
} from 'react-native';
const dataResource = [
{
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
id: 1,
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
id: 2,
},
];
export default function App() {
const [state, setState] = useState(dataResource);
const [sectionTitle,setSectionTitle] = useState('Drinks')
const editItem = (itemId, newValue) => {
let newState = [...state];
let itemIndex = newState.findIndex((item) => item.id == itemId);
if (itemIndex < 0) return;
newState[itemIndex] = {
...newState[itemIndex],
...newValue,
};
setState(newState);
};
const addSectionItem = (title)=>{
let newState = [...state]
newState.push({
title,
data:[],
id:newState.length+1
})
setState(newState)
}
const removeFood = (itemId, food) => {
let currentItem = state.find((item) => item.id == itemId);
console.log(currentItem);
currentItem.data = currentItem.data.filter((item) => item != food);
console.log(currentItem.data);
editItem(itemId, currentItem);
};
const addFood = (itemId, food) => {
let currentItem = state.find((item) => item.id == itemId);
console.log(currentItem.data);
currentItem.data.push(food);
console.log(currentItem.data);
editItem(itemId, currentItem);
};
const Item = ({ item, section, index }) => {
return (
<View style={styles.row}>
<Text>{item} </Text>
<Button
title={'Delete'}
onPress={() => {
removeFood(section.id, item);
}}
/>
</View>
);
};
const SectionHeader = ({ title, id }) => {
return (
<View style={styles.header}>
<Text style={{ fontSize: 18 }}>{title} </Text>
<Button
title={'X'}
onPress={() => {
setState(state.filter((e) => e.id != id));
}}
/>
</View>
);
};
const SectionFooter = ({ id }) => {
const [text, setText] = useState('');
return (
<View style={[styles.row, styles.inputWrapper]}>
<Text>Add Entry</Text>
<TextInput
value={text}
onChangeText={setText}
style={{ borderBottomWidth: 1 }}
/>
<Button title="Add" onPress={() => addFood(id, text)} />
</View>
);
};
return (
<SafeAreaView>
<Button title="Reset list" onPress={() => setState(dataResource)} />
<View style={[styles.row, styles.inputWrapper]}>
<Text>Add New Section</Text>
<TextInput
value={sectionTitle}
onChangeText={setSectionTitle}
style={{ borderBottomWidth: 1 }}
/>
<Button title="Add" onPress={() => addSectionItem(sectionTitle)} />
</View>
<View style={styles.sectionListWrapper}>
<SectionList
sections={state}
keyExtractor={(item, index) => item + index}
renderItem={Item}
renderSectionHeader={({ section }) => <SectionHeader {...section} />}
renderSectionFooter={({ section }) => <SectionFooter {...section} />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
padding: 5,
alignItems: 'center',
},
header: {
flexDirection: 'row',
paddingVertical: 5,
width: '100%',
borderBottomWidth: 1,
},
inputWrapper: {
paddingVertical: 15,
marginBottom: 10,
},
sectionListWrapper: {
padding: 5,
},
});
Here's a demo
I've improved my answer to give a clearer example of how to manipulate SectionList. An example of how to add/delete/modify sections and its data is provided.
import React from 'react';
export default function useStateHelpers(state, setState) {
const editSection = (sectionId, newValue) => {
// parse and stringify is used to clone properly
let newState = JSON.parse(JSON.stringify(state));
let itemIndex = newState.findIndex((item) => item.id == sectionId);
if (itemIndex < 0) return;
const section = {
...newState[itemIndex],
...newValue,
};
newState[itemIndex] = section;
setState(newState);
};
const editSectionDataItem = (sectionId, itemId, newValue) => {
const newState = JSON.parse(JSON.stringify(state));
const sectionIndex = newState.findIndex(
(section) => section.id == sectionId
);
const section = newState[sectionIndex];
const itemIndex = section.data.findIndex((item) => item.id == itemId);
let item = section.data[itemIndex];
section.data[itemIndex] = {
...item,
...newValue,
};
editSection(sectionId, section);
};
const editSectionTitle = (sectionId, title) =>
editSection(sectionId, { title });
const setSelectSectionItem = (sectionId, itemId, isSelected) => {
editSectionDataItem(sectionId, itemId, { isSelected });
};
const removeSectionDataItem = (sectionId, food) => {
let newState = JSON.parse(JSON.stringify(state));
let sectionIndex = newState.findIndex((section) => section.id == sectionId);
let section = newState[sectionIndex];
section.data = section.data.filter((item) => item.title != food);
editSection(sectionId, section);
};
const addSectionDataItem = (sectionId, title, price) => {
let newState = JSON.parse(JSON.stringify(state));
let sectionIndex = newState.findIndex((section) => section.id == sectionId);
let section = newState[sectionIndex];
section.data.push({
title,
price,
id: `section-${sectionId}-${section.data.length + 1}`,
});
editSection(sectionId, section);
};
const addSection = (title, data = [], id) => {
let newState = JSON.parse(JSON.stringify(state));
newState.push({
title,
data,
id: newState.length + 1,
});
setState(newState);
};
const helpers = {
editSection,
editSectionTitle,
removeSectionDataItem,
addSectionDataItem,
addSection,
setSelectSectionItem,
editSectionDataItem,
};
return helpers;
}
import React, { useState } from 'react';
import {
StyleSheet,
SafeAreaView,
View,
Button,
SectionList,
} from 'react-native';
import data from './data';
import StateContext from './StateContext';
import useStateHelpers from './hooks/useStateHelpers';
import Header from './components/SectionHeader';
import Footer from './components/SectionFooter';
import Item from './components/SectionItem';
import TextInput from './components/Input';
export default function App() {
const [state, setState] = useState(data);
const [sectionTitle, setSectionTitle] = useState('');
const helpers = useStateHelpers(state, setState);
return (
<SafeAreaView style={{ flex: 1 }}>
<StateContext.Provider value={{ state, setState, ...helpers }}>
<View style={styles.container}>
<Button title="Reset list" onPress={()=>setState(data)} />
<TextInput
label={'Add New Section'}
value={sectionTitle}
onChangeText={setSectionTitle}
onRightIconPress={() => {
helpers.addSection(sectionTitle);
setSectionTitle('');
}}
/>
<View style={styles.sectionListWrapper}>
<SectionList
style={{ flex: 1 }}
sections={state}
renderItem={(props) => <Item {...props} />}
renderSectionHeader={(props) => <Header {...props} />}
renderSectionFooter={(props) => <Footer {...props} />}
/>
</View>
</View>
</StateContext.Provider>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 8,
},
sectionListWrapper: {
padding: 5,
flex: 1,
},
});
import React, { useContext } from 'react';
import { View, TouchableOpacity, StyleSheet, Text, Button } from 'react-native';
import StateContext from '../StateContext';
export default function Item({ item, section, index }) {
const {
removeSectionDataItem,
setSelectSectionItem
} = useContext(StateContext);
const hasPrice = item.price.toString().trim().length > 0;
return (
<TouchableOpacity
style={[styles.container, item.isSelected && styles.selectedContainer]}
onPress={() =>
setSelectSectionItem(section.id, item.id, !item.isSelected)
}
>
<View style={styles.row}>
<Text>
{item.title + (hasPrice ? ' | ' : '')}
{hasPrice && <Text style={{ fontWeight: '300' }}>{item.price}</Text>}
</Text>
<Button
title={'Delete'}
onPress={() => {
removeSectionDataItem(section.id, item.title);
}}
/>
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container: {
marginVertical: 2,
},
selectedContainer: {
backgroundColor: 'rgba(0,0,0,0.2)',
},
row: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
padding: 5,
alignItems: 'center',
},
});
/*SectionFooter.js*/
import React, { useState, useContext, useEffect } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import TextInput from './Input';
import StateContext from '../StateContext';
export default function SectionFooter({ section }) {
const [title, setTitle] = useState('');
const [price, setPrice] = useState('');
const [titleWasEdited, setTitleWasEdited] = useState(false);
const { addSectionDataItem, editSectionDataItem } = useContext(StateContext);
const selectedItem = section.data.find((item) => item.isSelected);
// listen for item selection
useEffect(() => {
// pass item values to text input
setTitle(selectedItem?.title || '');
setPrice(selectedItem?.price || '');
// reset title/price input
setTitleWasEdited(false);
}, [selectedItem]);
return (
<View style={styles.container}>
<Text>
{selectedItem
? 'Editing ' + selectedItem.title
: 'Add New Entry'
}
</Text>
{/*one input handles both title and price*/}
<TextInput
label={titleWasEdited ? 'Price' : 'Title'}
value={titleWasEdited ? price : title}
onChangeText={titleWasEdited ? setPrice : setTitle}
rightIconName={titleWasEdited ? 'plus' : 'arrow-right'}
// rightIcon is disabled when theres no text
// but price is allowed to be empty
allowEmptySubmissions={titleWasEdited}
style={{ width: '80%' }}
onRightIconPress={() => {
// after title is edited allow submission
if (titleWasEdited) {
// if item was edited update it
if (selectedItem) {
editSectionDataItem(section.id, selectedItem.id, {
title,
price,
isSelected: false,
});
}
// or add new item
else {
addSectionDataItem(section.id, title, price);
}
setTitle('');
setPrice('');
}
// toggle between title & price
setTitleWasEdited((prev) => !prev);
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginVertical: 20,
alignItems: 'center',
},
});
import React, { useContext } from 'react';
import { View, StyleSheet } from 'react-native';
import { TextInput } from 'react-native-paper';
import StateContext from '../StateContext';
export default function SectionHeader({ section: { id, title } }) {
const { editTitle, setState, state } = useContext(StateContext);
return (
<View style={{ borderBottomWidth: 1 }}>
<View style={styles.header}>
<TextInput
style={styles.titleInput}
value={title}
onChangeText={(text) => {
editTitle(id, text);
}}
right={
<TextInput.Icon
name="close"
onPress={() => {
setState(state.filter((sec) => sec.id != id));
}}
/>
}
underlineColor="transparent"
activeUnderlineColor="transparent"
outlineColor="transparent"
activeOutlineColor="transparent"
selectionColor="transparent"
dense
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
header: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-around',
alignItems: 'center',
},
titleInput: {
fontSize: 18,
backgroundColor: 'transparent',
borderWidth: 0,
borderColor: 'transparent',
height: 36,
},
});

Getting undefined device.map error while displaying table react-native-table-component

I am a developing a React Native app.My requirement is to display data from API in the table format.I am using react-native-table-component.I am not able to traverse through an array to display data in the table.Below is my code:
TargetSetUpPage.js:
import React, { useEffect } from "react";
import { StyleSheet, Text, Button, TextInput, ScrollView ,View}
from "react-native";
import { useDispatch,useSelector } from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-
community/asyncstorage';
import { Table, Row, Rows } from 'react-native-table-
component';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = [useSelector(state =>
state.auth.availableDevice)];
//console.log('The devices are: '+JSON.stringify(devices));
if (typeof(devices) !== 'undefined' && devices != null) {
console.log('Not Undefined and Not Null')
} else {
console.log('Undefined or Null')
const devices = [useSelector(state =>
state.auth.availableDevice)];
}
const tableHead = ['Target Id','Target Name']
const tableRow = devices;
console.log(devices);
const tableRows = (devices || []).map(item => ({ name:
item.name
}));
useEffect(() => {
const onScreenLoad = async() => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const { userid } = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
},[dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
{/* <Table borderStyle={{borderWidth: 2, borderColor:
'#c8e1ff'}}>
<Row data={tableHead} style={styles.head} textStyle=
{styles.text}/>
<Rows data={tableRows} textStyle={styles.text}/>
</Table> */}
<FlatList
data={devices}
keyExtractor={item => item.TargetId}
renderItem={itemData => (
<View style={styles.card}>
{/* <Text style={styles.text}>{itemData.item.TargetName}
</Text> */}
<Button title={itemData.item.TargetName} onPress={()=>{}}/>
</View>
)}
numColumns={2}
/>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button
title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},});
export default TargetSetUpPage;
After executing it is showing the below tableRow array in console:
Not Undefined and Not Null
Array [
undefined,
]
TypeError: undefined is not an object (evaluating 'item.name')
But If I remove Table and display FlatList,I am able to display the list without any problem .I dont know what mistake I am doing in displaying the Table.Thanks in Advance.
// devices will be the same as auth.availableDevice in your redux
// If auth.availableDevice is undefined at any point, like during boot
// then devices will be undefined too
const devices = useSelector(state => state.auth.availableDevice);
// Let's write our map code so that it handles the case where devices is undefined
const tableRows = (devices || []).map(item => ({ name: item.name }));
I used DataTable from react-native-paper to get the table.Below is the code that renders data from API.
TargetSetUpPage.js:
import React, {useEffect} from "react";
import {StyleSheet, Text, Button, TextInput, ScrollView, View, FlatList}
from "react-native";
import {useDispatch, useSelector} from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-community/async-storage';
import {DataTable} from 'react-native-paper';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = useSelector(state => state.auth.availableDevice);
useEffect(() => {
const onScreenLoad = async () => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const {userid} = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
}, [dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
<DataTable>
<DataTable.Header>
<DataTable.Title>
<Text>Target Id</Text>
</DataTable.Title>
<DataTable.Title>
<Text>Target Name</Text>
</DataTable.Title>
</DataTable.Header>
{devices.map((item, key) => (
<DataTable.Row>
<DataTable.Cell>{item.TargetId}</DataTable.Cell>
<DataTable.Cell>{item.TargetName}</DataTable.Cell>
</DataTable.Row>
)
)}
</DataTable>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},
});
export default TargetSetUpPage;
which displays the table on the screen as shown below.

How do i pass the value in counter component to the add to the To Cart Button

Hi I have a add minus component button that I want to pass the value to selected value to the "To Cart" button. If the value is zero the orders will not be added but if the value is > 0 then it will be added to cart.
below is my Add Minus button component
AddMinusButton.js
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
import Colors from "../../constants/Colors";
const AddMinusButton = () => {
const [value, setValue] = useState(0);
const minusValue = () => {
if (value > 0) {
setValue(value - 1);
} else {
setValue(0);
}
};
const plusValue = () => {
setValue(value + 1);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.buttonLeft} onPress={minusValue}>
<Text>-</Text>
</TouchableOpacity>
<Text style={styles.quantity}> {value}</Text>
<TouchableOpacity style={styles.buttonRight} onPress={plusValue}>
<Text>+</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderRadius: 10,
height: 35,
overflow: "hidden",
borderColor: "black",
},
quantity: {
paddingHorizontal: 10,
marginRight: 3,
},
buttonLeft: {
alignItems: "center",
backgroundColor: Colors.primary,
borderRightWidth: 1,
borderRightColor: "black",
padding: 10,
},
buttonRight: {
alignItems: "center",
backgroundColor: Colors.primary,
borderLeftWidth: 1,
borderLeftColor: "black",
padding: 10,
},
});
export default AddMinusButton;
This is my Product Screen. I want to pass the value that is selected by the AddMinusButton component to the "To Cart". I am only able to made the button click and it will add one by one.
ProductOverviewScreen.js
import React, { useState, useEffect, useCallback } from "react";
import {
View,
Text,
FlatList,
Button,
Platform,
ActivityIndicator,
StyleSheet,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../../components/UI/HeaderButton";
import ProductItem from "../../components/shop/ProductItem";
import * as cartActions from "../../store/actions/cart";
import * as productsActions from "../../store/actions/products";
import Colors from "../../constants/Colors";
import * as AddMinusButton from "../../components/UI/AddMinusButton";
const ProductsOverviewScreen = (props) => {
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const [error, setError] = useState();
const products = useSelector((state) => state.products.availableProducts);
const dispatch = useDispatch();
const loadProducts = useCallback(async () => {
setError(null);
setIsRefreshing(true);
try {
await dispatch(productsActions.fetchProducts());
} catch (err) {
setError(err.message);
}
setIsRefreshing(false);
}, [dispatch, setIsLoading, setError]);
useEffect(() => {
const willFocusSub = props.navigation.addListener(
"willFocus",
loadProducts
);
return () => {
willFocusSub.remove();
};
}, [loadProducts]);
useEffect(() => {
setIsLoading(true);
loadProducts().then(() => {
setIsLoading(false);
});
}, [dispatch, loadProducts]);
const selectItemHandler = (id, title) => {
props.navigation.navigate("ProductDetail", {
productId: id,
productTitle: title,
});
};
if (error) {
return (
<View style={styles.centered}>
<Text>An error occurred!</Text>
<Button
title="Try again"
onPress={loadProducts}
color={Colors.primary}
/>
</View>
);
}
if (isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
);
}
if (!isLoading && products.length === 0) {
return (
<View style={styles.centered}>
<Text>No products found. Maybe start adding some!</Text>
</View>
);
}
return (
<FlatList
onRefresh={loadProducts}
refreshing={isRefreshing}
data={products}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<ProductItem
image={itemData.item.imageUrl}
title={itemData.item.title}
price={itemData.item.price}
onSelect={() => {
selectItemHandler(itemData.item.id, itemData.item.title);
}}
>
<AddMinusButton />
<Button
color={Colors.primary}
title="To Cart"
onPress={() => {
dispatch(cartActions.addToCart(itemData.item));
}}
/>
</ProductItem>
)}
/>
);
};
ProductsOverviewScreen.navigationOptions = (navData) => {
return {
headerTitle: "All Products",
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="Cart"
iconName={Platform.OS === "android" ? "md-cart" : "ios-cart"}
onPress={() => {
navData.navigation.navigate("Cart");
}}
/>
</HeaderButtons>
),
};
};
const styles = StyleSheet.create({
centered: { flex: 1, justifyContent: "center", alignItems: "center" },
});
export default ProductsOverviewScreen;

Searchable flat list react native

I am trying to search items in a flatlist, whenever i type in the search bar it only take one letter and closes the keyboard and the value inside the search bar disappear,
example when i type "george" it only takes the letter g and re-render a list of names containing the letter g and delete what's inside the search bar
how can i fix this ?
import React, { useState, useEffect } from 'react';
import { ScrollView } from 'react-native';
import { View, Text, FlatList, ActivityIndicator, SafeAreaView, TouchableOpacity } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Divider, SearchBar } from 'react-native-elements'
const Item = ({ name, lastName }) => (
<View>
<Text style={{ fontSize: 17, color: '#666', marginBottom: 10 }}>Full name: {name} {lastName}</Text>
</View>);
function Clients({ navigation }) {
const [isLoading, setLoading] = useState(true);
const usersApi = 'https://reqres.in/api/users'
const [users, setUsers] = useState([]);
const [search, setSearch] = useState("");
const [masterData, setMasterData] = useState("");
const fetchJson = async (userApi) => {
const response = await fetch(userApi);
return response.json()
}
useEffect(() => {
fetchJson(usersApi)
.then((users) => {
setUsers(users.data);
setMasterData(users.data);
})
.catch((error) => (alert(error)))
.finally(() => setLoading(false));
}, []);
const itemSeparator = () => {
return (
<Divider style={{ width: "105%", marginVertical: 3, alignSelf: 'center' }} />
)
}
const renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => searchFilterFunction(text)}
autoCorrect={false}
/>
);
};
const searchFilterFunction = text => {
setSearch(text);
const newData = masterData.filter(item => {
const itemData = `${item.first_name.toUpperCase()} ${item.last_name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setUsers(newData);
};
const renderItem = ({ item }) => (
<TouchableOpacity style={{
flex: 1,
padding: 10
}}
onPress={() => { navigation.navigate('Client', { item: item }); }}
underlayColor='#ccc'
activeOpacity={0.1} >
<Item name={item.first_name} lastName={item.last_name} />
</TouchableOpacity>
);
console.log(search)
console.log(users)
return (
<SafeAreaProvider>
<SafeAreaView>
<Text style={{ color: '#666', margin: 15, fontSize: 20 }}>Clients actuels</Text>
<View style={{
width: '96%',
backgroundColor: 'white',
borderRadius: 5,
alignSelf: 'center',
marginTop: 20,
}}>
<View style={{ margin: 15 }}>
{isLoading ? <ActivityIndicator size="large" /> :
<FlatList
data={users}
renderItem={renderItem}
ItemSeparatorComponent={itemSeparator}
ListHeaderComponent={renderHeader}
keyExtractor={item => item.id.toString()}
value={search}
/>}
</View>
</View>
</SafeAreaView>
</SafeAreaProvider>
);
}
export default Clients;

Add a loader for authentication flow on React Native Expo

I just added the loader that is loaded when the user sends the request to login by clicking the button with this code:
import React, { useState } from 'react';
import { Text, TextInput, SafeAreaView, StyleSheet, TouchableOpacity, Image, KeyboardAvoidingView, ActivityIndicator } from 'react-native';
import {AuthContext} from './utils';
// Creating Login Activity.
export function SignInScreen() {
const [isLoading, setLoading] = useState(false);
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
return (
<KeyboardAvoidingView behavior={Platform.OS == 'ios' ? 'padding' : 'height'} style={styles.keyboard_login}>
<SafeAreaView style={styles.contenitore_login}>
{isLoading == true ? <ActivityIndicator size="large"/> : (
<>
<Image style={styles.logo} source={require('../assets/logo.png')} />
<TextInput
style={styles.campo_login}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize='none'
textContentType='emailAddress'
keyboardType='email-address'
/>
<TextInput
style={styles.campo_login}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity style={styles.cta_login} onPress={() => { setLoading(true); signIn({ email, password });}}>
<Text style={styles.testo_cta_login}>Accedi</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
</KeyboardAvoidingView>
);
}
Everything works perfectly when the user enters the correct email and password, but when they enter the wrong credentials the loader keeps going.
}).then((response) => response.json()).then((responseJson) => {
// If server response message same as Data Matched
if(responseJson === 'Data Matched'){
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
}else{
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
As you can see if the fetch URL says "Data Matched" then it is assigned SIGN_IN which then returns the user to the home of my app, but in the case where the logins don't match I don't know what to do to send the user back to the login page.
What is the navigation code that is used in this case? Thanks!
We can have variable let's say loading
const [loading, setLoading] = useState(false)
And as far I can see your Sigin function connects to db. So do something like
const authContext = React.useMemo(
() => ({
signIn: async data => {
setLoading(true);
// Some code here
setLoading(false);
}
})
)
And then show an Activity indicator or any component you want to show fetching something like this
import React from 'react';
import PropTypes from 'prop-types';
import {View, ActivityIndicator, Modal} from 'react-native';
import styles from './Styles/FullScreenLoaderStyles';
const FullScreenLoader = ({loading}) => (
<Modal transparent={true} animationType={'none'} visible={loading}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator color="#000" animating={loading} />
</View>
</View>
</Modal>
);
FullScreenLoader.propTypes = {
loading: PropTypes.bool,
};
export default FullScreenLoader;
I have updated the loading screen code. Now you can import this in your google signin screen like
import FullScreenLoader from './FullScreenLoader';
// some code
return(
<View>
<FullScreenLoader loading={isLoading} />
</View>
)
FullScreenLoaderStyles.js
import {StyleSheet} from 'react-native';
import colors from '../../Themes/Colors';
export default StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#00000040',
},
activityIndicatorWrapper: {
backgroundColor: colors.grey,
height: 50,
width: 50,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
});