Unable to re-render FlatList in Functional Components? - reactjs

I want to re-render this flat list whenever I delete an item, I have tried several methods but nothing works, also I am getting an error:
Hooks can only be used in functional components
import React, { useEffect,useState } from "react";
import { Alert, FlatList, StyleSheet } from "react-native";
import { SearchBar } from 'react-native-elements';
import Swipeout from 'react-native-swipeout';
import ReactDOM from "react-dom";
import ActivityIndicator from "../components/ActivityIndicator";
import Button from "../components/Button";
import Card from "../components/Card";
import colors from "../config/colors";
import listingsApi from "../api/listings";
import routes from "../navigation/routes";
import Screen from "../components/Screen";
import AppText from "../components/Text";
import useApi from "../hooks/useApi";
import filter from 'lodash.filter';
import useAuth from "../auth/useAuth";
import client from "../api/client"
// function useForceUpdate()
// {
// let[value, setState]=useState(true);
// return () => setState(!value);
}
function ListingsScreen({ navigation }) {
//let forceUpdate=useForceUpdate();
const getListingsApi = useApi(listingsApi.getListings);
const { user, logOut } = useAuth();
const [filteredDataSource, setFilteredDataSource] = useState([]);
useEffect(() => {
fetch('http://192.168.1.6:9000/api/listings')
.then((response) => response.json())
.then((responseJson) => {
setFilteredDataSource(responseJson);
//filteredDataSource(responseJson)
// console.log(responseJson)
})
.catch((error) => {
console.error(error);
alert("Couldn't retrieve the listings.")
});
}, []);
var data =[];
const searchFilterFunction = () => {
const newData = filteredDataSource.filter(function (item) {
const itemData = user.email
const itemd=item.emailuser
return(itemd === itemData)
});
//setFilteredDataSource(newData);
//searchFilterFunction();
data=newData;
}
searchFilterFunction();
return (
<Screen style={styles.screen}>
{getListingsApi.error&& (
<>
<AppText>Couldn't retrieve the listings.</AppText>
<Button title="Retry" onPress={getListingsApi.request} />
</>
)}
<ActivityIndicator visible={getListingsApi.loading} />
{
searchFilterFunction()
}
<FlatList
data={data}
keyExtractor={(data) => data._id.toString()}
renderItem={({ item,index }) => {
// ------delete function -----//
const handlePress = (userId) => {
alert("Listing Deleted Succesfully")
console.log("del")
client.post("/listings/delete", {userId})
}
const swipeSettings = {
autoClose :true,
onClose: (secId, rowId, direction) => {
},
onOpen: (secId, rowId, direction) => {
},
right: [
{
onPress: () => {
Alert.alert(
'Alert',
'Are you sure you want to delete ?',
[
{ text : 'No', onPress: () => console.log('Cancel Pressed'),style : 'cancel'},
{ text : 'Yes', onPress:() => handlePress(item._id) }
]
);
},
text: 'Delete', type : 'delete',
}
],
// rowId: this.index,
// sectionId: 1
};
return(
<Swipeout {...swipeSettings} backgroundColor="transparent" style={styles.btn}>
<Card
title={item.title}
subTitle={"$" + item.price}
category={item.categoryId}
imageUrl={item.image}
onPress={() => navigation.navigate(routes.LISTING_DETAILS, item)}
/>
</Swipeout>);
}}
/>
</Screen>
);
}
const styles = StyleSheet.create({
screen: {
padding: 5,
backgroundColor: colors.light,
},
btn: {
flex: 1,
}
});
export default ListingsScreen;
Thanks in advance.

Related

How to console.log the

I have a simple React component and inside of it I am fetching data from a remote API, and I want to console.log it in useEffect. I am trying to do it but nothing doesn't get logged into the console, why? What am I missing here? Here is the component:
import React, { useState, useEffect } from 'react';
import { useLocalization } from '#progress/kendo-react-intl';
import { Card, CardHeader, Avatar, CardTitle, CardSubtitle } from '#progress/kendo-react-layout';
import { guid } from '#progress/kendo-react-common';
import { Scheduler } from './../components/Scheduler';
import { employees } from './../resources/employees';
import { images } from './../resources/images';
import { orders, ordersModelFields } from './../resources/orders';
import { teams } from './../resources/teams';
// const orderEmployees = employees.filter(employee => employee.jobTitle === 'Sales Representative');
// const initialFilterState = { };
// orderEmployees.forEach(employee => {
// if(employee.fullName === 'Wait Peperell') {
// initialFilterState[employee.id] = false;
// } else {
// initialFilterState[employee.id] = true;
// }
// });
const Planning = () => {
const localizationService = useLocalization();
const [filterState, setFilterState] = React.useState(initialFilterState);
const [data, setData] = React.useState(orders);
const [fetchedData, setFetchedData] = React.useState(null);
useEffect(() => {
fetch("https://mocki.io/v1/29b83c0b-1a55-430d-a173-92b3632e04aa")
.then(response => response.json())
// 4. Setting *dogImage* to the image url that we received from the response above
.then(data => setFetchedData(data))
console.log(fetchedData)
},[])
// console.log(fetchedData)
const onDataChange = React.useCallback(
({ created, updated, deleted }) => {
setData(old => old
// Filter the deleted items
.filter((item) => deleted.find(current => current[ordersModelFields.id] === item[ordersModelFields.id]) === undefined)
// Find and replace the updated items
.map((item) => updated.find(current => current[ordersModelFields.id] === item[ordersModelFields.id]) || item)
// Add the newly created items and assign an `id`.
.concat(created.map((item) => Object.assign({}, item, { [ordersModelFields.id]: guid() }))))
},
[]
);
const onEmployeeClick = React.useCallback(
(employeeId) => {
setFilterState({
...filterState,
[employeeId]: !filterState[employeeId]
});
},
[filterState, setFilterState]
);
return (
<div id="Planning" className="planning-page main-content">
<div className="card-container grid">
<h3 className="card-title">{localizationService.toLanguageString('custom.teamCalendar')}</h3>
{
orderEmployees.map(employee => {
return (
<div
key={employee.id}
onClick={() => onEmployeeClick(employee.id)}
style={!filterState[employee.id] ? {opacity: .5} : {}}
>
<Card style={{ borderWidth: 0, cursor: 'pointer'}}>
<CardHeader className="k-hbox" >
<Avatar type='image' shape='circle' size={'large'} style={{
borderWidth: 2,
borderColor: teams.find(({teamID}) => teamID === employee.teamId).teamColor,
}}>
<div className="k-avatar-image" style={{
backgroundImage: images[employee.imgId + employee.gender],
backgroundSize: 'cover',
backgroundPosition: 'center center',
}}
/>
</Avatar>
<div>
<CardTitle style={{color: teams.find(({teamID}) => teamID === employee.teamId).teamColor}}>{employee.fullName}</CardTitle>
<CardSubtitle>{employee.jobTitle}</CardSubtitle>
</div>
</CardHeader>
</Card>
</div>
);
})
}
<div className="card-component" >
<Scheduler
data={data.filter(event => filterState[event.employeeID])}
onDataChange={onDataChange}
modelFields={ordersModelFields}
resources={[
{
name: 'Teams',
data: teams,
field: 'teamID',
valueField: 'teamID',
textField: 'teamName',
colorField: 'teamColor'
}
]}
/>
</div>
</div>
</div>
);
}
export default Planning;
I also tried to place the console.log outside of useEffect but still, nothing gets console.logged.
You need to look how useEffect work, setFetchedData is async.
Create another useEffect only for console.log.
useEffect(() => {
console.log(fetchedData);
},[fetchedData]); // Update at the first render + when fetchedData state change.
You can do it like this
useEffect(() => {
fetch("https://mocki.io/v1/29b83c0b-1a55-430d-a173-92b3632e04aa")
.then((response) => response.json())
// 4. Setting *dogImage* to the image url that we received from the response above
.then((data) => {
setFetchedData(data);
console.log(data);
});
}, []);
or juste create another useEffect that listens to fetchedData change, like this
useEffect(() => {
console.log(fetchedData);
}, [fetchedData]);

How can I display an array of images after get the urls React Native

Im trying to display a preview of the picked images after pick them, im using this library import { AssetsSelector } from 'expo-images-picker';
This is the code to pick the image:
import React, { useMemo } from 'react';
import { Text, View, StyleSheet, SafeAreaView, Alert } from 'react-native';
import { AssetsSelector } from 'expo-images-picker';
import { Ionicons } from '#expo/vector-icons';
import { AntDesign } from '#expo/vector-icons';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { MediaType } from 'expo-media-library';
import { useNavigation } from '#react-navigation/core';
export default function App() {
const navigation = useNavigation();
const onSuccess = (data: any) => {
const filteredUri = data.filter(({ uri }) => uri).map(({ uri }) => uri);
navigation.navigate('AddProductScreen',
{
filteredUri: filteredUri,
});
};
const widgetErrors = useMemo(
() => ({
errorTextColor: 'black',
errorMessages: {
hasErrorWithPermissions: 'Please Allow media gallery permissions.',
hasErrorWithLoading: 'There was error while loading images.',
hasErrorWithResizing: 'There was error while loading images.',
hasNoAssets: 'No images found.',
},
}),
[]
);
const widgetSettings = useMemo(
() => ({
getImageMetaData: false,
initialLoad: 100,
assetsType: [MediaType.photo, MediaType.video],
minSelection: 1,
maxSelection: 3,
portraitCols: 4,
landscapeCols: 4,
}),
[]
);
const widgetResize = useMemo(
() => ({
width: 50,
compress: 0.7,
base64: false,
saveTo: 'jpeg',
}),
[]
);
const _textStyle = {
color: 'white',
};
const _buttonStyle = {
backgroundColor: 'orange',
borderRadius: 5,
};
const widgetNavigator = useMemo(
() => ({
Texts: {
finish: 'finish',
back: 'back',
selected: 'selected',
},
midTextColor: 'black',
minSelection: 1,
buttonTextStyle: _textStyle,
buttonStyle: _buttonStyle,
onBack: () => {navigation.goBack()},
onSuccess: (e: any) => onSuccess(e),
}),
[]
);
const widgetStyles = useMemo(
() => ({
margin: 2,
bgColor: 'white',
spinnerColor: 'blue',
widgetWidth: 99,
videoIcon: {
Component: Ionicons,
iconName: 'ios-videocam',
color: 'tomato',
size: 20,
},
selectedIcon: {
Component: Ionicons,
iconName: 'ios-checkmark-circle-outline',
color: 'white',
bg: '#0eb14970',
size: 26,
},
}),
[]
);
return (
<SafeAreaProvider>
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<AssetsSelector
Settings={widgetSettings}
Errors={widgetErrors}
Styles={widgetStyles}
Navigator={widgetNavigator}
/>
</View>
</SafeAreaView>
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
This is the code where I want o display the images, im using react navigation props to get the array:
const showPickedImages = ({ route, navigations }) => {
const navigation = useNavigation();
var filteredUri = route.params?.filteredUri;
return(
<View>
//Here I want to show the preview of the picked images
<View/>
)}
You can use Flatlist or ScrollView for this.
<Flatlist
ListEmptyComponent={
<Text>Loading...</Text>} // show loading text until you get the data
data={filteredUri}
renderItem={(uri)=>
<Image source={{uri}} style={{widht:100, height:100}} />
}
/>

infinite scroll relay modern (v11) using the pagination container and loadMore

Using react 17 and relay-modern 11 I want to build a list, with which, when one reaches the end, they can click a button that says load more and it adds more entries to the list. Here is what I have so far. The rows are name and cursor
see I should click load more and it should add an extra 5 rows of data. This is what happens when I click load more
See it did fetch 5 new nodes. I can tell because the cursors are unique. However it did not append it to the list like I wanted.
How can I get this list to continue to build all the time as I keep clicking load more, until there is no more data?
Here is my code:
the root of the component:
// index.js
import React, { useState } from "react";
import { Text, View } from "react-native";
import { QueryRenderer, useRelayEnvironment } from "react-relay";
import PaginatedProfilesListContainer from "./PaginatedProfilesListContainer";
const ProfilesList = () => {
const environment = useRelayEnvironment();
const [count, setCount] = useState(5);
const [name, setName] = useState("");
const query = graphql`
query ProfilesListQuery(
$count: Int!
$cursor: String
$filter: ProfileFilter
) {
...PaginatedProfilesListContainer_list
}
`;
const filter = {
name,
};
const variables = {
count: 5,
cursor: null,
filter,
};
const render = ({ error, props, retry }) => {
if (error) {
return (
<View>
<Text>{error.message}</Text>
</View>
);
} else if (props) {
return (
<View>
<PaginatedProfilesListContainer
pagination={props}
count={count}
setCount={setCount}
name={name}
setName={setName}
filter={filter}
/>
</View>
);
} else {
return (
<View>
<Text>loading profiles list...</Text>
</View>
);
}
};
console.log("vriable", variables)
return (
<QueryRenderer
environment={environment}
query={query}
variables={variables}
render={render}
/>
);
};
export default ProfilesList;
here is the component that should be listing the object
// PaginatedProfilesListContainer.js
import React from "react";
import { Text, View } from "react-native";
import { Button } from "react-native-paper";
import { createPaginationContainer } from "react-relay";
import { FadoTextInput } from "../forms/fadoTextInput";
const PaginatedProfilesListContainer = (props) => {
console.log(props);
console.log("createPaginationContainer", createPaginationContainer)
// console.log(pagination)
const { pagination, count, setCount, name, setName, relay, filter } = props;
const { hasMore, loadMore, refetchConnection } = relay;
console.log(loadMore)
const { profiles } = pagination;
const { edges, pageInfo } = profiles;
return (
<View>
<View>
<FadoTextInput
dense={true}
isNumeric={true}
graphqlErrors={[]}
label="count"
errorKey="count"
// savedValue={price.amount}
value={count}
onChangeText={setCount}
/>
<FadoTextInput
dense={true}
isNumeric={false}
graphqlErrors={[]}
label="name"
errorKey="name"
// savedValue={price.amount}
value={name}
onChangeText={setName}
/>
</View>
{edges.map(({ cursor, node: { name } }) => (
<View key={cursor} style={{ display: "flex", flexDirection: "row"}}>
<Text>{name}</Text>
<Text>{cursor}</Text>
</View>
))}
<Button disabled={!hasMore()} onPress={() => loadMore(count, (error) => { error && console.log("error", error); })}>
Load More
</Button>
</View>
);
};
export default createPaginationContainer(
PaginatedProfilesListContainer,
{
pagination: graphql`
fragment PaginatedProfilesListContainer_list on RootQueryType {
profiles(first: $count, after: $cursor, filter: $filter)
#connection(key: "PaginatedProfilesListContainer_profiles") {
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
edges {
cursor
node {
name
}
}
}
}
`,
},
{
direction: 'forward',
query: graphql`
query PaginatedProfilesListContainerQuery(
$count: Int!
$cursor: String
$filter: ProfileFilter
) {
...PaginatedProfilesListContainer_list
}
`,
getConnectionFromProps(props) {
console.log(props)
return props.pagination?.profiles
},
getFragmentVariables(prevVars, totalCount) {
return {
...prevVars,
count: totalCount,
};
},
getVariables(props, { count, cursor }, fragmentVariables) {
return {
count,
cursor,
filter: {},
};
},
}
);
I got the inspiration for this approach from https://github.com/facebook/relay/issues/1705
Note: I tried to use the #stream_connection, but the Elixir Absinthe on the backend doesn't seem tpo support it.
I know this is long so please any help appreciated :)

How to pass data to different screens

I need to pass data from one screen to anothre, an main screen loads and then loads the data from a mock api, it sets the state with providerData, then navigation gets created. I want to be able to use the data i got in the ProviderDetailScreen.js in ProviderOverview.js
Here is ProviderDetailsScreen.js
import React from 'react';
import {StatusBar, View} from 'react-native';
import ProviderOverview from './ProviderOverview';
import ProviderServicesList from './ProviderServicesList';
import NavigationbarHeaderWithExpandedImage from '../components/NavigationbarHeaderWithExpandedImage';
import TopTabbarComponent from '../components/TopTabbarComponent';
import mockApi from '../mockApi';
const ProviderDetailScreen = ({navigation, route}) => {
const [providerData, setProviderData] = React.useState({});
const [loading, setLoading] = React.useState(false);
//navigation setup
React.useLayoutEffect(() => {
navigation.setOptions({
header: ({scene, previous}) => {
return (
<NavigationbarHeaderWithExpandedImage
scene={scene}
previous={previous}
navigation={navigation}
backgroundImage={providerData?.profile?.backgroundImage}
/>
);
},
});
}, [navigation, providerData]);
//get results from API
const getServices = React.useCallback(async () => {
try {
setLoading(true);
const res = await mockApi.get('/Provider/Profile');
if (res.ok && res.body.data) {
// use this data in overview and service list
setLoading(false);
setProviderData(res.body.data);
} else {
throw new Error('Unable to retrieve profile');
}
} catch (error) {
console.log(error);
}
}, []);
React.useEffect(() => {
console.warn('here');
getServices();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<StatusBar barStyle="light-content" />
<TopTabbarComponent
items={[
{
title: 'Overview',
component: ProviderOverview,
},
{
title: 'Services',
component: ProviderServicesList,
},
{
title: 'Reviews',
component: View,
},
]}
/>
</>
);
};
export default ProviderDetailScreen;
Here is The ToTabbarComponent.js that created a tab for each item in providerdetailsscreen
import React from 'react';
import {StyleSheet} from 'react-native';
import {createMaterialTopTabNavigator} from '#react-navigation/material-top-tabs';
import {PRIMARY, WHITE, INACTIVE_TINT_COLOR, ACCENT} from '../lib/colors';
import fonts from '../lib/fonts';
const Tab = createMaterialTopTabNavigator();
const TopTabbarComponent = ({items}) => {
const tabs = () => {
let tabs = [];
// loop to create tabs
for (const [index, item] of items.entries()) {
tabs.push(
<Tab.Screen
name={item.title}
title={item.title}
component={item.component}
key={index}
/>,
);
}
return tabs;
};
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: WHITE,
labelStyle: [styles.labelStyle, fonts.medium],
inactiveTintColor: INACTIVE_TINT_COLOR,
style: styles.style,
indicatorStyle: styles.indicatorStyle,
}}>
{tabs()}
</Tab.Navigator>
);
};
const styles = StyleSheet.create({
tabBarOptions: {},
labelStyle: {
fontSize: 14,
},
indicatorStyle: {
backgroundColor: ACCENT,
height: 2,
},
style: {
backgroundColor: PRIMARY,
},
});
export default TopTabbarComponent;
Here is the ProviderOverview.js where i want to use that data that gets set in ProviderDetailsScreen.js
//WANT TO USE DATA HERE
import React from 'react';
import {StyleSheet, View, ScrollView, Text} from 'react-native';
import MediumText from '../components/MediumText';
const ProviderOverview = ({navigation}) => {
return (
<ScrollView style={styles.container}>
<View>
<MediumText onPress={() => navigation.push('PROVIDER_MESSAGE')}>
Send a message
</MediumText>
</View>
<View></View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default ProviderOverview;
I am still learning: Things i have tried:
In providerDetailsscreen i tried to set providerdata for each item, i tried setting a test var with some string to see if i can get it in ProviderOverview. But providerOverview only has Navigation prop.
Thanks in advance.
Try this way
ProviderDetailsScreen.js
<TopTabbarComponent
items={[
{
title: 'Overview',
component: ProviderOverview,
providerData: providerData, // set providerData to ProviderOverview
},
{
title: 'Services',
component: ProviderServicesList,
},
{
title: 'Reviews',
component: View,
},
]}
/>
TopTabbarComponent
// loop to create tabs
for (const [index, item] of items.entries()) {
const CompView = item.component;
const providerData = item.providerData || {};
tabs.push(
<Tab.Screen name={item.title} title={item.title} key={index}>
{(props) => <CompView {...props} {...providerData} />}
</Tab.Screen>
);
}
You can use navigation props
this.props.navigation.navigate('MyData', {data: data,});
https://reactnavigation.org/docs/navigation-prop/#navigate
Or add redux to your project https://redux.js.org/introduction/getting-started

GSAP animate elements in an array fetched from the server

I would like to animate each element in an array with TweenMax.staggerFrom. At the moment I created this working sample
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
const AnimateView = () => {
const data = [
{ title: 'Title 1', value: 1000 },
{ title: 'Title 2', value: 1100 },
{ title: 'Title 3', value: 1200 },
];
const elementsRef = useRef(data.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
{data.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
export default AnimateView;
The difference is that I want data array to be fetched from the server. I cannot figure out why in elementsRef I'm getting no attached refs. Below you can check what I wanted to achieve.
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
import { connect } from 'react-redux';
import { fetchData } from 'actions';
const AnimateView = ({ combineFetching, income }) => {
const elementsRef = useRef(income.length && income.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
<button onClick={fetchData}>Click</button>
{income.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
const mapDispatchToProps = state => ({
income: state.budget.income,
});
export default connect(
mapDispatchToProps,
{ fetchData },
)(AnimateView);
On Click I want to fetch data from database and animate each element in the array.

Resources