GVKun编程网logo

reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)(react-websocket)

15

本文的目的是介绍reactjs–React,Redux和Websocket的最佳实践(保持一个类来处理请求)的详细情况,特别关注react-websocket的相关信息。我们将通过专业的研究、有关数据

本文的目的是介绍reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)的详细情况,特别关注react-websocket的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)的机会,同时也不会遗漏关于react+react-router+redux+Node.js+socket.io写一个聊天webapp、react+react-router+redux+react-redux构建一个简单应用、reactjs – React Redux Axios:POST请求没有从redux状态接收凭据、reactjs – React,Redux,React-Router?的知识。

本文目录一览:

reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)(react-websocket)

reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)(react-websocket)

这个问题与React,Redux和Websocket(socket.io)有关.

在这种方法中,我想实时更新我的​​UI.我在每个要更新的文件中的每个componentDidMount中打开单独的套接字.

(例如:如果我有更新的通知和统计信息,我将打开包含通知和统计信息的2个套接字)

我的一位同事建议我,不要打开到每个端点的多个套接字连接,而是在主api-service文件中打开一个套接字,并触发对响应的每个密钥的操作.

实现此任务的最佳实践是什么,如果有任何演示,请帮助我解决其中一个问题.

谢谢!

Redux应用程序中类似套接字连接的标准位置是中间件.中间件可以监听调度的操作,告诉它订阅和取消订阅,还可以根据收到的消息调度Redux操作.

我的Redux addons catalog列出了各种各样的existing socket-related middleware.

react+react-router+redux+Node.js+socket.io写一个聊天webapp

react+react-router+redux+Node.js+socket.io写一个聊天webapp

一、项目预览

之前看一个写聊天器的教程,自己也跟着教程做了一遍,由于懒得去找图片和一些图标我就用教程中的素材来做,主要是用了react+react-router+redux+Node.js+socket.io的技术栈,接下来就是项目的预览

1.首先在/login下能看到有登录和注册按钮

登录注册页

2.点击注册按钮,路由跳到/register,注册一个账号,用户和密码都为LHH,选择“牛人”,点击注册,之后路由会跳到/geniusinfo,即牛人完善信息页,选择一个头像并完善信息后点击保存按钮

注册页
完善信息页

3.可以看到已经进入有三个tab选项的内容页面了,点击“我”,路由跳转到/me即可看到个人中心内容,但此时boss和消息的tab页仍没有内容,可以按照之前步骤注册一个Boss账号,只需在注册的时候选择Boss选项

个人中心

4.现在在LHH和LCE账号分别能看到的列表

列表

5.点击进入聊天室,输入内容

聊天室

二、接下来对项目的主要内容进行解释

1.项目的除掉node_modules后的目录
├─build
│  └─static
│      ├─css
│      └─js
├─config
│  └─jest
├─public
├─scripts
├─server
└─src
    ├─component
    │  ├─authroute
    │  ├─avatar-selector
    │  ├─boss
    │  ├─chat
    │  ├─dashboard
    │  ├─genius
    │  ├─img
    │  ├─logo
    │  ├─msg
    │  ├─navlink
    │  │  └─img
    │  ├─user
    │  └─usercard
    ├─container
    │  ├─bossinfo
    │  ├─geniusinfo
    │  ├─login
    │  └─register
    └─redux

其中build文件夹的内容为npm run build打包后的内容,在项目中如果启用后端接口也可访问

2.入口页面
import React from ''react'';
import ReactDOM from ''react-dom'';
import { createStore, applyMiddleware, compose } from ''redux'';
import thunk from ''redux-thunk'';
import { Provider } from ''react-redux'';
// eslint-disable-next-line
import { BrowserRouter } from ''react-router-dom'';
import App from ''./app''

import reducers from ''./reducer''
import ''./config''
import ''./index.css''

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f=>f
))

// boss genius me msg 4个页面
ReactDOM.render(
    (<Provider store={store}>
        <BrowserRouter>
            <App></App>
        </BrowserRouter>
    </Provider> ),
    document.getElementById(''root'')
 )

使用react-redux的Provider,可实现全局的状态存储,子组件可通过props获得存储在全局的状态

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f=>f
))

上面代码的主要作用是关于配置浏览器的redux插件的,可以通过这个插件在控制台中查看state中的数据。
来看下app.js中的代码

import React from ''react''
import Login from ''./container/login/login.js'';
import Register from ''./container/register/register.js'';
import AuthRoute from ''./component/authroute/authroute.js'';
import BossInfo from ''./container/bossinfo/bossinfo.js'';
import Geniusinfo from ''./container/geniusinfo/geniusinfo'';
import Dashboard from ''./component/dashboard/dashboard'';
import Chat from ''./component/chat/chat''
import {  Route,  Switch } from ''react-router-dom'';

class App extends React.Component{
    render() {
        return (
            <div>
                <AuthRoute></AuthRoute>
                <Switch>
                    <Route path=''/bossinfo'' component={BossInfo}></Route>
                    <Route path=''/geniusinfo'' component={Geniusinfo}></Route>
                    <Route path=''/login'' component={Login}></Route>
                    <Route path=''/register'' component={Register}></Route>
                    <Route path=''/chat/:user'' component={Chat}></Route>
                    <Route component={Dashboard}></Route>
                </Switch>
                
            </div>
        )
    }
}
export default App

这里主要是讲主页面中的代码分割出来。
authroute.js中是路由跳转的逻辑判断
页面中的UI组件也用到了antd-mobile插件
客户端接收和传送数据得引入socket.io-client,代码在chat.redux.js中。
聊天器中需要存储在数据库的内容主要为from(发送端)、to(接收端)、read(是否已读)、content(聊天内容)、create_time(聊天时间)而且还需要一个唯一的chatid来代表这个聊天室的唯一性,可以用fromto拼接,拼接函数写在util.js中。

3.Server

后端接口用到了node.jsexpress框架,数据库用到了mongodb,在server文件夹中存放连接数据库的文件,model.js在直接与mongodb数据库连接,

const mongoose = require(''mongoose'');
// 连接mongo,并且使用my_app这个集合
const DB_URL = "mongodb://localhost:27017/chat_app";
mongoose.connect(DB_URL);

const models = {
    user: {
        ''user'': { ''type'': String, ''require'': true },
        ''pwd'': { ''type'': String, ''require'': true },
        ''type'': { ''type'': String, ''require'': true },
        // 头像
        ''avatar'': { ''type'': String },
        // 个人简介或者职位简介
        ''desc'': { ''type'': String },
        // 职位名
        ''title'': { ''type'': String },
        // 如果是boss,还有两个字段
        ''company'': { ''type'': String },
        ''money'': { ''type'': String }
    },
    chat: {
        ''chatid'': { ''type'': String, ''require'': true },
        ''from'': { ''type'': String, ''rewuire'': true },
        ''to'': { ''type'': String, ''require'': true },
        ''read'': { ''type'': String, ''require'': true },
        ''content'': { ''type'': String, ''require'': true, ''default'': '''' },
        ''create_time'': { ''type'': Number, ''default'': new Date().getTime() }
    }
}
for (let m in models) {
    mongoose.model(m, new mongoose.Schema(models[m]))
}
module.exports = {
    getModel: function(name) {
        return mongoose.model(name)
    }
}

连接的数据库端口号为27017,这个视自己电脑的数据库端口号而定。
server.js中引入了http、express、socket.io插件,服务端用的是9093端口,

const express = require(''express'');
const bodyParser = require(''body-parser'');
const cookieParser = require(''cookie-parser'');
const model = require(''./model'')
    // const User = model.getModel(''user'');
const Chat = model.getModel(''chat'');
const path = require(''path'')
const app = express();
//work with express
const server = require(''http'').Server(app);
const io = require(''socket.io'')(server);
io.on(''connection'', function(socket) {
    // console.log(''user login'')
    socket.on(''sendmsg'', function(data) {
        const { from, to, msg } = data;
        const chatid = [from, to].sort().join(''_'');
        Chat.create({ chatid, from, to, content: msg }, function(err, doc) {
                // console.log(doc._doc)
                io.emit(''recvmsg'', Object.assign({}, doc._doc))
            })
            // console.log(data);
            // io.emit(''recvmsg'', data)
    })
})
const userRouter = require(''./user'');
app.use(cookieParser());
app.use(bodyParser.json())
app.use(''/user'', userRouter);
app.use(function(req, res, next) {
    if (req.url.startsWith(''/user/'') || req.url.startsWith(''/static/'')) {
        return next()
    }
    return res.sendFile(path.resolve(''build/index.html''))
})
app.use(''/'', express.static(path.resolve(''build'')))
server.listen(9093, function() {
    console.log(''Node app start at port 9093'')
});

客户端用到的接口写在user.js

const express = require(''express'')
const Router = express.Router();
const model = require(''./model'')
const User = model.getModel(''user'');
const Chat = model.getModel(''chat'');
const _filter = { ''pwd'': 0, ''__v'': 0 };

// 删除所有聊天记录
// Chat.remove({}, function(e, d) {})
// 加密
const utils = require(''utility'');
Router.get(''/list'', function(req, res) {
    const { type } = req.query
    // 删除所有用户
    // User.remove({}, function(e, d) {})
    User.find({ type }, _filter, function(err, doc) {
        return res.json({ code: 0, data: doc })
    })
});
Router.get(''/getmsglist'', function(req, res) {
    const user = req.cookies.userid;
    User.find({}, function(err, userdoc) {
        let users = {};
        userdoc.forEach(v => {
            users[v._id] = { name: v.user, avatar: v.avatar }
        })
        Chat.find({ ''$or'': [{ from: user }, { to: user }] }, function(err, doc) {
            // console.log(doc)
            if (!err) {
                return res.json({ code: 0, msgs: doc, users: users })
            }
        })
    })
})
Router.post(''/readmsg'', function(req, res) {
    const userid = req.cookies.userid;
    const { from } = req.body;
    // console.log(userid, from)
    Chat.update({ from, to: userid }, { ''$set'': { read: true } }, { ''multi'': true },

        function(err, doc) {
            if (!err) {
                return res.json({ code: 0, num: doc.nModified })
            }
            return res.json({ code: 1, msg: ''修改失败'' })
        })
})
Router.post(''/update'', function(req, res) {
    const userid = req.cookies.userid;
    if (!userid) {
        return json.dumps({ code: 1 });
    }
    const body = req.body;
    User.findByIdAndUpdate(userid, body, function(err, doc) {
        const data = Object.assign({}, {
            user: doc.user,
            type: doc.type
        }, body)
        return res.json({ code: 0, data })
    })
});
Router.post(''/login'', function(req, res) {
    const { user, pwd } = req.body;
    User.findOne({ user, pwd: md5Pwd(pwd) }, _filter, function(err, doc) {
        if (!doc) {
            return res.json({ code: 1, msg: ''用户名或者密码错误'' });
        }
        res.cookie(''userid'', doc._id)
        return res.json({ code: 0, data: doc })
    })
});
Router.post(''/register'', function(req, res) {
    console.log(req.body);
    const { user, pwd, type } = req.body;
    User.findOne({ user }, function(err, doc) {
        if (doc) {
            return res.json({ code: 1, msg: ''用户名重置'' })
        }
        const userModel = new User({ user, pwd: md5Pwd(pwd), type });
        userModel.save(function(e, d) {
            if (e) {
                return res.json({ code: 1, msg: ''后端出错了'' })
            }
            const { user, type, _id } = d;
            res.cookie(''userid'', _id)
            return res.json({ code: 0, data: { user, type, _id } })
        })
    })
})
Router.get(''/info'', function(req, res) {
    const { userid } = req.cookies;
    if (!userid) {
        return res.json({ code: 1 })
    }
    User.findOne({ _id: userid }, _filter, function(err, doc) {
            if (err) {
                return res.json({ code: 1, msg: ''后端出错了'' })
            }
            if (doc) {
                return res.json({ code: 0, data: doc })
            }
        })
        // 用户有没有cookie

});
// 密码加盐
function md5Pwd(pwd) {
    const salt = ''lhh_is_good_1310486!@#5^%~*'';
    return utils.md5(utils.md5(pwd + salt))
}
module.exports = Router

三、总结

本项目实现了获取数据和表现的代码分离,也是对于学习React、Node和WebSocket的一次更进一步提升,当然还有很多可以改进的地方,比如可以用asyncawait进行异步获取数据等等。
作为一名前端菜鸟,还是希望前辈能给一些学习的建议和指点迷津
最后附上本项目的代码链接
github链接

react+react-router+redux+react-redux构建一个简单应用

react+react-router+redux+react-redux构建一个简单应用

完整的demo代码:

https://gitee.com/dgx/demo-react

演示:

http://dgx.gitee.io/demo-react/build/index.html#/

一.基本知识

我们已经学习了react的语法使用,react-router和react的配合使用,redux通过react-redux的结合使用,下面我们要组合起来,开发一个简单的应用。

二.应用结构

index.html(我们的单页开发核心静态页面)

index.js(应用渲染首页面)

App.js(核心页面)

rootRedux.js(合并所有状态,对外接口)

indexRedux.js(根状态树)

 

page/(目录,存放路由页面组件)

page/login/LoginReactRedux.js(登录页面组件被react-redux封装,我们的首页显示页面,需要用户登录)

page/login/Login.js(登录页面组件)

page/login/LoginRedux.js(登录页面reducer)

我们不在创建action的页面,不会去分离出去,我们的业务只是demo使用

其他路由页面构建类似...(包含登录页面,主页面,关于我们,新闻中心四个页面作为演示)

 

tpl/(目录,存放公用组件,用于路由页面公用显示使用)

 

我们的ajax处理都会利用setTimeout去模拟,同样也不使用action可以用函数的中间件,因为应用非常简单

三.创建初始化应用

利用我们的create-react-app 项目名 来创建,执行下面创建我们的demo-react应用:

create-react-app demo-react

删除一些不要的东西,让我们的项目看起来尽可能简洁:

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

index.js

import React from ''react'';
import ReactDOM from ''react-dom'';
import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';

ReactDOM.render(<App />, document.getElementById(''root''));
registerServiceWorker();

App.js

import React, { Component } from ''react'';

class App extends Component {
  render() {
    return (
      <div className="App">
        demo
      </div>
    );
  }
}

export default App;

四.安装需要的依赖

我们的项目需要:

react-router:

npm install react-router

react-router-dom:(我们实际使用的路由模块)

npm install react-router-dom

redux:

npm install redux

react-redux:

npm install react-redux

等待完成...

我们的使用版本:

我们采用的react16.x和react-router4.x,不同的版本使用是有区别的,尤其路由使用上

五.创建目录结构和文件

page下存放我们路由使用的页面组件

六.创建路由页面和搭载路由

1.创建页面

这时候我们已经可以看到显示demo的页面,我们开始创建我们的页面:

login/Login.js

import React, { Component } from ''react'';

//=====组件=====

class Login extends Component {
	
	render() {
		return (
			<div>
				<h3>登录页面</h3>
				<div>
					用户名<input type="text" />
				</div>
				<div>
					密码<input type="text" />
				</div>
				<div>
					<button onClick={this.goLogin}>登录</button>
				</div>
			</div>
		);
	}
	
	goLogin(){
		alert("开始登录")
	}
	
	componentDidMount() {
	  	console.log("Login渲染完毕")
	}
	
}


export default Login

home/Home.js

import React, { Component } from ''react'';

//=====组件=====

class Home extends Component {
	
	render() {
		return (
			<div>
				<h3>主页</h3>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

about/About.js

import React, { Component } from ''react'';

//=====组件=====

class About extends Component {
	
	render() {
		return (
			<div>
				<h3>关于我们</h3>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("About渲染完毕")
	}
	
}


export default About

news/News.js

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
		// 设置 initial state
		this.state = {
			list: [
				{id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
				{id:2,title:"b",con:"cbbbbbbbbbbb"},
				{id:3,title:"c",con:"cccccccccccccc"},
				{id:4,title:"d",con:"cddddddddddddd"},
				{id:5,title:"e",con:"ceeeeeeeeeeee"}
			]
		};
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					{
						this.state.list.map(function(item,i){
							return <li key={item.id}>
								<a>{item.title}</a>
								<span>{item.con}</span>
							</li>						
						})	
					}
				</ul>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

2.搭载路由

我们把页面创建完毕,在index.js配置路由:

index.js:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {BrowserRouter as Router} from ''react-router-dom'';
import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';

ReactDOM.render(
	<Router>
		<App />
	</Router>	
, document.getElementById(''root''));
registerServiceWorker();

App.js完成我们路由和页面的使用:

App.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import Login from ''./page/login/Login.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import News from ''./page/news/News.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={Login}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={News}/>
			</div>
      </div>
    );
  }
}

export default App;

我们预览页面,就可以看到大概了:

七.redux和应用配合

创建我们的redux文件:

rootRedux.js(合并所有状态,对外接口):

import { combineReducers } from ''redux'';

//全局reducer
import isLogin from ''./indexRedux.js''
//子reducer


//合并reducer
var rootRedux = combineReducers({
	isLogin
})

export default rootRedux

indexRedux.js(根状态树)我们存放登录状态,默认是未登录:

//reducer

var isLogin=false;

function indexRedux(state = isLogin, action) {
	switch (action.type) {
		case "GO_LOGIN":
			//登录
			return true
		case "OUT_LOGIN":
			//退出登录
			return false
		default:
		  	return state
	}
}


export default indexRedux

index.js使用redux:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {BrowserRouter as Router} from ''react-router-dom'';

//redux 和react-redux(关联react和redux)
import { createStore } from ''redux'';
import { Provider } from ''react-redux'';

//reducers 状态树state和逻辑操作
import rootRedux from ''./rootRedux.js''

import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';


//创建状态树和设置

//生成状态树对象
const store = createStore(rootRedux);

//start 状态树应用到全局 通过Provider
ReactDOM.render(
<Provider store={store}>
	<Router>
		<App />
	</Router>
</Provider>		
, document.getElementById(''root''));
registerServiceWorker();

我们为news创建reducer,把list放入在reducer中,

NewsRedux.js:

//reducer

var newsinit={
	list:[
		{id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
		{id:2,title:"b",con:"cbbbbbbbbbbb"},
		{id:3,title:"c",con:"cccccccccccccc"},
		{id:4,title:"d",con:"cddddddddddddd"},
		{id:5,title:"e",con:"ceeeeeeeeeeee"}
	]
};

function NewsRedux(state = newsinit, action) {
	switch (action.type) {
		case "SORT_REVERSE":
			//倒叙显示
			var arr=state.list;
			var arr2=[];
			for(var i=arr.length-1;i>=0;i--){
				arr2.push(arr[i])
			}
			return Object.assign({},state,{list:arr2})
		default:
		  	return state
	}
}


export default NewsRedux

News.js移除构造函数的json设置:

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					
				</ul>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

rootRedux.js引入news的reducer:

import { combineReducers } from ''redux'';

//全局reducer
import isLogin from ''./indexRedux.js''
//子reducer
import NewsRedux from ''./page/news/NewsRedux.js''

//合并reducer
var rootRedux = combineReducers({
	isLogin,
	NewsRedux
})

export default rootRedux

八.利用react-redux链接react组件和redux

我们先以news的处理作为操作,首先用react-redux封装News.js组件:

创建NewsReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import News from ''./News.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		list: state.NewsRedux.list
	};
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		SORTREVERSE:function(){
			dispatch({type:"SORT_REVERSE"})
		}
	};
}

//封装传递state和dispatch
var NewsReactRedux = connect(mapStateToProps,mapDispatchToProps)(News);

export default NewsReactRedux

我们把redux的状态数据和action全部发射给News组件,我们在里面使用即可:

News.js

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					{
						this.props.list.map(function(item,i){
							return <li key={item.id}>
								<a>{item.title}</a>
								<span>{item.con}</span>
							</li>
						})	
					}
				</ul>
				<button onClick={this.SORTREVERSE.bind(this)}>倒叙显示</button>
			</div>
		);
	}
	
	SORTREVERSE(){
		this.props.SORTREVERSE();
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

App.js使用封装后的组件News:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import Login from ''./page/login/Login.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={Login}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

九.登录处理

/地址就是我们的登录页面,我们点击登录跳转就可以了,不过我们会把用户的登录状态存放在indexRedux.js中,我们不把这个状态存如cookie类似的本地,所以 我们刷新页面退出即可,我们只是模拟的处理:

如果存放在了cookie我们要如何处理,这时候在进入网站我们可以调用一个方法去获取cookie的登录状态,不管是什么我们都会执行action把redux的登录状态改为这个!

因为Login.js要和redux结合,我们要修改登录状态,我们还要结合router去手动跳转到主页,

LoginReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Login from ''./Login.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		GOLOGIN:function(username,password,history){
			console.log("用户名"+username)
			console.log("密码"+password)
			setTimeout(function(){
				dispatch({type:"GO_LOGIN"})
				history.push("/Home")
			},1000)
			
		}
	};
}

//封装传递state和dispatch
var LoginReactRedux = connect(mapStateToProps,mapDispatchToProps)(Login);

export default LoginReactRedux

login.js

import React, { Component } from ''react'';

//=====组件=====

class Login extends Component {
	
	render() {
		return (
			<div>
				<h3>登录页面</h3>
				<div>
					用户名<input type="text" ref="username" />
				</div>
				<div>
					密码<input type="password" ref="password" />
				</div>
				<div>
					<button onClick={this.goLogin.bind(this)}>登录</button>
				</div>
			</div>
		);
	}
	
	goLogin(){
		this.props.GOLOGIN(this.refs.username.value,this.refs.password.value,this.props.history);
	}
	
	componentDidMount() {
	  	console.log("Login渲染完毕")
	}
	
}


export default Login

App.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={LoginReactRedux}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

十.退出登录处理

我们在/Home加一个按钮就是退出按钮他和我们的登录处理相反:

HomeReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Home from ''./Home.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		OUTLOGIN:function(history){
			dispatch({type:"OUT_LOGIN"})
			history.push("/")
			
		}
	};
}

//封装传递state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js:

import React, { Component } from ''react'';

//=====组件=====

class Home extends Component {
	
	render() {
		return (
			<div>
				<h3>主页</h3>
				<div>
					<button onClick={this.outLogin.bind(this)}>退出登录</button>
				</div>
			</div>
		);
	}

	outLogin(){
		this.props.OUTLOGIN(this.props.history);
	}
		
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

十一.权限处理

1.显示级别

我们的网站在/地址是处在登录页面,这时候我们应该只有登录框,在进入主页之后会看到跳转链接,我们要获取我们的登录状态,还控制一些标签的显示:

我们在App.js存放了我们的导航,我们只需要对这个组件利用react-redux做一次封装,拿到状态,利用style去处理即可:

我们把导航提出到组件,并且react-redux做封装,在App.js使用

Nav.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';


class Nav extends Component {
  render() {
    return (
		<ul style={{display:this.props.isLogin?"block":"none"}}>
			<li style={{display:this.props.isLogin?"none":"block"}}>
				<Link to="/">登录</Link>
			</li>
			<li>
				<Link to="/Home">主页</Link>
			</li>
			<li>
				<Link to="/About">关于我们</Link>
			</li>
			<li>
				<Link to="/News">新闻页面</Link>
			</li>
		</ul>
    );
  }
}

export default Nav;

NavReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Nav from ''./Nav.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		isLogin:state.isLogin
	}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {};
}

//封装传递state和dispatch
var NavReactRedux = connect(mapStateToProps,mapDispatchToProps)(Nav);

export default NavReactRedux

App.js我们使用封装后导航:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';
import NavReactRedux from ''./NavReactRedux.js'';
import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import HomeReactRedux from ''./page/home/HomeReactRedux.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<NavReactRedux />
			<div>
				<Route exact path="/" component={LoginReactRedux}/>
				<Route exact path="/Home" component={HomeReactRedux}/>
				<Route exact path="/About" component={About}/>
				<Route exact path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

 

我们测试是没有问题的,我们在显示一级的权限做的差不多了!

 

2.逻辑级别

如果用户直接输入地址怎么办?所以我们在路由对应的页面都要加入登录状态的判断,然后处理是留在当前页面还是跳到登录页面:

我们登录后的页面只有三个,我们先对Home做一个处理,其他的类似:

HomeReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Home from ''./Home.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		isLogin:state.isLogin
	}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		OUTLOGIN:function(history){
			dispatch({type:"OUT_LOGIN"})
			history.push("/")
			
		}
	};
}

//封装传递state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js

import React, { Component } from ''react'';
import {Redirect} from ''react-router-dom'';

//=====组件=====

class Home extends Component {
	
	render() {
		if(this.props.isLogin==false){
			return <Redirect to="/" />
		}
		
		return (
			<div>
				<h3>主页</h3>
				<div>
					<button onClick={this.outLogin.bind(this)}>退出登录</button>
				</div>
			</div>
		);
	}

	outLogin(){
		this.props.OUTLOGIN(this.props.history);
	}
		
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

其他路由页面同理!!!

十二.刷新问题

我们本地存储可以使用cookie还可以使用localstorage,我们刷新应用就获取localstorage对登录状态的设置,然后action即可!

不过不管是cookie还是localstorage如果用户浏览器的安全级别高就完蛋了,我们存放在这个2个里面哪一个都会遇到这个问题。

我们或许可以这样做,在刷新我们就向后台发送一个请求,这个请求会返回用户是否在登录中和返回用户的一些信息,根据状态我们用手动方法跳转链接。

十三.404

这个其实使用的就是router为我们提供的 Switch 组件:

import React, { Component } from ''react'';
import {
  Route,
  Link,
  Switch
} from ''react-router-dom'';
import NavReactRedux from ''./NavReactRedux.js'';
import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import HomeReactRedux from ''./page/home/HomeReactRedux.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';
import NotFind from ''./page/notFind/NotFind.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<NavReactRedux />
			<div>
				<Switch>
					<Route exact path="/" component={LoginReactRedux}/>
					<Route exact path="/Home" component={HomeReactRedux}/>
					<Route exact path="/About" component={About}/>
					<Route exact path="/News" component={NewsReactRedux}/>		
					<Route component={NotFind}/>			
				</Switch> 
			</div>
      </div>
    );
  }
}

export default App;

十四.打包

我们执行下面命令:

npm run build

打包后文件index.html删除 /

我们打开index.html会出现问题,提示404,我们可以把路由处理改为:

HashRouter方式

index.js:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {HashRouter as Router} from ''react-router-dom'';

//redux 和react-redux(关联react和redux)
import { createStore } from ''redux'';
import { Provider } from ''react-redux'';

//reducers 状态树state和逻辑操作
import rootRedux from ''./rootRedux.js''

import App from ''./App.js'';

import registerServiceWorker from ''./registerServiceWorker'';


//创建状态树和设置

//生成状态树对象
const store = createStore(rootRedux);

//start 状态树应用到全局 通过Provider
ReactDOM.render(
<Provider store={store}>
	<Router>
		<App />
	</Router>
</Provider>		
, document.getElementById(''root''));
registerServiceWorker();

(打包后文件index.html删除 /)

 

完整的demo代码:

https://gitee.com/dgx/demo-react

 

reactjs – React Redux Axios:POST请求没有从redux状态接收凭据

reactjs – React Redux Axios:POST请求没有从redux状态接收凭据

我一直在用我的项目进行身份验证.我有一个REST api后端,为JWT令牌提供服务.我的前端堆栈是ReactJS,Redux,Axios和Redux Thunk.

我的问题是,为什么当我提交表单时,它不会发送任何凭据?

但是,当我在credChange上控制记录动作和有效负载时,它似乎是正确的.我不是在某个地方设置州吗?
此外,axios没有捕获400 Bad Request错误.

这是我的代码:

AuthActions.js

export const credChange = ({ prop,value }) => {
  return {
    type: CRED_CHANGE,payload: { prop,value },};
};
export const logoutUser = () => {
  return (dispatch) => {
    dispatch({ type: logoUT_USER });
  };
};
const loginSuccess = (dispatch,response) => {
  dispatch({
    type: LOGIN_USER_SUCCESS,payload: response.data.token,});
};
const loginError = (dispatch,error) => {
  dispatch({
    type: LOGIN_USER_ERROR,payload: error.response.data,});
};
export const loginUser = ({ empNum,password }) => {
  return (dispatch) => {
    dispatch({ type: LOGIN_USER });
    axios({
      method: ''post'',url: ''http://127.0.0.1:8000/profiles_api/jwt/authTK/'',data: {
        emp_number: empNum,password,},})
      .then(response => loginSuccess(dispatch,response))
      .catch(error => loginError(dispatch,error));
  };
};

AuthReducer.js

const INITIAL_STATE = {
  empNum: '''',password: '''',empNumErr: null,passwordErr: null,authTK: null,loading: false,};

export default (state = INITIAL_STATE,action) => {
  switch (action.type) {
    case CRED_CHANGE:
      return { ...state,[action.payload.prop]: action.payload.value };
    case LOGIN_USER:
      return {
        ...state,...INITIAL_STATE,loading: true,};
    case logoUT_USER:
      return {
        ...state,INITIAL_STATE,};
    case LOGIN_USER_SUCCESS:
      return {
        ...state,authTK: action.payload,};
    case LOGIN_USER_ERROR:
      return {
        ...state,empNumErr: action.payload.emp_number,passwordErr: action.payload.password,};
    default:
      return state;
  }
};

LoginForm.js

import React,{ Component } from ''react'';
import { connect } from ''react-redux'';

import {
  credChange,loginUser,logoutUser,} from ''../Actions'';

class LoginForm extends Component {
  constructor() {
    super();
    this.onFormSubmit = this.onFormSubmit.bind(this);
    this.renderEmpNumErr = this.renderEmpNumErr.bind(this);
    this.empNumChange = this.empNumChange.bind(this);
    this.passwordChange = this.passwordChange.bind(this);
  }
  onFormSubmit() {
    const { empNum,password } = this.props;
    this.props.loginUser({ empNum,password });
  }
  empNumChange(text) {
    this.props.credChange({ prop: ''empNum'',value: text.target.value });
  }
  passwordChange(text) {
    this.props.credChange({ prop: ''password'',value: text.target.value });
  }
  renderEmpNumErr() {
    if (this.props.empNumErr) {
      return (
        <p>
          {this.props.empNumErr}
        </p>
      );
    }
    return null;
  }
  render() {
    return (
      <div>
        <form onSubmit={this.onFormSubmit}>
          <label htmlFor="numberLabel">Employee Number</label>
          <input
            id="numberLabel"
            type="password"
            value={this.props.empNum}
            onChange={this.empNumChange}
          />
          <label htmlFor="passLabel">Password</label>
          <input
            id="passLabel"
            type="password"
            value={this.props.password}
            onChange={this.passwordChange}
          />
          <button type="submit">Login</button>
        </form>
        {this.renderEmpNumErr()}
      </div>
    );
  }
}

const mapStatetoProps = ({ counter }) => {
  const {
    empNum,loading,empNumErr,passwordErr,authTK,} = counter;
  return {
    empNum,};
};

export default connect(mapStatetoProps,{ credChange,logoutUser })(LoginForm);

提交带有凭据的表单后

控制台说:

POST XHR http://127.0.0.1:8000/profiles_api/jwt/authTK/ [HTTP / 1.0 400 Bad Request 5ms]

POST请求原始数据为空,因此未发送凭据.

{“emp_number”:[“此字段为必填项.”],“密码”:[“此字段为必填项.”]}

编辑
如果我能提供任何其他信息,请说明,但我认为这应该足够了.

解决方法

看起来empNum和密码没有在州内设置.这是因为credChange返回的操作对象没有被调度,所以reducer永远不会被调用:

// dispatch calls the reducer which updates the state
dispatch(actionCreator())

// returns an action object,doesn''t call reducer
actionCreator()

您可以通过调用绑定的操作创建者自动调度操作:

// calls the reducer,updates the state
const boundActionCreator = () => {dispatch(actionCreator())}

// call boundActionCreator in your component
boundActionCreator()

mapdispatchToProps可用于定义绑定的动作创建者(作为道具传递):

const mapdispatchToProps = (dispatch) => {

  return {
      credChange: ({ prop,value }) => {dispatch(credChange({prop,value})},loginUser: ({ empNum,password }) => {dispatch(loginUser({empNum,password})},logoutUser: () => {dispatch(logoutUser()},}
}

export default connect(mapStatetoProps,mapdispatchToProps)(LoginForm);

这应该解决状态更新问题,允许从状态(empNumber,密码等)读取的道具也更新.

reactjs – React,Redux,React-Router?

reactjs – React,Redux,React-Router?

应用程序架构的问题.
假设页面上有很多组件 (look at the picture)(主电源).
切换主要子组件(活动/非活动)有什么用?
和页面(1,2,3,下一个)?
我可以将react-router用于这两项任务吗?
P.S.:我使用ReactJS进行渲染

解决方法

我不会使用react-router进行结果过滤.由于您使用的是Redux,因此您可以将Reducer拆分为主要部分,并将每个部分的活动/非活动状态以及当前页面保存在一个存储中.

我们今天的关于reactjs – React,Redux和Websocket的最佳实践(保持一个类来处理请求)react-websocket的分享已经告一段落,感谢您的关注,如果您想了解更多关于react+react-router+redux+Node.js+socket.io写一个聊天webapp、react+react-router+redux+react-redux构建一个简单应用、reactjs – React Redux Axios:POST请求没有从redux状态接收凭据、reactjs – React,Redux,React-Router?的相关信息,请在本站查询。

本文标签: