Огромный массив состояний с количеством входных данных, снижает производительность при изменении состояния [React-native]

#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.