我正在尝试构建一个页面,其中一些数据在首次安装时已初始化,并在websocket服务器在触发某个按钮点击事件时给出响应消息时进行更新,同时我还需要禁用该按钮。禁用,并告诉用户再次点击按钮的秒数。
我的第一个想法是,单个组件,通过状态更新,给计数器一个状态,然后使用setTimeout每1000ms倒数1,结果是计数器“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;
请忽略'服务器和websocket连接是否有效,它们没问题。
我不知道为什么,我甚至没有更新Child组件,我是React的新手,我真的不知道如何调试这个,我读了几个小时的代码,但对我来说太复杂了
为什么每次都倒数2?而且我肯定是错的,这是正确的方法。
请帮我使用React和vanilla Javascript,我没有使用Redux或Flux,甚至不知道它们是什么,谢谢。
答案 0 :(得分:0)
这不是经过测试的代码,但是应该可以帮助你构建你想要的东西,我没有测试你的组件,但我怀疑你的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;
答案 1 :(得分:0)
最后我把它解决了。
这是因为React将重新渲染所有子组件,无论是否设置子级的新状态。阻止它重新渲染的唯一方法是使用ShouldComponentUpdate,所以:
shouldComponentUpdate() {
return this.state.banCount !== null;
}
将起作用,因为当子组件在websocket.send()之后接收道具时,this.count仍为null,但在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
});
}
在这个workround中,如果没有shouldComponentUpdate(),子组件将在其父组件接收websocket数据时始终重新呈现,因此在单击处理函数中,停止调用bancount(),因此它不会自行更新,而是设置状态当接收到nextProps时,将触发重新渲染。
总结以上所有:
子组件将始终使用或不通过新的道具设置状态重新渲染,除非shouldComponentUpdate返回false,我在click处理函数中称为bancount(),触发子组件更新状态本身,但在父组件接收之后websocket数据,它再次触发状态更新,这就是它运行两次的原因。