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

Я пытаюсь создать страницу с некоторыми данными, инициализированными при первом подключении, и обновлять, когда сервер веб-сокетов выдает сообщение msg, когда срабатывает определенное событие нажатия кнопки, также мне нужно запретить кнопку aka. отключить и сообщить пользователю, через сколько секунд кнопка снова станет активной.

Моя первая мысль: один компонент, обновление через состояния, присвоение счетчику состояния, затем использование setTimeout для отсчета 1 каждые 1000 мс, оказалось, что счетчик banCount работал хорошо, пока я не добавил websocket.send(), тогда он отсчитывал 2 каждый раз.

Я подумал, что это произойдет потому, что когда сервер websocket ответил, состояние меняется, поэтому весь компонент обновляется, счетчик не работает.

Итак, у меня была идея разделить его на дочерний компонент со своим собственным состоянием, но ничего не делать в жизненном цикле componentWillReceiveProps, и он не будет получать реквизиты, поэтому он будет работать только с собственным состоянием. Но результат с или без разделения счетчика на дочерний компонент, они работали одинаково.

родительский компонент:

import React from 'react';
import ReactDOM from 'react-dom';

import TestChild from './testChild/testChild';

class TestParent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     wsData: null,
   };
 }

 componentWillMount() {
   this.wsClient = new WebSocket("ws://localhost:9000/server", 'echo-protocol');
   this.wsClient.onmessage = msg => {
     if (msg) {
       this.setState({
         wsData: msg.data
       });
     }
   };
 }

 render() {
   const data = () => {
     if (this.state.wsData) {
       return this.state.wsData;
     } else {
       return "waiting data";
     }
   };
   return (
     <div>
       <div>{data()}</div>
       <TestChild wsClient={this.wsClient}/>
     </div>
   );
 }    
}

ReactDOM.render(
   <TestParent />,
   document.getElementById('reactWrapper')
);

и дочерний компонент:

import React from 'react';

class TestChild extends React.Component {
  constructor(props) {
    super(props);
    this.count = null;
    this.state = {
      banCount: this.count
    };
    this.wsClient = this.props.wsClient;
    this.countupdate = 0;
  }

  banCount() {
    this.setState({
      banCount: this.count
    });
  }

  callNext(n) {
    this.wsClient.send('can you hear me');
    this.count = n;
    this.banCount();
  }

  componentDidUpdate() {
    if (this.count > 0) {
      setTimeout(() => {
        this.count -= 1;
        this.banCount();
      }, 1000);
    } else if (this.count === 0) {
      this.count = null;
      this.banCount();
    }
  }

  render() {
    return <button onClick={() => this.callNext(3)}>click me {this.state.banCount}</button>;
  }    
}

export default TestChild;

Пожалуйста, не обращайте внимания на то, работает ли соединение между сервером и веб-сокетом, они в порядке.

Я не знаю почему, я даже не обновил дочерний компонент, я новичок в React, я действительно не знаю, как отлаживать это, я читаю этот код часами, но он слишком сложен для меня.

Почему он отсчитывал 2 каждый раз? и наверняка я не прав, каков правильный путь.

Пожалуйста, помогите мне только с React и ванильным Javascript, я не использовал Redux или Flux и даже не знал, что это такое, спасибо.

2 ответа

Решение

Наконец я решил это.

Это связано с тем, что React будет перерисовывать все дочерние компоненты с установкой новых состояний детей или без них. Единственный способ остановить его от повторного рендеринга - это использовать ShouldComponentUpdate, поэтому:

shouldComponentUpdate() {
  return this.state.banCount !== null;
}

будет работать, как когда дочерний компонент получает реквизиты после websocket.send(), this.count по-прежнему равен нулю, но сразу после websocket.send () this.count имеет значение 3, так что дочерний компонент будет обновляться с тех пор.

Также еще одно рабочее место:

callNext(n) {
  this.wsClient.send('can you hear me');
  this.count = n;
}

componentWillReceiveProps(nextProps) {
  this.data = nextProps.datas;
  this.setState({
    banCount: this.count
  });
}

в этом рабочем окружении, без shouldComponentUpdate(), дочерний компонент всегда будет перерисовываться, когда его родитель получает данные websocket, поэтому в функции обработчика щелчков остановите вызов bancount(), чтобы он не обновлялся сам, а устанавливал состояние при получении nextProps, что вызовет повторную визуализацию.

Подведем итог всему вышесказанному:

дочерний компонент всегда будет повторно визуализироваться с или без установки состояния с помощью новых реквизитов, если не должен возвращать falseComponentUpdate false, я уже вызвал bancount () в функции обработчика щелчка, запускает дочерний компонент для обновления самого состояния, но после того, как родительский компонент получает данные веб-сокета, это снова вызвало обновление состояния, поэтому оно запускается дважды.

Это НЕ проверенный код, но он должен помочь вам создать то, что вы хотите. Я не проверял ваш компонент, но подозреваю, что ваш setTimeout() вызывается несколько раз.

import React from 'react';

class TestChild extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: null,
    };
  }

  startCountDown() {
    var newCount = this.state.count -1;
    if(newCount === 0){
       clearTimeout(this.timer);
    }
    this.setState({
      count: newCount,
    });

  }

  callNext(n) {
    this.wsClient.send('can you hear me');
    this.setState({
      count: n,
    });
    this.timer = setTimeout(() => {
      startCountDown();
    }, 1000);
  }
  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  render() {
    return <button disabled={this.state.count>0} onClick={() => 
       this.callNext(3)}>click me {this.state.count}</button>;
  }    
}

export default TestChild;
Другие вопросы по тегам