Reactjs, как отобразить часы без утечки памяти - PullRequest
0 голосов
/ 30 октября 2019

Я создал простые часы в Reactjs (16.11.0), которые сообщают время и обновляют каждую секунду. См. Код ниже.

import React, {Component} from 'react';
import './clock.css'

class Clock extends Component {

    constructor(props, context) {
        super(props, context);
        this.state = {
            date: new Date()
        };
    }

    componentDidMount() {
        this.interval = setInterval(() => this.setState({date: new Date()}), 1000);
    }

    componentWillUnmount() {
        clearInterval(this.interval)
    }

    render() {
        let hour = ("0" + (this.state.date.getHours())).slice(-2);
        let minute = ("0" + (this.state.date.getMinutes())).slice(-2);
        let day = this.state.date.getDate();
        let month = this.state.date.toLocaleString('nb', {month: 'long'});
        return (
            <section className={'clock'}>
                <div>{hour + ':' + minute}</div>
                <div className={'date'}>{day + '. ' + month}</div>
            </section>
        );
    }
}

export default Clock;

При мониторинге диспетчера задач в Chrome я вижу, как со временем увеличивается объем памяти вкладок. Для сайта, который должен работать в «киоске», это не нужно. Мой Rasperrypi исчерпал память через пару часов.

Мой пример приложения основан на чистом 'create-реагировать-приложение' с добавленным этим компонентом Clock.

Есть ли что-нибудь, что я могусделать, чтобы предотвратить эту утечку памяти?

Обновление

Утечка памяти, кажется, происходит только при использовании хрома (и частично FireFox) на raspberrypi и уменьшается по некоторым причинам при использованииэтот флаг "--disable-gpu-program-cache"

Ответы [ 2 ]

1 голос
/ 30 октября 2019

В вашем приложении нет ничего, что могло бы вызвать утечку памяти. Я также создал пример в codesandbox с вашим приложением, которое устанавливает состояние каждую миллисекунду, и использование памяти вкладкой с приложением со временем не увеличивается (я проверил через диспетчер задач Chrome). Так что утечка, вероятно, вызвана чем-то другим.

0 голосов
/ 31 октября 2019

Я бы рекомендовал избегать установки состояния каждые 1 мс, и вместо этого вы можете упростить пример часов, используя moment (или moment-timezone ) и простой пользовательский хук:

Рабочий пример:

Edit Simple Clock Example


index.js

import React, { Fragment } from "react";
import { render } from "react-dom";
import { Container, CurrentDate, DisplayTime, Title } from "./components";
import { useTimer } from "./hooks";

const timezone = "Europe/Oslo";
const timeformat = "HH:mm:ss";
const dateformat = "DD.MMMM.YYYY";

const Clock = () => {
  const { currentDate, currentTime } = useTimer(
    timezone,
    timeformat,
    dateformat
  );

  return (
    <Fragment>
      <Title>Simple Clock Example</Title>
      <Container>
        <CurrentDate>{currentDate}</CurrentDate>
        <DisplayTime>{currentTime}</DisplayTime>
      </Container>
    </Fragment>
  );
};

render(<Clock />, document.getElementById("root"));

hooks / useTimer / index.js

import PropTypes from "prop-types";
import moment from "moment-timezone";
import { useCallback, useEffect, useRef, useState } from "react";

const useTimer = (timezone, timeformat, dateformat) => {
  // reuseable function to return current time
  const getCurrentTime = useCallback(
    format =>
      moment()
        .tz(timezone)
        .format(format),
    [timezone]
  );

  // utilize a ref to set and clear an interval
  const intervalRef = useRef();

  // utilize state to initialize and update "currentTime"
  const [currentTime, setTime] = useState(getCurrentTime(timeformat));

  // create a 1000ms setInterval timer to update "currentTime".
  const startTimer = useCallback(() => {
    intervalRef.current = setInterval(() => {
      setTime(getCurrentTime(timeformat));
    }, 1000);
  }, [getCurrentTime, intervalRef, timeformat]);

  // during initial load start the timer
  // during unmount remove the interval
  useEffect(() => {
    if (!intervalRef.current) {
      startTimer();
    }

    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = undefined;
      }
    };
  }, [intervalRef, startTimer]);

  return {
    currentDate: getCurrentTime(dateformat),
    currentTime
  };
};

useTimer.propTypes = {
  dateformat: PropTypes.string.isRequired,
  format: PropTypes.string.isRequired,
  timezone: PropTypes.string.isRequired
};

export default useTimer;
...