2017-08-16 71 views
0

我正在尝试构建一个页面,其中一些数据在初次装载时初始化,并且当websocket服务器在某些按钮单击事件时发出响应消息时进行更新被触发,我也需要禁止按钮aka。禁用,并告诉用户该按钮可以再次点击多少秒。React - 方法通过状态运行正确时间,但在父组件更改状态时运行双倍

我的第一个想法是,单一组件,通过状态更新,给计数器一个状态,然后使用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

最后我解决了它。

这是因为React会重新渲染所有的子组件,有或没有设置孩子的新状态。只有这样,才能从重新渲染停止它是使用ShouldComponentUpdate,所以:

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

将工作,因为当)的子组件接收websocket.send(后道具,this.count仍然是空的,但正确在websocket.send()之后,this.count被设置为3,所以子组件将自更新。

也是另一个workround:

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重新渲染,我alreay叫bancount()在click处理函数,触发器子组件更新状态本身,但在父组件接收到websocket数据之后,它会再次触发状态更新,这就是为什么它会运行两次。

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; 
+0

感谢您的帮助,但您的子组件从3到2计数,然后停止。 –