您的位置:首页 > Web前端 > React

React编写聊天室(1)

2016-01-11 17:15 816 查看
由于angular性能的问题,转投react的怀抱,学了半个月有了,写了一个聊天室的demo。

源码https://github.com/zezhipeng/react-socket.io-chat

例子的地址是 http://120.26.109.186:3000/?name=zezhipeng&&room=1

name 与room 分别为用户名和房间号,可以随意修改。

在同一个房间内可以相互聊天。

建议用手机打开,或者chrome用手机模式打开。

需要用到:

express

react

webpack

jade

bootstrap

mongoose

socket.io

和一堆中间件和包

首先关于webpack,就是一个web客户端的包管理工具。比较实际的感受就是,可以在js里面写require了(配合npm),或者可以说可以ES6来写前端了,还是蛮好用的。

我的webpack.config是

module.exports={
entry:"./public/js/app.jsx",
output:{
path:"./public/js",
filename:"bundle.js"
},
resolve:{
extensions: ['', '.js', '.jsx']
},
module:{
loaders:[
{
test: /\.jsx$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
}
}


然后执行

webpack -d --watch


就可以安心改代码了。如果你没用过webpack,网上有很多关于这个的资料,youtube上也有比较好的教程,由于篇幅问题我就不多说什么了。

最终效果图如下



从图上我们可以看到,我把app分成了3个子模块,

MsgForm //发送文字表单

MsgContent //信息展示模块

MsgTool //聊天模块的一些插件

首先关于父级组件app.jsx

var React = require("react");
var ReactDOM = require("react-dom");
var socket = io();
var marked = require('marked');
var MsgContent = require("./msgcontent.jsx");
var MsgTool = require("./msgtool.jsx");
var MsgForm = require("./msgform.jsx");
var ChatApp = React.createClass({
getInitialState: function () {
return {messages: [], system: [], scrollSwitch: true}
},
componentDidMount: function () {
socket.on("join", this.handleJoin);
socket.on("msg", this.handleMsgRce)
},

handleJoin: function (msg) {
console.log(msg);
var self = this;
var {system}=this.state;
system.push(msg);
this.setState({msg});
setTimeout(function () {
system.pop();
self.setState({system});
}, 5000)
},
handleMsgRce: function (msg) {
console.log(this.state);
var {messages}=this.state;
messages.push(msg)
this.setState({messages});
},
socketEmitMsg: function (msg) {
socket.emit("msg", msg)
},
switchCallback: function (v) {
this.setState({scrollSwitch: v})
},
render: function () {
return (
<div className="col-md-12 panel chat">
<div className="panel-header">
<MsgTool switchCallback={this.switchCallback}/>
</div>
<div className="panel-body">
<MsgContent messages={this.state.messages} system={this.state.system}
scrollSwitch={this.state.scrollSwitch}/>
</div>
<div className="panel-footer">
<MsgForm user={this.props.user}  socketEmitMsg={this.socketEmitMsg}/>
</div>
</div>
)

}
});

ReactDOM.render(<ChatApp user={user}/>, document.getElementById("content"))


初始化的时候,我定义了3个状态
{messages: [], system: [], scrollS
4000
witch: true}
messages即是接收到的消息的对象集合,system是系统消息的集合,而scrollSwitch是滚屏插件的对象(传递到MsgTool)的。

这里还要讲一下的是,
user={user}
里面的user是一个全局变量,通过express+jade直接渲染给前端的。这里有些人可能不会,我想做的就是后端通过JSON.stringify(user),将后端保存的用户信息转为string类型,前端的jade通过非转义的方式
user=!{user}
就可以直接调用用户的信息

关于MsgForm模块,

var React = require("react");
var EmojiList = require("./emojilist.jsx")

module.exports = React.createClass({
getInitialState: function () {
return ({text: "", disabled: false, count: 3,hide:true})
},
//把信息传递到父组件
handleSubmit: function (e) {
e.preventDefault();
if (this.state.text) {
var msg = {text: this.state.text, user: this.props.user};
this.props.socketEmitMsg(msg)
this.setState({text: "", disabled: true,hide:true})
}
},
handleInputMsg: function (e) {
this.setState({text: e.target.value})
},
componentDidMount: function () {
//发送间隔
this.interval = setInterval(this.tick, 1000);
var self = this
//把表情的string值传递到输入框中
$(document).ready(function(){
$(".theEmoji").click(function(){
var v = $(this).attr("value")
self.setState({text:self.state.text+v})
})
})
},

tick: function () {
if (this.state.count > 0 && this.state.disabled == true) {
this.setState({count: this.state.count - 1})
}
else {
this.setState({disabled: false, count: 3})
}
},
//显示表情列表
handleClick:function(){
this.setState({hide:!this.state.hide})
},
submitEmoji:function(v){
this.state.text +=v
},
render: function () {
return (
<form style={{"position":"relative"}}>
<EmojiList hide={this.state.hide} submitEmoji={this.submitEmoji} />
<div className="input-group sendMsgForm">
<span className="input-group-addon">
<span className="fa fa-smile-o feedback" onClick={this.handleClick}></span>
</span>
<input className="form-control" onChange={this.handleInputMsg} value={this.state.text} maxLength="50"/>
<span className="input-group-addon">
<button className="btn btn-danger" onClick={this.handleSubmit}
disabled={this.state.disabled}>{this.state.disabled?this.state.count:"发送"}
</button>
</span>
</div>
</form>
)
}
});


从代码中可以看到,最外层用了bootstrap的input-group类,里面包裹了3个div,表情列表呼出按钮,发送文字表单和发送文字按钮.。

利用componentDidMount函数,我设置了一个定时器,检查this.state.count是否大于0并且为禁用状态,如果是的话,计数减1

不是的话,设置为非禁用,初始话计数为3。这样,每次发送一条消息后会有4秒的禁言时间,防止刷屏。

这里的socketEmitMsg属性是从父组件app.jsx传递过来的一个方法,就是一个简单的socket.emit方法

socketEmitMsg: function (msg) {
socket.emit("msg", msg)
},


关于表情emojilist.jsx

var React = require("react");
var emojione = require("emojione");

var EmojiList = React.createClass({
getInitialState:function(){
return ({
emoji:[":grinning:",":grimacing:",":grin:",":joy:",":smiley:",":sweat_smile:",
":innocent:",":wink:",":blush:",":slight_smile:",":yum:",":heart_eyes:",
":kissing_heart:",":stuck_out_tongue_closed_eyes:",":money_mouth:",":nerd:",
":sunglasses:",":hugging:",":thinking:",":flushed:",":disappointed:",":rage:",
":confused:",":persevere:",":tired_face:",":triumph:",":open_mouth:",":scream:",
":cold_sweat:",":cry::disappointed_relieved:",":sweat::sob:",":head_bandage:",":poop:",
":spy::tongue:",":pray::muscle:",":clap::v:",":ok_hand:",":point_up:",":middle_finger:",
":metal::vulcan:"]
})
},

render:function(){

var emojiList = this.state.emoji.map(function(v,n){
var createMarkup = function(){
return {__html:emojione.shortnameToImage(v).toString()}
}
return <li key={n} dangerouslySetInnerHTML={createMarkup()} className="theEmoji" value={v}></li>
})
return (<ul className={this.props.hide?"hide":"show"+" emoji "}>
{emojiList}
</ul>)
}
});
module.exports=EmojiList


这里表情库是从github上找来的一个免费的,有兴趣的可以上他们官网http://emojione.com/ 看看。

这里要注意的是,我们转换过来的html代码加入以{emojione.shortnameToImage(v)}这样的形式显示的话,react会直接将他显示为文本,先要转换为string类型后,通过dangerouslySetInnerHTML来显示。

未完待续。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息