用react的思维考虑网页用户登陆的逻辑

Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack,it's easy to try it out on a small feature in an existing project.

我大致意译一下:React仅仅尝试解决关于UI的某一个方面(不是全部)的问题,它不来假设你使用了什么其它技术。它易于理解,可以在您已经存在的项目中尝试使用。背后的哲学是:我只做一件事(关于UI),并且把它做到极致。

这不是一篇叫你如何使用React的文章。它只是用react的方式思考一个问题,把它表达出来,如果您是业内人士,一定会用自己的技术栈和它作出对比。如果你这样做了,这篇文章的目的就达到了。

这是一个登陆页面,服务器一端提供restful形式的响应,这是一个单页面程序。服务器响应的数据结构如下,| 表示可能性:

{
state:ANONYMOUS|REMEMBER_ME|FULL,showCaptcha:true|false,message:WRONG_JSON_MESSAGE|USERNAME_PASSWORD_NOT_MATCH|UNKNOWN|SHOW_CAPTCHA|WRONG_CAPTCHA|NO_MESSAGE,user:null|{email:xxx@yyy},wtg:wheretogo
}

客户端rest请求的表:

路径 http方法 content-type 响应
/login GET * 登陆页面HTML(静态)
/login POST application/json 上面提到的数据结构
/loginstatus GET * 上面提到的数据结构

可能的用户操作流程:

  1. 用户访问/login,返回登陆html页面。页面内的rest客户端访问/loginstatus,获取数据。

  2. 用户输入用户名密码等,rest客户端post到服务器,获取数据

  3. 用户可能中途刷新了浏览器,那么就和1一样了。

不管是哪种可能,我们都会获取一个数据。数据有许多不同的组合,我们把不同的组合叫做状态,react的核心就是状态(数据)和UI之间建立单向的联系。

下面来分析一下可能的状态:

1、用户打开登陆页面,此时通过/loginstatus获取的数据可能是:

{state:ANONYMOUS,message:NO_MESSAGE,showCaptcha:false}

这种状态下,显示,用户名框,密码框,登陆按钮

2、用户登录时返回错误,此时的数据可能是:

{state:ANONYMOUS,message:USERNAME_PASSWORD_NOT_MATCH,showCaptcha:false}

这时可以文本框添加显示错误状态的css等。

3、用户尝试了好几次,超过显示验证码的阈值

{state:ANONYMOUS,showCaptcha:true}

此时开始,验证码字段显示。为什么说此时开始呢?即使你刷新了浏览器,还是会显示验证码字段。

4、用户登录成功:

state:FULL,showCaptcha:false,wtg:"http://哪里跌倒,哪里起来"}

此时客户端根据情况开始新的页面。

从整个过程来说,用react提倡的状态来思考,一切行为都通用化,没有什么特殊性。UI只是忠实的反应状态而已,至于是显示验证码,或提示用户名错误都一视同仁。

代码:

1、表单容器组件,主要是为登陆表单增加一些效果切换,比如页面初始打开时显示的是Loading,然后在请求登陆状态的时候显示"querying state",最后根据query结果,显示登陆表单。

varLoginFormContainer=React.createClass({
getInitialState:function(){
return{queryLoginState:true};
},componentDidMount:function(){
console.log(this.props.ucRestClient?"ucrestclientisset.":"noucrestclient");
varthat=this,restClient=this.props.ucRestClient;

restClient.loginstate().then(function(response){
console.log('response:',response);
that.setState({queryLoginState:false,entity:response.entity});
});
},render:function(){
varc;
if(this.state.queryLoginState){
c=<p>queryLoginState....</p>;
}else{
c=<LoginForm{...this.props}{...this.state.entity}/>;
}
return(
<divclassName="login-form-container">
{c}
</div>
);
}
});

2、表单组件:

varUserNameField=React.createClass({
componentDidMount:function(){
console.log(this.props.value);//=>true
},render:function(){
return(
<divclassName="pure-control-group">
<labelhtmlFor="username">Username</label>
<inputname="username"value={this.props.value}type="text"placeholder="Username"/>
</div>);
}
});

varPasswordField=React.createClass({
render:function(){
return(
<divclassName="pure-control-group">
<labelhtmlFor="password">Password</label>
<inputname="password"value={this.props.value}type="password"placeholder="Password"/>
</div>);
}
});

varCaptchaField=React.createClass({
render:function(){
return(
<divclassName="pure-control-group">
<labelhtmlFor="captcha">Captcha</label>
<inputname="captcha"value={this.props.value}placeholder="Captcha"/>
</div>);
}
});

varLoginForm=React.createClass({
getInitialState:function(){
return{};
},handleSubmit:function(e){
e.preventDefault();
if(this.state.submiting){
return;
}

varusername=this.state.username,password=this.state.password,captcha=this.state.captcha,ucClient=this.props.ucRestClient,that=this;

varcanSubmit=true;
if(!username||username.length<3){
canSubmit=false;
}

if(!password||password.length<6){
canSubmit=false;
}

if(this.state.showCaptcha){
if(!captcha||captcha.length!==5){
canSubmit=false;
}
}

if(canSubmit){
this.setState({submiting:true});
ucClient.login({
username:username,password:password,captcha:captcha
}).then(function(response){
			console.log('response:',response);
that.setState({submiting:false});
			});
}
},handleChange:function(e){
//DOMEventTarget,id,tagName
varstateo={};
stateo[e.target.name]=event.target.value.trim();
this.setState(stateo);
//console.log(e.target.value);
},render:function(){
varcaptchaField;
if(this.state.showCaptcha){
captchaField=<CaptchaFieldvalue={this.state.captcha}/>;
}
return(
<formclassName="pure-formpure-form-aligned"action="/login"method="POST"onSubmit={this.handleSubmit}onChange={this.handleChange}>
<fieldset>
<UserNameFieldvalue={this.state.username}/>
<PasswordFieldvalue={this.state.password}/>
{captchaField}
<divclassName="pure-controls">
<labelhtmlFor="cb"className="pure-checkbox">
<inputid="cb"type="checkbox"/>I'vereadthetermsandconditions
</label>
<buttontype="submit"className="pure-buttonpure-button-primary">Submit</button>
</div>
</fieldset>
</form>
);
}
});

3、rest客户端工具代码:

'usestrict';

varrest,mime,csrf,client;

rest=require('rest');
mime=require('rest/interceptor/mime');
csrf=require('rest/interceptor/csrf');

var_csrf=window.jQuery("meta[name='_csrf']").attr("content"),_csrf_header=window.jQuery("meta[name='_csrf_header']").attr("content"),ucclient=rest.wrap(mime,{mime:'application/json'}).wrap(csrf,{token:_csrf});

module.exports={
	login:function(entity){
		returnucclient({
			path:'/login',method:"POST",entity:entity});
	},loginstate:function(){
		returnucclient({
path:'/loginstate',method:"GET"
});
	}
};


注意:以上代码是本人在2天时间内(包括寻找开发环境的设置)通过阅读reactjs官方文档写作而成,可能会存在非常规的代码写法,请指正。

结论:以reactjs编写UI界面,逻辑非常简单明了,便于维护,强烈推荐尝试。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...