Есть ли способ использовать изменяемые массивы в FlatList?

#javascript #reactjs #react-native #react-native-flatlist

#javascript #reactjs #реагировать-родной #react-native-flatlist

Вопрос:

Я пытаюсь создать игрушечное приложение для секундомера, чтобы изучить react-native.

Я сделал систему кругов, но она становится слишком медленной, когда проходит более 15 кругов. Я думаю, что проблема с низкой производительностью заключается в laps: this.state.laps.concat([d - this.state.lapTimerStart]) том, что .concat каждый раз Lap при нажатии кнопки создается новый объект.

Я слышал, что .push это намного быстрее, чем .concat .

Поэтому я попытался использовать .push , но из .push -за того, что мутировал массив, и FlatList был чистым компонентом, поэтому он повторно отображался только тогда, когда изменились реквизиты.

Я нашел способ, но это было то же самое, что и делать .concat , потому что, по сути, это было

 let lapArr = this.state.laps;
Array.prototype.push.apply(lapArr, [d - this.state.lapTimerStart]);
this.setState({
    laps: lapArr,
})
 

Полный код

 import React, {Component} from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableHighlight,
  FlatList,
} from 'react-native';
import {useTheme} from 'react-native-paper';
import TimeFormatter from 'minutes-seconds-milliseconds';
class Stopwatch extends Component {
  constructor(props) {
    super(props);
    this.state = {
      laps: [],
      isRunning: false,
      mainTimer: null,
      lapTimer: null,
      mainTimerStart: null,
      lapTimerStart: null,
    };
  }

  handleLapReset() {
    let {isRunning, mainTimerStart, lapTimer} = this.state;
    if (mainTimerStart) {
      if (isRunning) {
        const d = new Date();
        this.setState({
          lapTimerStart: d,
          lapTimer: d - this.state.lapTimerStart   lapTimer,
          laps: this.state.laps.concat([d - this.state.lapTimerStart]),
        });
        return;
      }
      this.state.laps = [];
      this.setState({
        mainTimerStart: null,
        lapTimerStart: null,
        mainTimer: 0,
        lapTimer: 0,
      });
    }
  }
  handleStartStop() {
    let {isRunning, mainTimer, lapTimer} = this.state;
    if (isRunning) {
      clearInterval(this.interval);
      this.setState({
        isRunning: false,
      });
      return;
    }
    const d = new Date();
    this.setState({
      mainTimerStart: d,
      lapTimerStart: d,
      isRunning: true,
    });

    this.interval = setInterval(() => {
      const t = new Date();
      this.setState({
        mainTimer: t - this.state.mainTimerStart   mainTimer,
        lapTimer: t - this.state.lapTimerStart   lapTimer,
      });
    }, 10);
  }
  _renderTimers() {
    const {theme} = this.props;
    return (
      <View
        style={[
          styles.timerWrapper,
          {backgroundColor: theme.colors.background},
        ]}>
        <View style={styles.timerWrapperInner}>
          <Text style={[styles.lapTimer, {color: theme.colors.text}]}>
            {TimeFormatter(this.state.lapTimer)}
          </Text>

          <Text style={[styles.mainTimer, {color: theme.colors.text}]}>
            {TimeFormatter(this.state.mainTimer)}
          </Text>
        </View>
      </View>
    );
  }
  _renderButtons() {
    const {theme} = this.props;
    return (
      <View style={styles.buttonWrapper}>
        <TouchableHighlight
          underlayColor={theme.colors.disabled}
          onPress={this.handleLapReset.bind(this)}
          style={[styles.button, {backgroundColor: theme.colors.background}]}>
          <Text style={[styles.lapResetBtn, {color: theme.colors.text}]}>
            {this.state.mainTimerStart amp;amp; !this.state.isRunning
              ? 'Reset'
              : 'Lap'}
          </Text>
        </TouchableHighlight>
        <TouchableHighlight
          underlayColor={theme.colors.disabled}
          onPress={this.handleStartStop.bind(this)}
          style={[styles.button, {backgroundColor: theme.colors.background}]}>
          <Text
            style={[styles.startBtn, this.state.isRunning amp;amp; styles.stopBtn]}>
            {this.state.isRunning ? 'Stop' : 'Start'}
          </Text>
        </TouchableHighlight>
      </View>
    );
  }
  _renderLaps() {
    return (
      <View style={styles.lapsWrapper}>
        <FlatList
          data={this.state.laps}
          renderItem={this.renderItem}
          keyExtractor={this.keyExtractor}
        />
      </View>
    );
  }
  keyExtractor(item, index) {
    return index.toString();
  }
  renderItem({item, index}) {
    return (
      <View style={styles.lapRow}>
        <View style={styles.lapStyle}>
          <View style={styles.lapBoxStyle} />
          <Text style={styles.lapNumber}>{index   1}</Text>
        </View>
        <View style={styles.lapStyle}>
          <Text style={styles.lapTime}>{TimeFormatter(item)}</Text>
        </View>
      </View>
    );
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.top}>{this._renderTimers()}</View>
        <View style={styles.middle}>{this._renderButtons()}</View>
        <View style={styles.bottom}>{this._renderLaps()}</View>
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {flex: 1},
  timerWrapper: {
    justifyContent: 'center',
    flex: 1,
  },
  top: {
    flex: 1,
  },
  middle: {
    flex: 1,
    backgroundColor: '#F0EFF5',
  },
  bottom: {
    flex: 2,
  },
  mainTimer: {
    fontSize: 50,
    fontFamily: 'CircularStd-Medium',
    alignSelf: 'center',
  },
  lapTimer: {
    fontSize: 18,
    fontFamily: 'CircularStd-Medium',
    alignSelf: 'flex-end',
  },
  timerWrapperInner: {
    alignSelf: 'center',
  },
  buttonWrapper: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    paddingTop: 15,
    paddingBottom: 30,
  },
  button: {
    height: 80,
    width: 80,
    borderRadius: 40,
    backgroundColor: '#FFF',
    justifyContent: 'center',
    alignItems: 'center',
  },
  lapRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    height: 40,
    paddingTop: 10,
  },
  lapNumber: {
    flexDirection: 'row',
    fontSize: 16,
    fontFamily: 'CircularStd-Book',
    color: '#777',
    flex: 1,
  },
  lapTime: {
    flexDirection: 'row',
    color: '#000',
    fontSize: 20,
    fontFamily: 'CircularStd-Book',
    flex: 1,
  },
  startBtn: {
    color: '#0C0',
    fontFamily: 'CircularStd-Book',
  },
  stopBtn: {
    color: '#C00',
    fontFamily: 'CircularStd-Book',
  },
  lapsWrapper: {
    backgroundColor: '#ddd',
  },
  lapResetBtn: {
    fontFamily: 'CircularStd-Book',
  },
  lapStyle: {
    width: '40%',
    flexDirection: 'row',
  },
  lapBoxStyle: {
    flexDirection: 'row',
    flex: 1,
  },
});
export default function StopwatchScreen(props) {
  const theme = useTheme();

  return <Stopwatch {...props} theme={theme} />;
}

 
  • Я старался не использовать функции со стрелками new , многие функции, но это не очень помогло.

Комментарии:

1. Я могу попытаться повторить это, но есть несколько вопросов: 1.- Почему вы избегаете функций со стрелками?, 2.- Вы используете последнюю версию react? (или 16 ?), 3.- Если предыдущий вопрос «да», почему классы вместо функциональных компонентов?

2. Потому что функции со стрелками воссоздаются при каждом рендеринге -> дорого. На самом деле это react-native . Классы, потому что у них много состояний, и настоящий конструктор сложно реализовать в функциональном компоненте.

3. версия react-native: 0.63

Ответ №1:

FlatList это чистый компонент, и для отображения списка обязательно указывать новую ссылку data на prop. Вы должны использовать concat , но он создал новый объект.

я думаю, что основной проблемой является renderItem.

создайте отдельный PureComponent, чтобы избежать повторного преобразования элементов

 class Item extends PureComponent {
const {item,index} = this.props;
 return (
      <View style={styles.lapRow}>
        <View style={styles.lapStyle}>
          <View style={styles.lapBoxStyle} />
          <Text style={styles.lapNumber}>{index   1}</Text>
        </View>
        <View style={styles.lapStyle}>
          <Text style={styles.lapTime}>{TimeFormatter(item)}</Text>
        </View>
      </View>
    );
}
 

и использовать в элементе рендеринга

 renderItem({item, index}) {
    return (
      <Item item={item} index={index}  />
    );
  }
 

Комментарии:

1. Я понял. задержка произошла из-за повторного рендеринга всех элементов при каждом изменении массива lap, верно?