setTimeout(function(){
window.dispatchEvent(new Event('resize'))
},0)
问:为什么这个项目需要redux:
做项目的时候我通过不停总结,总算想通了:
每次在页面发生跳转的时候,为了确保在下一次渲染正确的页面(每个发生跳转的页面内部都获取了redux的state,通过redirect字段来判断跳转),就必须更改redux的数据。
还有其他的内容渲染也依赖最新redux数据!!所以你必须时时刻刻及时改变redux的数据!
一、项目学习过程中的几点总结:
1、登录注册的关键点 :
redux里建立登录注册两个方法,通过该方法比较自身state和后台请求的数据是否相同(在组件内部调用该方法,参数是组件自己的sate),如验证通过,改变redux的state。
比如:genius/boss页面,底部icon是渲染boss还是genius,就是取决于state里的type字段。
2、请求数据的组件:
登录、注册(提交后台用户名、密码、身份)、完善信息(提交后台头像,描述等等)
3、cookie :
添加登录成功,后台设置cookie保存登录状态;2、写cookie是在登录后,读cookie在用户请求时,先读一下是否有cookie,所以刷新页面会停留在上次的页面,正是因为cookie设置的原因**。
4、关于Ant Design:
该组件库没有实现和react-router-dom的切换路由的良好配合,所以平时我们是通过Link标签切换路由,而引用这个组件库的navbar时直接用this.props.history里push吧!
二、页面逻辑讲解:
页面跳转 :
为什么每次跳转都正确的渲染了页面?
redux的state设置了redirect,传入一个跳转方法。
详解过程:在请求数据后,会在action里传入请求到的data,在redux的state里设置redirect(跳转字段),该字段接收一个页面跳转方法,参数为请求到的数据,该跳转方法的内部逻辑就是解耦请求到的data,获取type,通过该字段决定跳转到对应的页面(boss/genius,bossinfo/geniusinfo)。
发生跳转的几个组件:
- 登录:完成提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
- 注册:提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
- 完善信息:提交了AUTH_SUCESS,改变了state的redirect,发生了跳转
共同点:改变state,为redirectTo的绑定跳转函数,组件内部设置Redirect重定向组件,所以:正确的渲染了对应的页面。
case AUTH_SUCESS:
return {...state,msg:'',redirectTo:getRedirectPath(action.data),...action.data,pwd:''}
export function getRedirectPath({type, avatar}){
// 根据用户信息 返回跳转地址
// user.type /boss /genius
// user.avatar /bossinfo /geniusinfo
console.log(type)
let url = (type==='boss')?'/boss': '/genius'
if (!avatar) {
url += 'info'
}
return url
}
要在各组件内放置:
{this.props.redirectTo?<Redirect to="this.props.redirectTo"/>:null}
登录:
要做的事 :
- 请求数据
- 将请求到的数据合并到redux的state
- 跳转页面。
组件内代码讲解:设置自己的state(字段:user,pwd),在表单控件发生变化的同时setstate,点击登录按钮时传入redux的校验方法:login,用来判断用户密码是否正确。
代码:
1、redux内:
actionType:AUTH_SUCESS
actionCreator:
authSucess(return {type:AUTH_SUCESS,data:data})
export function login(data) {
return dispatch=>{
axios.post('./user/login',data).
then(res=>{
//请求成功,提交成功actionCreator:actionCreator
if(res.status === 200 && res.data.code===0){
dispatch(actionCreator(res.data.data))
}
// 请求失败,提交失败actionCreator:errorMsg
else{
dispatch(errorMsg(res.data,msg))
}
})
}
}
function reducer(state,action){
switch(action.type){
case AUTH_SUCESS:
return {...state,msg:'',redirect:getRedirectPath(action.data),...action.data}
default:
return state
}
}
2、请求通过之后跳转页面。
二、注册:
1、逻辑和登录差不多,就不详细说了。(提交后台注册信息、跳转页面)
三、完善信息页面
两个组件 : geniusinfo/bossinfo
流程:
用户添加头像和简介等相关信息(设置组件内部的state,设置对应字段) => 点击保存 => 请求接口、post传递数据(提交对应的action,传入请求到的值) => 更改redux(这一步是确保点击按钮后渲染到正确组件的关键!再次说明:跳转到哪是通过redirectTo决定的!!!)
//组件内部
this.state = {
title : '',
company : '',
money : '',
desc : ''
}
<Button onClick={ ()=>{ this.props.update(this.state) } }
>保存</Button>
// redux
// 账号补全信息补全信息
export function update(state) {
return dispatch=>{
axios.post('/user/update',state).
then(res=>{
if(res.status === 200 && res.data.code===0){
// 后台返回的信息:user和用于跳转的type
dispatch(authSucess(res.data.data))
console.log(res.data.data)
}else{
dispatch(errorMsg(res.data.msg))
}
})
}
}
// reducer
case AUTH_SUCESS:
return {...state,msg:'',redirectTo:getRedirectPath(action.data),...action.data,pwd:''}
四、头像选择组件:
要给父组件bossinfo/geniusinfo传入选择的是哪一个图片,方便父组件记录到redux里面。
其实是父组件想从子组件中拿到数据,那么也是一样,在父组件设置自定义属性,写一个方法,子组件通过props接收该方法并传入具体参数即可
// 子组件:获取props
<Grid
onClick={elm=>{
this.props.selectAvator(elm.text)
}}
/>
// 父组件传递
<AvatarSelector
selectAvator={(imgname)=>{
this.setState({
avatar : imgname
})
}}
></AvatarSelector>
boss组件
请求接口,获取genius数据后渲染到页面,请求到的数据都统一放到组件自身的state里,方便处理。然后将state的数据进行循环渲染即可!
使用redux管理牛人列表
因为BOSS和求职的卡片信息是一样的,所以我们可以将其封装成组件方便使用。
将数据放到redux的步骤:
// 1
const USER_LIST = 'USER_LIST'
const initState = {
userList : []
}
// 3
export function chatuser(state=initState,action){
switch(action.type){
case USER_LIST :
// 这个状态随时可能加其他的数据,所以需要先展开
return {...state,userList:action.payload}
default:
return state
}
}
// 2
export function userList(data){
return {type:USER_LIST,payload:data}
}
// 4
export function getUserList(type){
return dispatch=>{
axios.get('/user/list?type='+type).
then(res=>{
if(res.data.code==0){
dispatch(userList(res.data.data))
}
})
}
}
boss列表和组件优化
boss和gennius的用户展示是一样的,所以将用户展示这部分单独抽离成一个组件:usercard。
个人中心信息展示
我们之前已经把用户所有的数据,通过get请求'/user/info'接口,将获取的数据都保存在了redux的state中,在redux的调试工具中即可查看到。所以用户中心这个页面是非常好做的,直接从redux获取数据,展示出来,加上需要的按钮即可。
在个人中心里退出登录
关键是清除cookie。npm下载browserCookie,并在页面中引入。
1、逻辑:点击退出登录按钮清除cookie
logout(){
console.log('logout')
browserCookies.erase('userid')
}
2、具体实现
使用Modal组件,点击时调用以下代码
alert('注销', '确认退出登录吗', [
{ text: '取消', onPress: () => console.log('cancel') },
{ text: '确认', onPress: () => {
browserCookies.erase('userid')
window.location.href = window.location.href
}},
])
现在是清空cookie,刷新页面实现了跳转。
这样的体验不好。
所以我们在注销时清空redux数据这个方法较好。
// 退出登录 :把初始信息放入
case LOGOUT :
return {...initState,redirectTo:'/login'}
Socket.io
屏幕快照 2018-07-16 上午6.47.29.png屏幕快照 2018-07-16 上午6.49.33.png
socket.io前后端联通,消息发送流程梳理
注:socket是当前的请求,io是全局的
流程:
1、后端io.on建立connection链接
2、前端触发 socket.emit,方法:sendmsg,携带数据,发起成功之后,后端就可以收到消息了。
3、后端socket.on监听前端发送的消息sendmsg,在方法体内发送到全局接收io.emit('recvmsg',data)
4、前端在componentDidMount中socket.on监听recvmsg,获取data,将数据更新到组件中
1、后端express : 引入socket.io,和express关联
const app = express()
//work with express
Const server = require('http').Server(app)
const io = require('socket.io')(server)
// 参数:socket,当前的链接;io,全局链接
io.on('connection',function(socket){
console.log('user login')
// 监听前端发送的消息,方法就是前端的方法
socket.on('sendmsg',function(data){
const {form , to , msg} = data
const chatid = [form,to].sort().join('_')
// data是前端发送过来的消息
console.log(data)
// 发送到全局,每个人都是接受的状态
//io.emit('recvmsg',data)
})
})
server.listen(9093,function(){
console.log('Node app start at port 9093')
})
2、前端
发起链接,在conponentMount中,也可以放在外面直接引入;
发起成功之后,后端就可以收到消息了。
// socket客户端
import io from 'socket.io-client'
// 因为跨域了,所以需要写明链接的地址
const socket = io('ws://localhost:9093')
// 消息发送到后端
handelSubmit(){
//发送state到后端
socket.emit('sendmsg',{text:this.state.text})
}
// 监听sendmsg事件,监控消息
socket.on('recvmsg',function(data)){
console.log(data)
}
3、聊天信息展示,在componentDidMount中监听
componentDidMount(){
// socket.监听recvmsg,接收消息
// socket.on('recvmsg',data=>{
this.setState({
msg:[...this.state.msg,data.text]
})
})
// 加载消息列表
this.props.getMsgList()
}
聊天页面redux链接 : chat.redux.js
1、state设置chatmsg消息列表和unread未读信息字段;
2、进入聊天页面,axios获取消息列表,并将信息并入state。
!!!用户识别:使用用户的ID来识别用户
1、发送方 : user中获取id
2、接收方:在usercard组件中,在点击用户头像时,在地址栏中加入用户ID
this.props.history.push(`/chat/${v._id}`)
1.点击发送按钮,传给后台的字段 : from,to,msg
2.组件即将挂载时recvMsg,渲染对话
注意:后端返回的是全部消息,分组需要前后端协商将两个id拼接进行校验!
点击提交,将from,to,msg传给后端,后端接收到之后将消息广播到全局。通过地址栏获取的id(to)和redux的to比较,如果相同,就说明是接收方,然后将这条消息显示在左边,否则就是自己的消息,显示在右边。通过对比chatId,就可以定位到这两个人的消息,避免消息错乱。
消息列表:消息分组,将chatmsg(返回的是全部的消息)的数据根据chaid分组,这样就得到了对话分组。
chatList=this.props.chat.chatmsg.filter( v=>v.chatid===chatid )
未读消息数量:chatList(根据v.chatid分组)中 : to = 当前登录用户,说明消息是本人发送
未读消息清零: 进入聊天页面,向后端发送请求,让后端标识一下,将read字段标识为true.
后端返回from(对方),to(当前登录的userid)
网友评论