Помогите: [TypeError: this.props.getTimerData is not a function] Почему React не понимает функцию getTimerData()?

237
25 августа 2021, 07:20

Сразу демка https://codepen.io/lubus/pen/rNBXJRB

В чем проблема:

Я хочу из компонента Timer получить данные, сколько там натикало в родительский компонент Question. Пытался забирать это через callback внутри componentWillUnmount(), чтобы не дергать каждую секунду. Сейчас уже вставил вызов this.props.getTimerData сразу в tick() - результат тот же.

В консоли вижу каждую секунду это:

---Question.getTimerData:  00:07 false
Timer.js:45 Uncaught TypeError: this.props.getTimerData is not a function
    at Timer.tick (Timer.js:45)
    at Timer.js:14

Строка "---Question.getTimerData: 00:07 false" выводиться из компонента Question, но откуда тогда ошибка?

Код:

child Timer:

import React from 'react';
export default class Timer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            min: '00',
            sec: '00',
            timeout: false,
        };
    }
    componentDidMount() {
        this.timerID = setInterval(
            () => this.tick(),
            1000
        );
    }
    componentWillUnmount() {
        clearInterval(this.timerID);
    }
    tick() {
        let min = this.state.min;
        let sec = this.state.sec;
        let timeOut = this.state.timeout;
        if (+sec+1 > 59) {
            min++;
            if (+min < 10) { min='0'+min }
            if (+min >= 20) { timeOut = true }
            sec = '00';
        } else {
            sec++;
            if (+sec < 10) { sec='0'+sec }
        }
        this.setState({
            min: min,
            sec: sec,
            timeout: timeOut
        });
        this.props.getTimerData(this.state.min+':'+this.state.sec, this.state.timeout);
    }
    render() {
        let timerClasses = 'examtimer';
        if (this.state.timeout) {
            timerClasses = 'examtimer examtimer_overrun';
        }
        return (
            <div className={timerClasses}>
                {this.state.min}:{this.state.sec}
            </div>
        );
    }
}

parent Question (упростил для наглядности):

import React from 'react';
import Timer from './Timer';
import questions from '../questions';
export default class Question extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            timerData: null,
            timeOut: false
        };
    }
    getTimerData = (timerData, timeOut) => {
        this.setState({ timerData: timerData, timeOut: timeOut});
        console.log('---Question.getTimerData: ', this.state.timerData, this.state.timeOut);
    };
    render() {
            return(
                <div className="exam__timer">
                <Timer getTimerData={this.getTimerData} />
                <div style={{'display': 'none'}}>
                    <Timer />
                </div>
            </div>
        );
    }
}

package.json:

{
  "name": "app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "classnames": "^2.2.6",
    "react": "^16.10.0",
    "react-dom": "^16.10.0",
    "react-scripts": "3.1.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
Answer 1

У вас компонент Timer используется дважды: один раз с параметром getTimerData, а второй раз - без

            <div style={{'display': 'none'}}>
                <Timer />
            </div>

Вот во втором и возникает ошибка.

Answer 2

Если пока не понятно, как именно реализовать Timer - то можно указать дефолтное значение для props через defaultProps, а также указать тип через propTypes для проверки типов. Есть два способа. Первый через static свойства внутри класса:

class Timer extends React.Component {
  static propTypes = {
    getTimerData: PropTypes.func
  }
  static defaultProps = {
    getTimerData: () => {}
  }
  // some code...
}

Второй - через свойства класса после его объявления:

class Timer extends React.Component {
  // some code...
}
Timer.propTypes = {
  getTimerData: PropTypes.func
};
Timer.defaultProps = {
  getTimerData: () => {}
};

Не обязательно указывать defaultProps и propTypes, можно указывать и что-то одно.

После того, как Вы укажете значение в defaultProps для метода getTimerData - ошибка пропадет, и если не передать в компонент пропсу getTimerData - будет использоваться дефолтное значение по-умолчанию, в данном примере это просто пустая функция () => {}, которая ничего не делает.

Для справки:

  • defaultProps
  • propTypes
  • Значения пропсов по умолчанию
  • Проверка типов с помощью PropTypes

PropTypes предоставляет ряд валидаторов, которые могут использоваться для проверки, что получаемые данные корректны. С версии React 15.5 React.PropTypes были вынесены в отдельный пакет. Необходимо использовать библиотеку prop-types для проверки типов.

READ ALSO
Как получить вчерашнюю дату на JavaScript?

Как получить вчерашнюю дату на JavaScript?

Как получить правильною (формат даты ддмм

147
как обьединить два селектора в один?

как обьединить два селектора в один?

Подскажите как обьединить два селектора:

154
Почему передается не та функция?

Почему передается не та функция?

Имеется такой кусок самописного плагина для валидации форм:

142