React native elements Checkbox selecting multiple items using react hooks - react-native

I need a little help trying to figure this out. I am using react native elements flatlist with a checkbox inside of it. I am also using react hooks with this. Everything is working perfectly but when I try and select one of the items in the checkbox it selects all of the items. Now I have had this issue before when I was just using components and not hooks and functions. I tried to use the same method that I used here Selecting multiple items
Like this...
function ViewCategoryWS2({navigation}) {
const {category, setCategory} = useContext(ItemContext);
const [eats, setEats] = useState([]);
const [checked, toggleChecked] = useState(false);
function back() {
navigation.navigate('MenuWS2');
}
function test(index) {
const foods = category;
foods[index].checked = !foods[index].checked;
setCategory(foods);
}
return (
<View style={styles.container}>
<Icon
name='arrow-left'
color="#000000"
size={25}
style={styles.menuIcon}
onPress={back}
/>
<FlatList
data={category}
//extraData={eats}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<CheckBox
center
titleProps={{ color: 'black', fontWeight: 'bold'}}
title={item}
iconRight
checked={checked}
onPress={() => test(index)}
size={30}
containerStyle={styles.checkBox}
/>
)}
/>
</View>
)
}
and I keep getting this error TypeError: Attempted to assign to readonly property. I have also tried it this way...
<FlatList
data={category}
//extraData={eats}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<CheckBox
center
titleProps={{ color: 'black', fontWeight: 'bold'}}
title={item}
iconRight
checked={checked}
onPress={() => toggleChecked(!checked)}
size={30}
containerStyle={styles.checkBox}
/>
)}
/>
Here is my code from where the context is coming from. This page I pull data from my datbase and it displays categories. I click on any category and it pulls data from my database that's assigned to that certain category that was clicked on..
Here is the code...
import ItemContext from '../context/CategoryItems';
export default function menuWS({navigation}) {
const [restaurantlocationcode, setRestaurantlocationcode] = useState('')
const [menu, setMenu] = useState([]);
const {category, setCategory} = useContext(ItemContext);
const [selected, setSelected] = useState(0);
function viewMenu() {
fetch('URL', {
method: 'POST',
body: JSON.stringify({ restaurantlocationcode: restaurantlocationcode}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(response=> setMenu(response));
console.log(menu);
alert(menu);
}
function viewCategory({item}) {
fetch('URL', {
method: 'POST',
body: JSON.stringify({
category: item,
restaurantlocationcode: restaurantlocationcode,
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(response => {
setCategory(response);
console.log(response);
alert(response);
});
navigation.navigate('ViewCategoryWS2', {category});
}
function showMenu() {
console.log(menu);
alert(menu);
}
const buttons = ['Menu']
return (
<View style={styles.container}>
<Input
style={styles.Input}
placeholder='Restaurant Location Code'
leftIcon={
<Icon
name='location-arrow'
size={24}
color='black'
/>
}
onChangeText={setRestaurantlocationcode}
value={restaurantlocationcode}
underlineColorAndroid='transparent'
/>
<ButtonGroup
onPress={viewMenu}
selectedIndex={selected}
selectedButtonStyle={{backgroundColor: 'blue'}}
buttons={buttons}
containerStyle={styles.buttonGroup}
/>
<FlatList
data={menu}
extraData={category}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => (
<ListItem
titleStyle={{ color: 'black', fontWeight: 'bold'}}
title={item}
onPress={() => viewCategory({item})}
bottomDivider
chevron
/>
)}
/>
</View>
)
}
Here is my useContext code...
import { createContext } from 'react';
const ItemContext = createContext({});
export default ItemContext;
Here is the second part to my useContext code..
import React, {useState} from 'react';
import ItemContext from './CategoryItems';
const MenuItemState = ({children}) => {
const [category, setCategory] = useState([]);
return (
<ItemContext.Provider value={{category, setCategory}}>
{children}
</ItemContext.Provider>
)
}
export default MenuItemState;
And it just selects all of the items. I'm not sure what I am missing. Help with this would be greatly appreciated.

App Output:
import React, { useState } from 'react';
import { Text, View, StyleSheet, CheckBox, Icon, FlatList } from 'react-native';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
/*
I am using this data, but you can continue
with the one you getting from context
*/
const data = [
{ id: 1, title: 'one', checked: true },
{ id: 2, title: 'two', checked: false },
{ id: 3, title: 'three', checked: false },
{ id: 4, title: 'four', checked: false },
{ id: 5, title: 'five', checked: false },
{ id: 6, title: 'six', checked: false },
];
export default function App() {
const [category, setCategory] = useState(data);
const [eats, setEats] = useState([]);
const [checked, toggleChecked] = useState(false);
function back() {
// navigation.navigate('MenuWS2');
}
function test(index) {
/*
Use this approach while changing the state.
This will create the copy of original array,
and you can perform mutation there
and update state with it.
*/
console.log(index);
const foods = [...category];
foods[index].checked = !foods[index].checked;
setCategory(foods);
}
/*
In your Checkbox component,
use "onChange" prop instead of "onPress"
and "value" instead of "checked"
*/
return (
<View style={styles.container}>
<FlatList
data={category}
//extraData={eats}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<Card style={{ padding: 10, margin: 5 }}>
<Text>{item.title}</Text>
<CheckBox
center
titleProps={{ color: 'black', fontWeight: 'bold' }}
title={item}
iconRight
value={item?.checked}
onChange={() => test(index)}
size={30}
containerStyle={styles.checkBox}
/>
</Card>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginTop: 24,
backgroundColor: 'grey',
flex: 1,
},
});
Working Example: Expo Snack

Related

React-Navigation: How to change the params passed to the child screen once the parent screen rerenders?

i have two screens one for documents (parent) and one for each document(child),when a document on my flatlist of docs is pressed it navigates to the document screen, on that screen i can perform CRUD operations one of them is giving a label to the document, this part is necessary because in order to send the document it must be labeled, however when i label the document the database receives the PUT requests and updates the element but the child screen is not getting the new updated object and i can only send if i navigate back to documents then to document.
How can i solve this?
this is my document label function:
const LabelItem=()=>{
const modefiedFile=new FormData();
modefiedFile.append('file', {
uri: fileUri,
name: fileTitle,
type: 'file/pdf'})
modefiedFile.append('label',Label)
const headers={
Accept:'application/json',
'Content-Type': 'multipart/form-data'
}
console.log(modefiedFile)
axios
.put(`http://192.168.1.17:8000/File/${key}/`,modefiedFile,{headers:headers})
.then((response)=> {response})
.then((error)=>{console.log(error)})
setSecondModal(false)
}
this is the button to send:
<View style={footerStyle.footer}>
<TouchableOpacity onPress={()=> {
label!='none'?
props.navigation.navigate('Envoi'): openAlert()
console.log(label)
}}>
<Icon style={footerStyle.ellipsis} name="share" />
</TouchableOpacity>
and this is from the parent navigation :
<TouchableOpacity onPress={()=>props.navigation.navigate('Document',{url:{uri: `${item.file}`, file:`${item.title}`,key:`${item.id}`,label:`${item.label}`}})}>
<View style={DocumentStyle.flatitem}>
<Icon style={DocumentStyle.pdf} name="file-pdf-o" color="#666"/>
<Text style={DocumentStyle.itemtext}> {item.title}</Text>
</View>
</TouchableOpacity>
for this, you have to change an item on the child screen and pass that item to the parent screen. Please See This.
This is the List Screen.
import React from 'react';
import { useState } from 'react';
import { SafeAreaView, FlatList, StyleSheet,DeviceEventEmitter, Text, StatusBar, TouchableOpacity } from 'react-native';
const App = ({navigation}) => {
DeviceEventEmitter.addListener("imagechange", (eventData) => {
onSelect(eventData)
})
const [data , setData] = useState([
{id: '1',title: 'First Item'},
{id: '2',title: 'Second Item'},
{id: '3',title: 'Third Item'},
{id: '4',title: 'Fourth Item'},
{id: '5',title: 'Fifth Item'},
{id: '6',title: 'Sixth Item'},
{id: '7',title: 'seventh Item'},
{id: '8',title: 'eight Item'},
{id: '9',title: 'nineth Item'},
])
const onSelect = item => {
// let id = data.findIndex((obj => obj.id == item.id));
// const newstate = {...data,data[id].title = item.title}
const newState = data.map(obj =>
obj.id === item.id ? { ...obj, title: item.title } : obj
);
setData(newState)
};
const onPress = (item) => {
navigation.navigate("Details", {item});
};
const renderItem = ({ item }) => (
<TouchableOpacity style={styles.item}
onPress={() => onPress(item)}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
Here are the details Screen Where I am changing the object.
import * as React from 'react';
import { useState, useEffect } from 'react';
import { View, Text, StyleSheet,DeviceEventEmitter, TouchableOpacity } from 'react-native';
function DetailsScreen ({navigation, route}) {
const [item, setItem] = useState(route.params.item)
let data = route.params.item;
const updatevalue = () => {
setItem({...item,title:"updated"})
navigation.goBack();
DeviceEventEmitter.emit("imagechange",{...data,title:"updated this value"});
// route.params.onSelect({...data,title:"updated this value"});
}
// useEffect(() => {
// return () => {
// DeviceEventEmitter.removeAllListeners()
// };
// }, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{item.title}</Text>
<TouchableOpacity style={styles.button} onPress={() => updatevalue()}>
<Text style={{color:"#fff"}}>Update Value</Text>
</TouchableOpacity>
</View>
);
}
export default DetailsScreen
const styles = StyleSheet.create({
button:{
marginTop:20,
backgroundColor:"#333333",
borderRadius:10,
paddingHorizontal:20,
paddingVertical:5,
}
})
Here is the link to git repos

UI not updating after adding or removing items with apollo cache and reactive variables

I'm having a hard time updating my UI after adding or removing items on my react native app with apollo cache.
To explain a little bit more. I have an
explorer Screen where are displayed some items with a toggle to subscribe or unsubscribe to an item. To another screen called "Subscribe Screen", I'm supposed to displayed all my favorite items. So I created a reactive variable called allFavoritesVar where I can add or delete items from there.
So in my cache.js I have :
import {InMemoryCache} from '#apollo/client/core';
import {makeVar} from '#apollo/client';
export const allFavoritesVar = makeVar([]);
export const cache = new InMemoryCache({
Query: {
fields: {
userFavorites: {
read() {
return allFavoritesVar();
},
},
}
}
})
So on my Explorer screen i'm checking if each item exist in allFavoritesVar to make the toggle red and inform the user that these items are already in their "Subscribe Screen".
const favExists = (flux) => {
if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
return true;
}
return false;
};
I was using redux before and switch to apollo because I needed to persist the cache when users were opening their app. Everything was much simpler with redux, the toggle were working fine and becoming red or grey when adding or removing items from the store and the "subscribe screen" was updating itself.
Now when I'm toggling, the mutation works, I can see that items are added or removed, but my ui is not updating. And when I'm closing my app, the last state of the cache is not displayed.
Here is my Explorer Screen :
import React, {useEffect, useState} from 'react';
import {
SafeAreaView,
StyleSheet,
Dimensions,
ScrollView,
TouchableOpacity,
Image,
FlatList,
ActivityIndicator,
} from 'react-native';
import {
NetworkStatus,
useLazyQuery,
useMutation,
useQuery,
} from '#apollo/client';
import {useSelector, useDispatch} from 'react-redux';
import {Box, Text} from 'react-native-design-utility';
import {Notifier} from 'react-native-notifier';
import {useTheme} from '#react-navigation/native';
import ErrorIcon from 'react-native-vector-icons/Ionicons';
import RefreshIcon from 'react-native-vector-icons/Ionicons';
import {theme} from '../theme/theme';
import Loading from '../components/Loading';
import CustomNotifier from '../components/CustomNotifier';
import CustomNotifierError from '../components/CustomNotifierError';
import SubscribeItem from '../components/SubscribeItem';
import {
SUBSCRIBE_FLUXGROUP_MUTATION,
SUBSCRIBE_FLUX_MUTATION,
UNSUBSCRIBE_FLUXGROUP_MUTATION,
UNSUBSCRIBE_FLUX_MUTATION,
} from '../graphql/mutations/fluxMutations';
import {
GET_EXPLORER_CATEGORIES_QUERY,
GET_EXPLORER_SLIDES_QUERY,
} from '../graphql/queries/explorerQueries';
import ToggleIcon from '../components/ToggleIcon';
import {HEIGHT} from '../utils/constants';
import {ALL_FAVORITES_QUERY} from '../graphql/queries/userQueries';
import {allFavoritesVar, cache} from '../utils/cache';
import {FLUX_QUERY} from '../graphql/queries/fluxesQueries';
const WIDTH = Dimensions.get('window').width;
const PAGE_SIZE = 10;
const ExplorerScreen = ({navigation}) => {
const {colors, dark} = useTheme();
const [limit, setLimit] = useState(PAGE_SIZE);
const [isError, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const {
data: explorerData,
loading: explorerLoading,
error,
refetch,
} = useQuery(GET_EXPLORER_CATEGORIES_QUERY, {
fetchPolicy: 'cache-first',
errorPolicy: 'all',
});
const {data: favoritesData, loading: favLoading} =
useQuery(ALL_FAVORITES_QUERY);
const {data: slidesData, loading: slidesLoading} = useQuery(
GET_EXPLORER_SLIDES_QUERY,
{
fetchPolicy: 'cache-first',
},
);
const [subscribeToFlux] = useMutation(SUBSCRIBE_FLUX_MUTATION);
const [subscribeToFluxGroup] = useMutation(SUBSCRIBE_FLUXGROUP_MUTATION);
const [unsubscribeFromFlux] = useMutation(UNSUBSCRIBE_FLUX_MUTATION);
const [unsubscribeFromFluxGroup] = useMutation(
UNSUBSCRIBE_FLUXGROUP_MUTATION,
);
const addFav = (flux) => {
const explorerFav = allFavoritesVar([...allFavoritesVar(), flux]);
console.log('explorerFav: ', explorerFav);
return explorerFav;
};
const favExists = (flux) => {
if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
return true;
}
return false;
};
const handleAddFavorite = async (flux) => {
if (flux.__typename === 'FluxGroup') {
addFav(flux);
Notifier.showNotification({
title: 'Vous êtes abonné à ce groupe de flux',
Component: CustomNotifier,
componentProps: {
alertType: 'info',
},
});
await subscribeToFluxGroup({
variables: {
id: parseInt(flux.id),
frequency: 'all',
},
});
} else {
addFav(flux);
Notifier.showNotification({
title: 'Vous êtes abonné à ce flux',
Component: CustomNotifier,
componentProps: {
alertType: 'info',
},
});
await subscribeToFlux({
variables: {
id: parseInt(flux.id),
frequency: 'all',
}
});
}
};
const handleRemoveFavorite = async (flux) => {
if (flux.__typename === 'FluxGroup') {
Notifier.showNotification({
title: 'Vous êtes désabonné de ce groupe de flux',
Component: CustomNotifierError,
componentProps: {
alertType: 'error',
},
});
await unsubscribeFromFluxGroup({
variables: {
id: parseInt(flux.id),
},
update: (cache, {data}) => {
const existingFavs = cache.readQuery({
query: ALL_FAVORITES_QUERY,
});
//console.log('DATA UPDATE:', data);
const newFavs = existingFavs.userFavorites.filter(
(item) => item.id !== flux.id,
);
console.log('DATA UPDATE:', newFavs);
cache.writeQuery({
query: ALL_FAVORITES_QUERY,
data: {userFavorites: [newFavs, ...existingFavs.userFavorites]},
});
},
});
} else {
Notifier.showNotification({
title: 'Vous êtes désabonné de ce flux',
Component: CustomNotifierError,
componentProps: {
alertType: 'error',
},
});
await unsubscribeFromFlux({
variables: {
id: parseInt(flux.id),
},
update: (cache, {data}) => {
const existingFavs = cache.readQuery({
query: ALL_FAVORITES_QUERY,
});
//console.log('DATA UPDATE:', data);
const newFavs = existingFavs.userFavorites.filter(
(item) => item.id !== flux.id,
);
console.log('DATA UPDATE:', newFavs);
cache.writeQuery({
query: ALL_FAVORITES_QUERY,
data: {userFavorites: [newFavs, ...existingFavs.userFavorites]},
});
},
});
}
};
function sliceIntoChunks(arr, chunkSize) {
const res = [];
for (let i = 0; i < arr.length; i += chunkSize) {
const chunk = arr.slice(i, i + chunkSize);
res.push(chunk);
}
return res;
}
useEffect(() => {
if (error) {
setIsLoading(true);
setError(error.message);
setIsLoading(false);
}
}, [error]);
const SeeMore = ({onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<Text
size={15}
mr="sm"
color={dark ? 'primary' : colors.text}
style={styles.letSpacing}>
Tout Voir
</Text>
</TouchableOpacity>
);
};
const renderHeader = () => {
if (slidesLoading) {
return (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingHorizontal: theme.space.sm,
paddingTop: theme.space.sm,
height: HEIGHT / 4.8,
justifyContent: 'center',
alignItems: 'center',
width: WIDTH,
}}>
<ActivityIndicator color={theme.color.primary} size={24} />
</ScrollView>
);
}
return (
<>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingHorizontal: theme.space.sm,
paddingTop: theme.space.sm,
height: HEIGHT / 4.8,
}}>
{slidesData.explorer_slides.map((slide) => {
const type = slide.item.__typename;
return (
<TouchableOpacity
key={slide.id}
onPress={() =>
navigation.navigate(
type === 'Flux'
? 'SingleFlux'
: type === 'FluxGroup'
? 'MultipleFlux'
: 'FluxCategory',
{
headerTitle: slide.item.name,
headerItem: slide.item,
itemId: slide.item.id,
headerText:
slide.item.__typename !== 'FluxCategory'
? slide.item.description
: null,
},
)
}>
<Box
mx="xs"
bg="primary"
w={WIDTH - 120}
h={150}
radius="sm"
align="center"
justify="center"
overflow="hidden">
<Image
source={{uri: slide.image.uri}}
style={styles.imgCat}
resizeMode="cover"
/>
</Box>
</TouchableOpacity>
);
})}
</ScrollView>
<Box mt="md" h={1} w={WIDTH} bg={dark ? 'grey' : 'lightBorder'} />
</>
);
};
const renderItem = ({item, index}) => {
return (
<Box key={item - index} mb={8}>
{item.map((section, index) => {
const multiple = section.__typename === 'FluxGroup';
const subscribed = section.subscribed;
return (
<TouchableOpacity
key={section.id}
onPress={() =>
!multiple
? navigation.navigate('SingleFlux', {
headerTitle: section.name,
itemId: section.id,
headerItem: section,
subscribed: subscribed,
itemExist: exists(section),
})
: navigation.navigate('MultipleFlux', {
headerTitle: section.name,
itemId: section.id,
headerItem: section,
subscribed: subscribed,
itemExist: exists(section),
})
}>
<SubscribeItem
flux={section}
id={section.id}
channel={section.name}
title={
section.description
? section.description
: `Toutes les actualités sur ${section.name}`
}
icon={section.image?.uri ? `${section.image?.uri}` : null}
custom={section.customChannel}
pushNumber={section.frequency_numbers_all}
multiple={multiple}
button={
<>
{/* <ToggleIcon
favorite={exists(section)}
onPress={() =>
exists(section)
? handleRemoveFavorite(section)
: handleAddFavorite(section)
}
/> */}
<ToggleIcon
favorite={favExists(section)}
onPress={() =>
favExists(section)
? handleRemoveFavorite(section)
: handleAddFavorite(section)
}
/>
</>
}
/>
</TouchableOpacity>
);
})}
</Box>
);
};
const renderCategories = () => {
if (!explorerData) {
return (
<Box py="sm">
<Text mb="sm" center color="lightGrey">
Catégories en chargement
</Text>
<Loading />
</Box>
);
}
if (explorerData) {
return explorerData.explorer_categories.map((section) => {
const sectionData = sliceIntoChunks(section.related, 3);
return (
<>
<Box
w={WIDTH}
key={section.id}
dir="row"
justify="between"
align="center">
<Text
size="xl"
pt="sm"
pb="2xs"
ml="sm"
color={dark ? 'white' : 'black'}
style={styles.header}>
{section.name}
</Text>
<SeeMore
onPress={() =>
navigation.navigate('FluxCategory', {
headerTitle: section.name,
headerItem: section,
itemId: section.id,
headerText: null,
})
}
/>
</Box>
<Box>
<FlatList
horizontal
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.contentContainerStyle}
data={section ? sectionData : []}
renderItem={renderItem}
extraData={favoritesData}
keyExtractor={(item, index) => item + index}
onEndReachedThreshold={0}
/>
<Box h={1} bg={dark ? 'grey' : 'lightBorder'} mb="sm" />
</Box>
</>
);
});
}
};
if (error) {
return (
<Box f={1} justify="center" align="center">
<Box mb="xs">
<ErrorIcon
name="cloud-offline-outline"
color={dark ? theme.color.lightGrey : 'grey'}
size={32}
/>
</Box>
<Text
size="md"
center
color={dark ? 'lightGrey' : 'grey'}
style={styles.letSpacing}>
Une erreur s'est produite
</Text>
<Text
size="sm"
color={dark ? 'lightGrey' : 'grey'}
style={styles.letSpacing}>
Réessayez plus tard
</Text>
<TouchableOpacity onPress={() => refetch()}>
<Box mt="sm">
<RefreshIcon name="refresh" size={24} color={theme.color.primary} />
</Box>
</TouchableOpacity>
</Box>
);
}
if (isLoading) {
return <Loading />;
}
return (
<SafeAreaView
style={[styles.container, {backgroundColor: colors.background}]}>
<ScrollView showsVerticalScrollIndicator={false}>
<Box>{renderHeader()}</Box>
{renderCategories()}
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
width: WIDTH,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
searchBar: {
width: WIDTH,
backgroundColor: theme.color.secondary,
borderBottomColor: theme.color.secondary,
borderTopColor: theme.color.secondary,
},
inputBar: {
backgroundColor: theme.color.black,
borderRadius: theme.space.md,
},
header: {
fontFamily: 'System',
fontWeight: '700',
letterSpacing: 0,
},
icon: {
width: 25,
height: 25,
borderRadius: 6,
backgroundColor: theme.color.primary,
overflow: 'hidden',
},
iconNull: {
width: 25,
height: 25,
borderRadius: 6,
backgroundColor: theme.color.primary,
overflow: 'hidden',
},
imgCat: {
width: '100%',
height: 150,
},
letSpacing: {
letterSpacing: 0,
},
});
export default ExplorerScreen;
Am I missing something ? Or Am I doing it totally wrong haha ?
If you need more info on my code, don't hesitate to ask :)
Try this. The UI will get refreshed as soon as you delete the item or add item:
await unsubscribeFromFlux({
variables: {
id: parseInt(flux.id),
},
refetchQueries: GET_EXPLORER_SLIDES_QUERY
});
Well turns out, you can't persist a reactive variable! So I'm just refetching queries and updating my cache after a mutation :) All good now ! Thank you !

Sticky header on SectionList ReactNative

I need to create a screen Catalog(Categories and Products).
I'm using SectionList from React Native in order to achive this.
I need to make that Categories component stick on the top when you scroll product lists.
Is there any library that could help me with this Catalog screen ?
Please look at the image here..
import React from "react";
import { View, StyleSheet, SectionList } from "react-native";
import Text from "../Text";
const DATA = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"],
},
{
title: "Drinks",
data: ["Water", "Coke", "Beer"],
},
{
title: "Desserts",
data: ["Cheese Cake", "Ice Cream"],
},
];
const TabCategories = () => (
<View>
<Text>Horizontal list of categories</Text>
</View>
);
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const TestSectionList = (props) => {
return (
<View style={styles.container}>
<Text style={styles.SRC}>Some React Component</Text>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
renderSectionHeader={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
StickyHeaderComponent={TabCategories}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {},
SRC: {
fontWeight: "bold",
borderWidth: 1,
borderColor: "#fff",
padding: 10,
},
item: {
padding: 30,
},
header: {
fontWeight: "bold",
fontSize: 20,
},
});
export default TestSectionList;
stickySectionHeadersEnabled
Makes section headers stick to the top of the screen until the next one pushes it up
ListHeaderComponent
Rendered at the very beginning of the list
renderSectionHeader
Rendered at the top of each SECTION
I think this should do:
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
ListHeaderComponent={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
renderSectionHeader={TabCategories}
stickySectionHeadersEnabled
/>
You can try this library react-native-tabs-section-list
https://github.com/bogoslavskiy/react-native-tabs-section-list
If you are talking about react-native-section-list, it inherits ScrollView props, you can check in the docs, in props section, so it has stickyHeaderComponent prop which should be exactly what you want.

How to pass 'State' from child to parent in 'React Native Functional Component'?

I have created a picker component to get data from API and display it(ProductNames) in the picker list, to select one of them. Then I able to get picker item value as a productID. After that, I use this child component in the parent component, which indicates a form to submit. Now I can see the picker in the parent component(form) and I can select one of them.
My main purpose is to open an input field through the picker. So now I need to pass the item.value ( as a state 'selectedValue') to parent component to submit. How can I pass this child state to parent state?
this is my child component:
import React, {useEffect, useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {Picker} from '#react-native-picker/picker';
import AsyncStorage from '#react-native-community/async-storage';
const ProductPicker = () => {
const [selectedValue, setSelectedValue] = useState('');
const [productDetails, setproductDetails] = useState([]);
console.log('selected value', selectedValue);
useEffect(() => {
getProductList();
}, []);
const getProductList = async () => {
const token = await AsyncStorage.getItem('userToken');
fetch('http://10.0.2.2:3000/customer/get-all-products', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`,
},
})
.then((response) => response.json())
.then((json) => setproductDetails(json.data))
.catch((error) => console.error(error));
};
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
}}
>
{productDetails.map((item, index) => {
return (
<Picker.Item label={item.productName} value={item.productID} key={index}/>);
})}
</Picker>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: 'center',
},
});
export default ProductPicker;
and this is my parent component:
import * as React from 'react';
import {Button, View, Text, ScrollView, StyleSheet, Alert} from 'react-native';
import {Appbar} from 'react-native-paper';
import {TextInput, HelperText} from 'react-native-paper';
import {useEffect, useState} from 'react';
import AsyncStorage from '#react-native-community/async-storage';
import ProductPicker from './ProductPicker';
const ComplaintSubmission = ({navigation}) => {
const [productID , setproductID] = useState('');
const [description , setdescription] = useState('');
const [token, setToken] = useState('');
const saveToken = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log('token from storage', token);
setToken(token);
}
useEffect(() => {
saveToken();
fetch("http://10.0.2.2:3000/customer/lodge-complaint", {
method: "post",
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`
},
body: JSON.stringify({
description : description,
})
})
}, []);
const openAlert = () => {
Alert.alert(
"Complaint Successfully Submitted",
"We review it as soon as possible. Thank you for reaching for us!",
[{
text: "OK",
onPress : () => navigation.navigate("DashboardDrawer" ),
}]
);
}
return (
<ScrollView>
<Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Submit Complaint" />
<Appbar.Action icon="magnify" onPress={() => navigation.openDrawer()} />
</Appbar.Header>
<Text>Plese Fill the following</Text>
<View>
<ProductPicker />
<HelperText type="info">
Make sure select the correct Product
</HelperText>
</View>
<TextInput
style={styles.PIDstyle}
label="Description"
onChangeText = {(description) => setdescription(description)}
/>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>This is submittion</Text>
<Button onPress={() => openAlert()} title="Submit Complaint" />
</View>
</ScrollView>
);
};
export default ComplaintSubmission;
const styles = StyleSheet.create({
PIDstyle: {
marginTop: 30,
marginLeft: 10,
marginRight: 10,
},
});
You don't want to pass the state from child to parent, you want to pass it from parent to child. You need to lift the state up to ComplaintSubmission and use props to control ProductPicker.
In ComplaintSubmission, call the ProductPicker with the current productId and a callback to update it.
<ProductPicker productID={productID} setproductID={setproductID} />
Now ProductPicker has these props. It should use them instead of the local state selectedValue which you can now delete.
const ProductPicker = ({productID, setproductID}) => {
<Picker
selectedValue={productID}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setproductID(itemValue);
}}
>

Flat list single item with two buttons

A flat list displays information from an API, I want to add a delete button for each item. Where user can click on it and be able to delete this specific item.
Please check my code below.
<FlatList
numColumns={1}
data={this.state.allDocs}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => Linking.openURL('http://URL')}>
<Text>{item.docName}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={deleteFunction}>
<Text>Delete</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.docName}
/>
import React, { Component } from "react";
import {
SafeAreaView,
View,
FlatList,
StyleSheet,
Text,
TouchableOpacity
} from "react-native";
export default class Example extends Component {
state = {
data: [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item"
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item"
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item"
}
]
};
renderItem = ({ item }) => {
return (
<View style={styles.item}>
<Text>{item.title}</Text>
<TouchableOpacity onPress={() => this.removeValue(item.id)}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
);
};
removeValue = id => {
let newData = this.state.data.filter(item => item.id !== id);
this.setState({
data: newData
});
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
flexDirection: "row",
justifyContent: "space-between"
}
});
Change this according to your requirements.
Hope this will helps you. Feel free for doubts.
You just need to pass a function that will remove the document from your state like this:
export default class FlatListExample extends Component {
_openDoc = async (index) => {
const { allDocs } = this.state;
Linking.openURL(allDocs[i].url);
}
_deleteDoc = (index) => {
const { allDocs } = this.state;
allDocs.splice(index, 1);
this.setState({ allDocs });
}
render() {
const { allDocs } = this.state;
return (
<FlatList
data={allDocs}
keyExtractor={item => item.docName}
renderItem={({ item, index }) => (
<Fragment>
<TouchableOpacity onPress={() => this._openDoc(index)}>
<Text>{item.docName}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => this._deleteDoc(index)}>
<Text>Delete</Text>
</TouchableOpacity>
</Fragment>
)} />
);
}
}