#reactjs #react-native
#reactjs #react-native
Вопрос:
Я работаю над приложением, в которое пользователи будут непрерывно вводить большое количество записей. Функциональные возможности приложения завершены, но производительность низкая. Поскольку я новичок в react native, я не очень понимаю это. Но когда я погуглил по этому вопросу, было замечено, что при внесении state
изменений все приложение получает rerenders
. Итак, согласно некоторым блогам, они предложили разделить компоненты на части, то есть, parent
и child
. Я сделал это, но и производительность низкая. Также они предложили memorize
состояние и компоненты. Но я не понял, что они имели в виду.
Это был мой предыдущий код до того, как я разделил предыдущий код на компоненты, и это мой существующий код после того, как я разделил его на компоненты.
import React, { useState, useEffect, useRef } from 'react'
import { StyleSheet, View, ScrollView, Text, RefreshControl, Alert } from 'react-native'
import Background from '../components/Background'
import Header from '../components/Header'
import { Appbar, Button, DataTable, ActivityIndicator, Menu, Divider, IconButton } from 'react-native-paper'
import{ showMessage } from "react-native-flash-message";
import { theme } from '../core/theme'
import Icon from 'react-native-vector-icons/Ionicons';
import { numberValidator } from '../helpers/numberValidator'
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as constants from "../core/constants";
import axios from "axios";
import NumberInput from '../components/NumberInput'
import moment from 'moment';
const Componentinput = ({data,setListData,scrollref}) => {
const ref = React.useRef(View.prototype);
const firstref = React.useRef(View.prototype);
const [digit, setDigit] = useState({ value: '', error: '' })
const [count, setCount] = useState({ value: '', error: '' })
const onSubmitPress = async () => {
const digitError = numberValidator(digit.value)
const countError = numberValidator(count.value)
const usertoken = await AsyncStorage.getItem("@userToken")
if (digitError || countError) {
setDigit({ ...digit, error: digitError })
setCount({ ...count, error: countError })
return
}else if(digit.value.length < 3 || digit.value.length > 3){
setDigit({ ...digit, error: 'Enter 3 digits' })
return
}
let date = moment().format('YYYY-MM-DD HH:mm:ss');
axios
.post(
constants.BASE_URL 'savedata',
{
digit: digit.value,
count:count.value,
created_at:date,
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(setListData([...data, {digit:digit.value, count:count.value, countsum:'', created_at:date}]))
.then(firstref.current.focus(),scrollref.current.scrollToEnd({animated: true}))
// .then(firstref.current.focus())
.then(setDigit({ value: '', error: '' }),setCount({ value: '', error: '' }))
.then(response => {
if(response.data.status == 1){
setListData(response.data.data);
}
})
.catch(error => {
// setLoading(false)
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
}
return (
<View style={styles.fixedform}>
<View style={styles.textinputViewleft}>
<NumberInput
style={styles.textinput}
ref={firstref}
label="Digit"
returnKeyType="next"
value={digit.value}
onChangeText={(text) => { setDigit({ value: text, error: '' }); if (text.length === 3) { ref.current.focus(); } }}
error={!!digit.error}
errorText={digit.error}
keyboardType="numeric"
maxLength={3}
minLength={3}/>
</View>
<View style={styles.textinputView}>
<NumberInput
style={styles.textinput}
ref={ref}
label="Count"
value={count.value}
onChangeText={(text) => setCount({ value: text, error: '' })}
error={!!count.error}
errorText={count.error}
keyboardType="numeric"
maxLength={3}/>
</View>
<View style={styles.textinputView}>
<Button style={styles.buttonView} mode="contained" onPress={onSubmitPress} >Submit</Button>
</View>
</View>
)
}
const Dashboard = ({ navigation }) => {
const [listdata, setListData] = useState([])
const [menuvisible, setMenuVisible] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const scrollViewRef = useRef();
const [exceedCnt, setexceedCnt] = useState(null)
useEffect(() => {
async function fetchData() {
setexceedCnt(await AsyncStorage.getItem("@exceedCount"))
}
fetchData();
getListData();
}, []);
const openMenu = () => setMenuVisible(true);
const closeMenu = () => setMenuVisible(false);
const onRefresh = () => {
setRefreshing(true);
getListData();
};
const logOut = async () => {
setMenuVisible(false);
try {
await AsyncStorage.clear()
navigation.reset({
index: 0,
routes: [{ name: 'LoginScreen' }],
})
console.log('Storage successfully cleared!')
} catch (e) {
console.log('Failed to clear the async storage.')
}
}
const getListData = async () => {
const token = await AsyncStorage.getItem("@userToken")
try {
axios
.get(constants.BASE_URL "getlist?token=" token)
.then(response => {
setRefreshing(false)
setListData(response.data)
})
.catch(error => {
console.log(error);
});
} catch(error) {
console.log(error);
}
}
const deleteConfirmation = (index,id) => {
Alert.alert(
"Warning",
"Do you want to delete this record?",
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => onDeletePress(index,id) }
],
{ cancelable: true }
);
}
const onDeletePress = async (index,id) => {
const usertoken = await AsyncStorage.getItem("@userToken")
try {
axios.post(constants.BASE_URL 'deletedata',
{
id: id,
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(response => {
if(response.data.status == 1){
showMessage({
message: "Success",
description: "Data Deleted successfully",
type: "success",
color: "#fff",
icon: "success",
floating: true
});
var array = [...listdata]; // make a separate copy of the array
if (index !== -1) {
array.splice(index, 1);
setListData(response.data.data);
}
}else{
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
}
})
.catch(error => {
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
} catch(error) {
console.log(error);
}
}
const onDeleteoldrecordsPress = async () => {
setMenuVisible(false)
const usertoken = await AsyncStorage.getItem("@userToken")
try {
axios.post(constants.BASE_URL 'deletepreviousdata',
{
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(response => {
if(response.data.status == 1){
// setUploadData([]);
showMessage({
message: "Success",
description: "Data Deleted successfully",
type: "success",
color: "#fff",
icon: "success",
floating: true
});
getListData();
}else{
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
}
})
.catch(error => {
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
} catch(error) {
console.log(error);
}
}
const setexceedCount = () => {
setMenuVisible(false);
navigation.navigate('SetCountScreen');
}
const onDetailsPress = async () => {
navigation.reset({
routes: [{ name: 'DetailsScreen' }],
})
}
return (
<Background>
<Appbar style={styles.top}>
<Menu style={styles.menu}
visible={menuvisible}
onDismiss={closeMenu}
anchor={<Button onPress={openMenu}><Icon style={styles.appbariconfloat} name="ellipsis-vertical-outline" type="ionicon"/></Button>}>
<Menu.Item icon="trash-can-outline" onPress={onDeleteoldrecordsPress} title="Delete old records" />
<Divider />
<Menu.Item icon="plus" onPress={setexceedCount} title="Set Count" />
<Divider />
<Menu.Item icon="power" onPress={logOut} title="Logout" />
</Menu>
<Appbar.Content title='Add records' />
<Appbar.Action icon="table" onPress={onDetailsPress} />
</Appbar>
<Header style={styles.headermargin}></Header>
<View style={styles.datatable}>
{/* <ActivityIndicator style={styles.loadercenter} animating={loading} color="white" /> */}
<DataTable>
<DataTable.Header>
<DataTable.Title>Digit</DataTable.Title>
<DataTable.Title>Count</DataTable.Title>
<DataTable.Title>Total</DataTable.Title>
<DataTable.Title numeric>Action</DataTable.Title>
</DataTable.Header>
{listdata.length > 0 ?
<ScrollView ref={scrollViewRef} style={{marginBottom:150}} refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
{listdata.map((item, index) => {
return (
<DataTable.Row key={index}>
<DataTable.Cell > {item.digit} </DataTable.Cell>
<DataTable.Cell > {item.count} </DataTable.Cell>
<DataTable.Cell > <Text style={ (parseInt(item.countsum) > exceedCnt) ? styles.countstyleexceed : styles.countstylenotexceed}>{item.countsum}</Text> </DataTable.Cell>
<DataTable.Cell numeric>
<IconButton icon="delete" color="red" size={20} onPress={() => deleteConfirmation(index,item.id)} />
</DataTable.Cell>
</DataTable.Row>
)
})}
<DataTable.Row>
</DataTable.Row>
</ScrollView>
: console.log('no records found')}
{listdata.length == 0
?<DataTable.Row style={styles.norecords}><Text style={{color:"white"}}>No Records found</Text></DataTable.Row>
: console.log("records found")
}
</DataTable>
</View>
<Componentinput
data={listdata}
setListData={setListData}
scrollref={scrollViewRef}
/>
</Background>
)
}
export default Dashboard
const styles = StyleSheet.create({
top: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
},
headermargin: {
marginTop: 40,
},
customView: {
width: '100%',
marginTop: 37
},
appbariconfloat:{
// marginLeft: 0,
// left:0
color:"white",
fontSize:20
},
datatable:{
backgroundColor:'white',
width:'100%',
// marginBottom: 200,
// minHeight:10
},
textinputView:{
flex: 1,
marginRight: 10
},
textinputViewleft:{
flex: 1,
marginRight: 10,
marginLeft: 10
},
textinput:{
height: 55,
margin: 0,
backgroundColor:"#272727",
borderWidth:0.3,
borderColor:"#dfdfdf",
borderRadius: 4
},
buttonView:{
height: 55,
justifyContent: 'center',
marginTop: 12
},
fixedform:{
flexDirection: 'row',
width:'auto',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: '#272727',
},
datatable:{
backgroundColor:'#272727',
width:'100%',
marginBottom: 82
},
iconstyle:{
color: 'red',
},
loader:{
color:"white",
},
loadercenter:{
position:"absolute",
left:0,
right:0,
top:0,
bottom:0
},
menu:{
paddingTop: 20,
flexDirection: 'row',
// justifyContent: 'left',
left:8
},
norecords:{
marginTop:25,
},
countstyleexceed:{
color:"red",
fontWeight:"bold"
},
countstylenotexceed:{
color:'#00ff69',
fontWeight:'bold'
}
})
Комментарии:
1. Попробуйте service worker, если вам нужно обрабатывать большие данные, не уверен, чего вы достигаете с помощью своего кода (не все прочитал), но проверьте service worker, есть ли манипуляции с большим массивом / структурой данных,
2. Я думаю, вы имеете в
memoize
виду, чего вы можете достичь, используяReact.memo(Component)
. React запоминает отображаемый результат, а затем пропускает ненужный рендеринг. При принятии решения об обновлении DOM React сначала отображает ваш компонент, а затем сравнивает результат с предыдущим результатом рендеринга. Если результаты рендеринга отличаются, React обновляет DOM.