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;