React Native performance slow - javascript

I am new to React Native, I've created an Instagram Clone with customizations, but there got some issues.
FlatList Re-Renderring is slow.
When the like button is Pressed, FlatList is taking 2 Seconds to re-render. So I tried into Flipkart's RecyclerView Package, that is too taking 400-600 ms. I've came to know that Instagram and Facebook are built at React Native, but they don't take this much time on like. I guess something is wrong in my code.
I got Recycler View package from here
Re-Rendering is slow.
In those screens without any List, there is too an issue of slow re-rendering.
Material Top Tabs Navigation is slow.
I've found that React Navigations's Material Top navigation is working absolutely fine on swipe, but on button click, it is taking 2-4 Seconds.
Here is my code for feed page.
import React, { useEffect, useState, useRef } from 'react';
import { SafeAreaView, Pressable, AppRegistry, Text, View, Image, TouchableOpacity, StyleSheet, ImageBackground, ActivityIndicator, Platform } from 'react-native';
import { Pranah } from '../pranah/cust';
import { colors } from '../pranah/colors';
import { uni } from '../css/uni';
import axios from 'axios';
import base64 from 'react-native-base64';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { DataProvider, LayoutProvider, RecyclerListView } from 'recyclerlistview';
import { UserHead } from './tminc/userhead';
import { AntDesign, FontAwesome5, Feather } from '#expo/vector-icons';
import { design } from './tminc/design';
import { WebBasedNavigation } from './tminc/widenav'
const style = StyleSheet.create({
web: {
width: uni.dev("100%", "100%", "40%"),
height: uni.dev("100%", "100%", uni.height - 50),
marginLeft: uni.dev(0, 0, 10 / 100 * uni.width)
}
});
const postDesign = {
width: uni.dev(95 / 100 * uni.width, 95 / 100 * uni.width, 35 / 100 * uni.width),
height: uni.dev(95 / 100 * uni.width, 95 / 100 * uni.width, 35 / 100 * uni.width),
backgroundColor: "#ededed",
borderRadius: 10,
}
const iconDynamicSizing = 25;
const iconDesign = StyleSheet.create({
icon: {
margin: 10
}
});
//POST PART IN PARTS
//USER HEAD
function ListHead(txt) {
return (
<>
<Text
style={{
fontSize: 35,
fontWeight: "bold",
margin: 20
}}
>{txt.txt}</Text>
</>
)
}
function MediaCont(obj) {
return (
<View
style={design.media}
>
<Image
source={{ uri: obj.url }}
defaultSource={{ uri: obj.url }}
style={postDesign}
/>
<View
style={design.mediaSnap}
>
<Text style={design.mediaCap}>{obj.caption.length > 20 ? `${obj.caption.substring(0, 20)}...` : obj.caption}</Text>
</View>
</View>
);
}
function TextCont(obj) {
return (
<View
style={design.textContParent}
>
<View
style={[postDesign, design.center]}
>
<Text
style={design.textMain}
>{obj.caption}</Text>
</View>
</View>
);
}
let layoutProvider = new LayoutProvider(
index => {
return index == 0 ? "HEAD" : "NORMAL";
},
(type, dim) => {
switch (type) {
case "NORMAL":
dim.height = uni.dev(uni.width + 150, uni.width + 150, 40 / 100 * uni.width + 150);
dim.width = uni.dev(uni.width, uni.width, 40 / 100 * uni.width);
break;
case "HEAD":
dim.height = 85;
dim.width = uni.dev(uni.width, uni.width, 40 / 100 * uni.width);
break;
}
}
);
function PostLikes(obj) {
let post = obj.postId;
let like = parseInt(obj.like);
let navigation = obj.screenNav;
let toprint;
if (like == 0) {
toprint = uni.lang("इसे पसंद करने वाले पहले व्यक्ति बनें", "Be first to like this.");
} else if (like == 1) {
toprint = uni.lang("एक व्यक्ति द्वारा पसंद किया गया", "Liked by one person");
} else {
like = String(like);
toprint = uni.lang(`${like} लोगो ने पसंद किया`, `${like} likes`);
}
return (
<>
<TouchableOpacity
onPress={() => {
navigation.push('LikeList', { postId: post });
}}
>
<Text
style={{
marginLeft: uni.dev(5 / 100 * uni.width, 5 / 100 * uni.width, 4 / 100 * uni.width),
fontWeight: "bold",
marginTop: 5
}}
>{toprint}</Text>
</TouchableOpacity>
</>
);
}
const headerComp = ({
title: uni.lang("सबकुछ ||", "Everything."),
type: "head"
});
export function Feed({ navigation }) {
const [List, setData] = useState([headerComp]);
const [FooterConst, setFoot] = useState(true);
const [start, setStart] = useState(0);
// navigation.setOptions({ tabBarVisible: false });
let dataProvider = new DataProvider((r1, r2) => {
return r1 !== r2;
}).cloneWithRows(List);
function fetchMore() {
AsyncStorage.getItem("mail")
.then((val) => {
let mail = val;
AsyncStorage.getItem("pass")
.then((value) => {
let pass = value;
// CONNECTING TO SERVER
axios({
method: 'post',
url: uni.bind('feed'),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: uni.string({
mail: mail,
pass: base64.encode(pass),
start: start
})
})
.then((resp) => {
if (resp.status == 200) {
let page = resp.data;
/*
SERVER RETURNS
nomore | followernull | error | invalid | {json data}
*/
if (uni.logic(page) === "error") {
uni.Error();
} else if (uni.logic(page) === "followernull" || uni.logic(page) === "nomore") {
//SET FOOTER
setFoot(false);
} else if (uni.logic(page) === "invalid") {
//SIGNOUT
uni.signOut(navigation);
} else {
setStart(start + 20);
setData(
[
...List,
...page
]
);
}
} else {
uni.Error();
}
})
.catch((e) => {
uni.Error();
});
})
.catch((e) => { uni.signOut(navigation) })
})
.catch(() => { uni.signOut(navigation) })
}
function PostAction(obj) {
let index = obj.in;
function addRemoveLike() {
let temp = List;
temp[index].liked = temp[index].liked === "true" ? "false" : "true";
// console.warn(temp[index]);
setData([...temp]);
//SAVING LIKE ON SERVER
AsyncStorage.getItem("mail")
.then((val) => {
let mail = val;
AsyncStorage.getItem("pass")
.then((value) => {
let pass = value;
// CONNECTING TO SERVER
axios({
method: 'post',
url: uni.bind('like'),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: uni.string({
mail: mail,
pass: base64.encode(pass),
post: String(obj.id)
})
})
.then((resp) => {
if (resp.status == 200) {
let page = resp.data;
/*
SERVER RETURNS
true | error | invalid
*/
if (uni.logic(page) === "error") {
uni.Error();
} else if (uni.logic(page) === "invalid") {
uni.signOut(navigation);
}
} else {
uni.Error();
}
})
.catch((e) => { uni.Error() });
})
.catch((e) => { uni.signOut(navigation) })
})
.catch(() => { uni.signOut(navigation) })
}
return (
<>
<View
style={design.postActionParent}
>
<TouchableOpacity
onPress={() => {
// console.warn(likeRef.current);
// console.warn(likeRef.current);
addRemoveLike();
}}
><AntDesign name={obj.liked === "true" ? "heart" : "hearto"} size={iconDynamicSizing} color="black" style={iconDesign.icon} /></TouchableOpacity>
<TouchableOpacity onPress={() => {
navigation.push('Comment', { postId: obj.id });
}}><FontAwesome5 name="comment" size={iconDynamicSizing} color="black" style={iconDesign.icon} /></TouchableOpacity>
<TouchableOpacity><Feather name="send" size={iconDynamicSizing} color="black" style={iconDesign.icon} /></TouchableOpacity>
<TouchableOpacity><AntDesign name="retweet" size={iconDynamicSizing} color="black" style={iconDesign.icon} /></TouchableOpacity>
</View>
<View
style={design.underLinePrnt}
>
<View style={design.underline}></View>
</View>
</>
);
}
function TextPost(params) {
let item = params.data;
let index = params.in;
return (
<>
<UserHead dp={item.dp} name={item.name} user={item.username} />
<Pressable onLongPress={() => { alert('null') }}><TextCont caption={item.caption} /></Pressable>
<PostLikes like={item.fav} postId={item.id} screenNav={navigation} />
<PostAction liked={item.liked} in={index} id={item.id} />
</>
);
}
function MediaPost(params) {
let item = params.data;
let index = params.in;
return (
<>
<UserHead dp={item.dp} name={item.name} user={item.username} />
<MediaCont url={item.url} caption={item.caption} />
<PostLikes like={item.fav} postId={item.id} screenNav={navigation} />
<PostAction liked={item.liked} in={index} id={item.id} />
</>
);
}
function ListItem(type, data, index) {
let item = data;
return item.type === "head" ? <ListHead txt={item.title} /> : item.type === "text" ? <TextPost data={item} in={index} /> : <MediaPost data={item} in={index} />;
}
useEffect(function () {
// let tmp = List.push(json);
// setData([
// ...List,
// ...json
// ]);
navigation.setOptions({ tabBarVisible: uni.isPC() == true ? false : true })
fetchMore();
}, []);
function footerComp() {
return FooterConst == true ? (
<>
<ActivityIndicator size={"large"} color={colors.primary} />
<Pranah.br height={20} />
</>) : (
<>
<Text
style={{
textAlign: "center",
width: "100%",
fontSize: 20,
fontWeight: "bold",
paddingBottom: 13
}}
>{uni.lang("सूची का अंत", "End of Posts")}</Text>
</>
);
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: "#FFFFFF" }}>
<ImageBackground
style={{
width: "100%",
height: "100%"
}}
source={require('../assets/background_mobile.png')}
>
<Pranah.stb />
<Pranah.pranahHead nav={navigation} />
<View
style={{ width: "100%", height: "100%", flexDirection: "row" }}
>
<View
style={style.web}
>
<RecyclerListView
dataProvider={dataProvider}
rowRenderer={ListItem}
layoutProvider={layoutProvider}
extendedState={{ List }}
renderFooter={footerComp}
onEndReached={fetchMore}
/>
</View>
<WebBasedNavigation navigation={navigation} />
</View>
</ImageBackground>
</SafeAreaView>
);
}
There were lags in iOS and Web too but those were acceptable.
I know, I've done very wrong with AsyncStorage, please tell me a short way to do that too.
Thanks in advance.

In your case, I don't know why you are using another package when react-native contains a built-in component called as FlatList which is backed by virtualised rendering.
Make this changes
rowRenderer={() => ListItem()}
renderFooter={() => footerComp()}
Check the () => arrow function this will assign the method only once on the initial render. You need to provide a https://reactnative.dev/docs/flatlist#keyextractor prop to create a unique ID for all the rendered items (will be used when you want to do some action like remove element or update).
With this simple change, you should see a lot of performance improvement for the initial render & for each re-render.
Do the same for the props that accept a function as a param.
IDK why are you storing the value on async storage they should be store in a local variable like the useState hook. Keep an eye on the API call if you do frequent API calls or on each re-render surely it will reduce the app performnce.
My Opinion
React & React Native are fast by default but developers use a lot of anti-pattern code and make the application slow and complain RN is slow.
Here you can find some of the common things which cause performance issues in react native.
https://reactnative.dev/docs/performance

Related

Cannot update a component (`Categoriess`) while rendering a different component (`CellRenderer`)

I am getting this warning.
Warning: Cannot update a component (Categoriess) while rendering a different component (CellRenderer). To locate the bad setState() call inside, follow the stack trace as described in https://reactjs.org/link/setstate-in-render%60
I tried too much to fix this issue but I can't. Can anyone please solve this issue?
my code:
`
import { DrawerActions, useNavigation } from '#react-navigation/native';
import { Divider, NativeBaseProvider } from 'native-base';
import fetch from 'node-fetch';
import React, { useEffect, useState } from 'react';
import {
ActivityIndicator,
FlatList,
StyleSheet, Text, TouchableOpacity, View
} from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
import { API_KEY, URL } from '../../globalVariable';
const Categoriess = () => {
const [data, setData] = useState([]);
const [homeID, setHomeId] = useState([]);
const [loading, setLoading] = useState(true)
const navigation = useNavigation();
const option = () => {
let url = `${URL}/store-api/category`;
let options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'sw-access-key': `${API_KEY}`,
},
};
fetch(url, options)
.then(res => res.json())
.then(json => {
setData(!!json?.elements ? json.elements : [])
setLoading(false)
})
.catch(err => { });
};
useEffect(() => {
option();
}, []);
return (
<View >
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.closeDrawer())}>
<View style={styles.closeDrawer}>
<Text style={styles.text}>Close Menu</Text>
<AntDesign name='close' size={30} />
</View>
</TouchableOpacity>
<Text style={{ fontSize: 25, fontWeight: 'bold', textAlign: 'center' }}>
Categories
</Text>
{
loading ? <ActivityIndicator /> : <FlatList
data={data}
initialNumToRender={7}
renderItem={({ item }) => {
{
if (item.level === 1 && item.afterCategoryId === null) {
setHomeId(item.id);
}
}
const newLocal =
item.level === 2 && item.parentId === homeID ? item.parentId : '';
return (
<View >
<View>
<View>
{item.visible === true && item.parentId === newLocal ? (
<View>
<TouchableOpacity
onPress={() => navigation.navigate('Products', item)}>
<Text style={styles.category}>{item.name}</Text>
<NativeBaseProvider>
<Divider thickness={3} bg="red.200" />
</NativeBaseProvider>
</TouchableOpacity>
{data.map((curElem, index) => {
return item.id === curElem.parentId ? (
<TouchableOpacity
key={index}
onPress={() =>
navigation.navigate('Products', curElem)
}>
<Text style={styles.subCategory}>
- {curElem.name}
</Text>
<NativeBaseProvider>
<Divider thickness={1} />
</NativeBaseProvider>
</TouchableOpacity>
) : null;
})
}
</View>
) : null}
</View>
</View>
</View>
);
}}
/>
}
</View>
);
};
export default Categoriess;
const styles = StyleSheet.create({
category: {
marginHorizontal: 15,
marginVertical: 5,
paddingVertical: 10,
fontSize: 18,
color: '#6779b4',
fontFamily: "Montserrat-Bold"
},
subCategory: {
marginLeft: 30,
marginBottom: 10,
fontSize: 15,
paddingVertical: 10,
fontFamily: "Montserrat-Bold"
},
closeDrawer: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
paddingVertical: 5,
},
text: {
fontSize: 18,
fontFamily: "Montserrat-Bold"
},
});
`
Check this part in renderItem prop. This issue often happens when you trying setState something inside render function
if (item.level === 1 && item.afterCategoryId === null) {
setHomeId(item.id);
}
Looks like you shouldn't store this value in state. Just check it in every renderItem will be enough. FlatList optimisations do all other work well.
const homeID = item.level === 1 && item.afterCategoryId === null ? setHomeId(item.id) : null;
As example

How to handle volumes of multiple track in react-native sound, i want to play 2 sound together and if i want to decrease sound of one

Here is my code screen code
import React, {Component} from 'react';
import {StyleSheet, Text, TouchableOpacity, View, ScrollView, Alert} from 'react-native';
import Sound from 'react-native-sound';
import BgSoundPlayer from '../../../../Components/BgSoundPlayer/BgSoundPlayer';
const audioTests = [
{
title: 'mp3 remote download',
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
},
{
title: "mp3 remote - file doesn't exist",
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-9.mp3',
},
];
const Button = ({title, onPress}) => (
<TouchableOpacity onPress={onPress}>
<Text style={styles.button}>{title}</Text>
</TouchableOpacity>
);
const Header = ({children, style}) => <Text style={[styles.header, style]}>{children}</Text>;
const Feature = ({
title,
onPress,
buttonLabel = 'PLAY',
status,
volumeIncrease,
volumeDecrease,
}) => (
<View style={styles.feature}>
<Header style={{flex: 1}}>{title}</Header>
{status ? <Text style={{padding: 5}}>{resultIcons[status] || ''}</Text> : null}
<Button title={buttonLabel} onPress={onPress} />
<Button title="Volume Increase" onPress={volumeIncrease} />
<Button title="volume Decrease" onPress={volumeDecrease} />
</View>
);
const resultIcons = {
'': '',
pending: '?',
playing: '\u25B6',
win: '\u2713',
fail: '\u274C',
};
function setTestState(testInfo, component, status) {
component.setState({tests: {...component.state.tests, [testInfo.title]: status}});
}
/**
* Generic play function for majority of tests
*/
function playSound(testInfo, component) {
setTestState(testInfo, component, 'pending');
BgSoundPlayer.setSound(testInfo);
}
class MainView extends Component {
constructor(props) {
super(props);
Sound.setCategory('Playback', true); // true = mixWithOthers
// Special case for stopping
this.stopSoundLooped = () => {
if (!this.state.loopingSound) {
return;
}
this.state.loopingSound.stop().release();
this.setState({
loopingSound: null,
tests: {...this.state.tests, ['mp3 in bundle (looped)']: 'win'},
});
};
this.state = {
loopingSound: undefined,
tests: {},
};
}
render() {
return (
<View style={styles.container}>
<ScrollView style={styles.container} contentContainerStyle={styles.scrollContainer}>
{audioTests.map(testInfo => {
return (
<Feature
status={this.state.tests[testInfo.title]}
key={testInfo.title}
title={testInfo.title}
onPress={() => {
playSound(testInfo, this);
}}
volumeIncrease={() => {
BgSoundPlayer.increaseVolume();
}}
volumeDecrease={() => {
BgSoundPlayer.decreaseVolume();
}}
/>
);
})}
<Feature
title="mp3 in bundle (looped)"
buttonLabel={'STOP'}
onPress={() => {
BgSoundPlayer.pouse();
}}
/>
</ScrollView>
</View>
);
}
}
export default MainView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'gray',
},
scrollContainer: {},
title: {
fontSize: 20,
fontWeight: 'bold',
paddingTop: 30,
padding: 20,
textAlign: 'center',
backgroundColor: 'rgba(240,240,240,1)',
},
button: {
fontSize: 20,
backgroundColor: 'rgba(220,220,220,1)',
borderRadius: 4,
borderWidth: 1,
borderColor: 'rgba(80,80,80,0.5)',
overflow: 'hidden',
padding: 7,
},
header: {
textAlign: 'left',
},
feature: {
padding: 10,
borderTopWidth: 1,
borderBottomWidth: 1,
},
});
And my BgSoundPlayer file
import {Alert} from 'react-native';
import Sound from 'react-native-sound';
Sound.setCategory('Playback', true);
class BgSoundPlayer1 {
setSound(soundURL) {
try {
this.soundURL = soundURL;
if (soundURL.isRequire) {
this.soundRef = new Sound(soundURL.url, error =>
this.callback(error, this.soundRef),
);
} else {
this.soundRef = new Sound(soundURL.url, Sound.MAIN_BUNDLE, error =>
this.callback(error, this.soundRef),
);
}
} catch (error) {
console.log('SOUNDREFERROR::', error);
}
}
callback(error, sound) {
try {
if (error) {
Alert.alert('error', error.message);
return;
}
//this.soundURL.onPrepared && this.soundURL.onPrepared(sound);
sound.play(() => {
sound.release();
});
} catch (error) {
console.log('CALL_BACKERROR::', error);
}
}
getVolume() {
return this.soundRef?.getVolume();
}
increaseVolume(soundURL) {
console.log('CHECKREF', this.soundRef);
let sound = this.soundRef?.getVolume();
if (sound < 1 || sound == 1) {
this.soundRef?.setVolume(sound + 0.1);
}
}
decreaseVolume(soundURL) {
console.log('CHECKREF', this.soundRef);
let sound = this.soundRef?.getVolume();
if (sound > 0 || sound == 0) {
this.soundRef?.setVolume(sound - 0.1);
}
}
pouse() {
this.soundRef?.pause();
}
}
const BgSoundPlayer = new BgSoundPlayer1();
export default BgSoundPlayer;
So what is happening right now if, when I am playing one audio track and increasing decreasing volume of track its working fine
but problem occurre's when I play second track and decrease sound of first one, it dosent works it decrease volume of second track
but when I tried to debug problem, I got to know that my this.soundRef variable is taking the refrance of latest one sound, so how can solve this

Uber eats type Horizontal ScrollSpy with scroll arrows

What i am looking for is uber eats type menu style with auto horizontal scroll if the menu categories are more then the total width that is available and When the user scroll down, the menu active links keeps changing according to the current category that being viewed.
I am using material-ui at the moment and its Appbar, Tabs and TabPanel only allow a single category items to be displayed at the same time, not all, i have to click on each category to view that category items, unlike uber eats where you can just keep scrolling down and the top menu categories indicator keeps on reflecting the current position.
I searched a lot but i didn't find any solution to my problem or even remotely related one too.
Any help, suggestion or guide will be appreciated or if there is any guide of something related to this that i have missed, link to that will be awesome.
By following this Code Sandbox
https://codesandbox.io/s/material-demo-xu80m?file=/index.js
and customizing it to my needs i did came up with my required scrolling effect by using MaterialUI.
The customized component code is:
import React from "react";
import throttle from "lodash/throttle";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import useStyles2 from "../styles/storeDetails";
import Tabs from "#material-ui/core/Tabs";
import Tab from "#material-ui/core/Tab";
import { Grid } from "#material-ui/core";
import MenuCard from "./MenuCard";
const tabHeight = 69;
const StyledTabs = withStyles({
root: {
textAlign: "left !important",
},
indicator: {
display: "flex",
justifyContent: "center",
backgroundColor: "transparent",
"& > div": {
maxWidth: 90,
width: "100%",
backgroundColor: "rgb(69, 190, 226)",
},
},
})((props) => <Tabs {...props} TabIndicatorProps={{ children: <div /> }} />);
const StyledTab = withStyles((theme) => ({
root: {
textTransform: "none",
height: tabHeight,
textAlign: "left !important",
marginLeft: -30,
marginRight: 10,
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.pxToRem(15),
[theme.breakpoints.down("sm")]: {
fontSize: theme.typography.pxToRem(13),
marginLeft: -10,
},
"&:focus": {
opacity: 1,
},
},
}))((props) => <Tab disableRipple {...props} />);
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
indicator: {
padding: theme.spacing(1),
},
demo2: {
backgroundColor: "#fff",
position: "sticky",
top: 0,
left: 0,
right: 0,
width: "100%",
},
}));
const makeUnique = (hash, unique, i = 1) => {
const uniqueHash = i === 1 ? hash : `${hash}-${i}`;
if (!unique[uniqueHash]) {
unique[uniqueHash] = true;
return uniqueHash;
}
return makeUnique(hash, unique, i + 1);
};
const textToHash = (text, unique = {}) => {
return makeUnique(
encodeURI(
text
.toLowerCase()
.replace(/=>|<| \/>|<code>|<\/code>|'/g, "")
// eslint-disable-next-line no-useless-escape
.replace(/[!##\$%\^&\*\(\)=_\+\[\]{}`~;:'"\|,\.<>\/\?\s]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
),
unique
);
};
const noop = () => {};
function useThrottledOnScroll(callback, delay) {
const throttledCallback = React.useMemo(
() => (callback ? throttle(callback, delay) : noop),
[callback, delay]
);
React.useEffect(() => {
if (throttledCallback === noop) return undefined;
window.addEventListener("scroll", throttledCallback);
return () => {
window.removeEventListener("scroll", throttledCallback);
throttledCallback.cancel();
};
}, [throttledCallback]);
}
function ScrollSpyTabs(props) {
const [activeState, setActiveState] = React.useState(null);
const { tabsInScroll } = props;
let itemsServer = tabsInScroll.map((tab) => {
const hash = textToHash(tab.name);
return {
icon: tab.icon || "",
text: tab.name,
component: tab.products,
hash: hash,
node: document.getElementById(hash),
};
});
const itemsClientRef = React.useRef([]);
React.useEffect(() => {
itemsClientRef.current = itemsServer;
}, [itemsServer]);
const clickedRef = React.useRef(false);
const unsetClickedRef = React.useRef(null);
const findActiveIndex = React.useCallback(() => {
// set default if activeState is null
if (activeState === null) setActiveState(itemsServer[0].hash);
// Don't set the active index based on scroll if a link was just clicked
if (clickedRef.current) return;
let active;
for (let i = itemsClientRef.current.length - 1; i >= 0; i -= 1) {
// No hash if we're near the top of the page
if (document.documentElement.scrollTop < 0) {
active = { hash: null };
break;
}
const item = itemsClientRef.current[i];
if (
item.node &&
item.node.offsetTop <
document.documentElement.scrollTop +
document.documentElement.clientHeight / 8 +
tabHeight
) {
active = item;
break;
}
}
if (active && activeState !== active.hash) {
setActiveState(active.hash);
}
}, [activeState, itemsServer]);
// Corresponds to 10 frames at 60 Hz
useThrottledOnScroll(itemsServer.length > 0 ? findActiveIndex : null, 166);
const handleClick = (hash) => () => {
// Used to disable findActiveIndex if the page scrolls due to a click
clickedRef.current = true;
unsetClickedRef.current = setTimeout(() => {
clickedRef.current = false;
}, 1000);
if (activeState !== hash) {
setActiveState(hash);
if (window)
window.scrollTo({
top:
document.getElementById(hash).getBoundingClientRect().top +
window.pageYOffset,
behavior: "smooth",
});
}
};
React.useEffect(
() => () => {
clearTimeout(unsetClickedRef.current);
},
[]
);
const classes = useStyles();
const classes2 = useStyles2();
return (
<>
<nav className={classes2.rootCategories}>
<StyledTabs
value={activeState ? activeState : itemsServer[0].hash}
variant="scrollable"
scrollButtons="on"
>
{itemsServer.map((item2) => (
<StyledTab
key={item2.hash}
label={item2.text}
onClick={handleClick(item2.hash)}
value={item2.hash}
/>
))}
</StyledTabs>
<div className={classes.indicator} />
</nav>
<div className={classes2.root}>
{itemsServer.map((item1, ind) => (
<>
<h3 style={{ marginTop: 30 }}>{item1.text}</h3>
<Grid
container
spacing={3}
id={item1.hash}
key={ind}
className={classes2.menuRoot}
>
{item1.component.map((product, index) => (
<Grid item xs={12} sm={6} key={index}>
<MenuCard product={product} />
</Grid>
))}
</Grid>
</>
))}
</div>
</>
);
}
export default ScrollSpyTabs;
In const { tabsInScroll } = props; I am getting an array of categories objects, which themselves having an array of products inside them.
After my customization, this is the result:

React native - is there a way to use an object from another page?

I want to know how can I use the "userPrincipalName" on other pages.
what do I need to do to make it work?
in my example, I try to use the "userPrincipalName" object on the MainScreenpage but I don't understand how to do it.
this my example of the Modal page which it has the object "userPrincipalName":
import React,
{ Component } from 'react';
import {
Text,
TouchableOpacity,
StyleSheet,
View,
} from 'react-native';
import Modal from 'react-native-modal';
import PlacesNavigator from '../navigation/PlacesNavigator';
import { LinearGradient } from 'expo-linear-gradient';
import { AzureInstance, AzureLoginView } from 'react-native-azure-ad-2';
const credentials = {
client_id: 'ea00ca9e-8c37-4520-8d80-2c2bb9239bf8',
scope: 'User.Read',
};
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
visibleModal: 3,
azureLoginObject: {},
loginSuccess: false
};
this.azureInstance = new AzureInstance(credentials);
this._onLoginSuccess = this._onLoginSuccess.bind(this);
}
_renderButton = () => (
<TouchableOpacity
onPress={() => this.setState({ visibleModal: false })}>
<LinearGradient
colors={['#4c669f', '#3b5998', '#192f6a']}
style={{
height: 80,
width: 180,
borderRadius: 10,
backgroundColor: "#2196F3",
justifyContent: 'center',
alignItems: 'center',
marginTop: 50,
}}>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold' }}>כניסה</Text>
</LinearGradient>
</TouchableOpacity>
);
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState({
loginSuccess: true,
azureLoginObject: result,
});
console.log(result);
}).catch(err => {
console.log(err);
})
}
renderWelcomeMsg = (currentTime = new Date()) => {
const currentHour = currentTime.getHours()
const splitAfternoon = 12;
const splitEvening = 17;
if (currentHour >= splitAfternoon && currentHour <= splitEvening) {
return 'צהריים טובים,';
} else if (currentHour >= splitEvening) {
return 'ערב טוב,';
}
return 'בוקר טוב,';
}
render() {
if (!this.state.loginSuccess) {
return (
<AzureLoginView
azureInstance={this.azureInstance}
onSuccess={this._onLoginSuccess}
/>)
}
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;////THIS IS THE OBJECT I WANT
return (
<View style={styles.container}>
<Modal
isVisible={this.state.visibleModal === 3}
animationInTiming={1000}
animationOutTiming={1000}
backdropTransitionInTiming={4000}
backdropTransitionOutTiming={4000}
animationIn={'flipInY'}
>
<LinearGradient
colors={['#43D4FF', 'white']}
style={{ borderRadius: 10 }}>
<View style={styles.modalContent}>
<Text style={{
fontWeight: "bold",
fontSize: 35,
justifyContent: 'center',
alignItems: 'center',
}}>{this.renderWelcomeMsg()} {givenName}
</Text>
<View style={styles.buttonContainer}>
{this._renderButton()}
</View>
</View>
</LinearGradient>
</Modal>
</View>
);
}
return (
<PlacesNavigator />
);
}
}
And this is the MainScreen page that i want to use the object "userPrincipalName" in the Axios:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
ActivityIndicator,
Platform,
FlatList,
TouchableOpacity,
TouchableHighlight,
WebView
} from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import axios from "axios";
import moment from 'moment'
import storeService from '../components/storeService'
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
userPrincipalName: null
};
}
getData = () => {
this.setState({ isLoading: true, data: [] })
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/" + userPrincipalName.split('#')[0])
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
console.log(res.data);
});
}
componentDidMount() {
this.props.navigation.setParams({ getData: this.getData });
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
console.log("THIS IS THE userPrincipalName", res) //res will contain the value given the key, store this value in your state and use it any where in the component
})
this.getData()
// this.postData()
}
renderItems = (item, index) => {
const { merhavid, yaamID, ezorID, shemEzor } = item;
return (
<TouchableHighlight style={{
backgroundColor: '#ffff78'
}}>
<TouchableOpacity
style={{
paddingVertical: 15,
paddingHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
borderWidth: 0.8,
borderColor: '#d5d7db',
backgroundColor: index % 2 == 0 ? '#d1f0da' : '#f2f5f3',
}}
onPress={() => this.props.navigation.navigate("Info")}>
<Text style={styles.name}>
{ezorID + "" + " |" + " " + merhavid + " " + yaamID + " " + shemEzor}
</Text>
</TouchableOpacity>
</TouchableHighlight>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 0, paddingTop: 300 }}>
<Text style={{ alignSelf: "center", fontWeight: "bold", fontSize: 20 }}>טוען נתונים...</Text>
<ActivityIndicator size={'large'} color={'#08cbfc'} />
</View>
);
}
return (
<>
<View style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={(_, index) => String(index)}
renderItem={({ item, index }) => { return this.renderItems(item, index) }}
/>
</View>
<View style={styles.bottomMainContainer}>
<View style={styles.bottomView} >
<Text style={styles.bottomTextStyle}>סה"כ: {this.state.data.length} רשומות</Text>
</View>
</View>
</>
);
}
}
This is what React Context was designed for:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
This also applies to updating data from nested components e.g.
const UserContext = React.createContext({
user: null,
setUser: () => {}
});
function UserContextProvider {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{props.children}
</UserContext.Provider>
)
}
function App() {
return (
<UserContextProvider>
<MainScreen />
</UserContextProvider>
);
}
class MainScreen extends Component {
static contextType = UserContext;
getData() {
// We should see the user details
console.log(this.context.user);
}
render() {
return (
<div>
<Example />
</div>
)
}
}
class Example extends Component {
static contextType = UserContext
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState(...);
// set user in UserContext
this.context.setUser(result);
});
}
}
The best way would be use redux store which helps you create immutable data object which can be update only based on user action https://redux.js.org/basics/store/.
Another simple but not efficient way is to use the react native async storage, Where you store the object and later load it up in the componentDidMount() of your new component.
Another way is to pass these props as ScreenProps in your child component(this is only possible if the screens have a parent child relation)
solution - Sharing the object between components using asnc storage
import AsyncStorage from '#react-native-community/async-storage';
const storeService = {
async saveItem(key, value) {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
},
loadKey(key) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(key)
.then(res => {
resolve(res)
})
.catch(err => reject(err));
});
}
};
export default storeService;
Note that these stateless component has 2 methods, One is to save against a key and another is to load.
Now to save a value against a key use
import React,
{ Component } from 'react';
....
export default class Example extends Component {
constructor(props) {
super(props);
....
}
....
render() {
...
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;
//this is how we will store the value when this component Example loads
storeService.saveItem('userPrincipalName', userPrincipalName)
return (
....
);
}
return (
<PlacesNavigator />
);
}
}
And to load this item again use
import React, { Component } from "react";
...
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
userPricipalName: null //initialize this state variable
data: []
};
}
getData = () => {
...
var userPrincipalName = this.state.userPrincipalName;
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/"+userPrincipalName.split('#')[0])
....
}
componentDidMount() {
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
this.setState({userPricipalName: res}) //store like this
})
this.getData()
}
...
render() {
....
return (
....
);
}
}
Note that in order to be able to save a whole object in async store you will first need to stringify the object which storing and json parse while reading it again.

React Native search function not rendering

I have two functions that are not rendering: renderTeachers() and renderSubjects(). They render based on the length of the teachers array in the state object. I console logged state.teachers and the result is as expected, the array length is more than one, but the functions still don't render. I don't understand why these functions are not rendering.
class Search extends Component {
state = {
teachers: [],
subjects: [],
rating: 3.5,
};
requestData = (queryObj) => {
console.log(queryObj);
const client = algoliasearch('__ID__', '__KEY__');
const queries = [{
indexName: 'teachers',
query: queryObj,
filters: 'Rating >= 3.5',
}, {
indexName: 'subjects',
query: queryObj,
}];
if (queryObj === '') {
this.setState({ showSearchVal: false });
} else {
client.search(queries, this.searchCallback.bind(this));
}
}
searchCallback = (err, content) => {
if (err) {
console.error(err);
return;
}
this.setState({ teachers: content.results[0].hits, subjects: content.results[1].hits });
}
renderSubjects = () => {
if (this.state.subjects.length >= 1) {
return this.state.subjects.map(subject => <SubjectDetail key={subject.objectID} subject={subject} />);
}
return null;
}
renderTeachers = () => {
console.log('in');
if (this.state.teachers.length >= 1) {
return this.state.teachers.map(teacher => <SearchDetail key={teacher.UID} person={teacher} />);
}
return null;
}
render() {
return (
<View>
<Header search onPress={() => this.searchBar.show()} />
<SearchBar
backgroundColor='#02254e'
iconColor='#4f5d6d'
placeholderTextColor='#4f5d6d'
backButton={<Icon name='keyboard-backspace' size={24} color='#4f5d6d' style={{ alignSelf: 'center' }} />}
textColor='white'
animate={false}
handleChangeText={this.requestData}
selectionColor='#01152d'
fontFamily='avenir_heavy'
backCloseSize={24}
ref={(ref) => { this.searchBar = ref; }}
/>
<View style={{ width, height, alignItems: 'center', flex: 1 }}>
<ScrollView style={{ flex: 1 }}>
<Text style={styles.topResultTextStyle}>
{this.state.subjects.length >= 1 ? 'Subjects' : ''}
</Text>
{this.renderSubjects()}
<Text style={styles.topResultTextStyle}>
{this.state.teachers.length >= 1 ? 'Teachers' : ''}
</Text>
{this.renderTeachers()}
</ScrollView>
</View>
</View>
);
}
}
export { Search };
from the code you post, I can't see a reason why teachers / subjects won't render. My question is, how do you set the teachers /subjects arrays ? without changing state / props, react component shouldn't render. Can you please share the arrays set code ?

Categories

Resources