首页 > 解决方案 > Huge State Array with number of Inputs, reduce performance when state changes [React-native]

问题描述

I'm working on an app which the users will enter large number of records continuously. App functionalities are completed but the performance is slow . As i'm new to react native I've not much idea on this. But when I googled regarding this issue it has been noticed that when the state changes the whole app get rerenders. So as per some blogs they suggested to split components into pieces i.e., parent and child. I've done that but also the performance is low. Also they suggested to memorize the state and components. But I didn't understand what they meant.

This was my previous code before i split into components Previous code and this is my existing code after i splitted this into components.

    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'
  }
})

标签: reactjsreact-native

解决方案


推荐阅读