項目地址:github.com/Nealyang/Re…html
本想等項目作完再連載一波系列博客,隨着開發的進行,也是的確遇到了很多坑,請教了很多人。遂想,何不一邊記錄踩坑,一邊分享收穫呢。分享固然是好的,
若是能作到集思廣益,那豈不是更美。咱們的口號是:堅定不會爛尾前端
本博客爲連載代碼博客同步更新博客,隨着項目日後開發可能會遇到前面寫的不合適的地方會再回頭修改。若有不妥~歡迎兄弟們不嗇賜教。謝謝!react
接上篇,咱們登陸界面已經畫完了,登陸功能,涉及到異步請求。因此大體我須要須要以下幾個action。請求發起action,請求結束action,錯誤信息提醒action,登陸action,註冊action以及後面免登錄咱們用到的自動登陸action。git
由於該登陸功能涉及到的都是全局的信息,因此這裏咱們放到index的reducer中處理github
const initialState = {
isFetching: true,
msg: {
type: 1,//0失敗 1成功
content: ''
},
userInfo: {}
};
export const actionsTypes = {
FETCH_START: "FETCH_START",
FETCH_END: "FETCH_END",
USER_LOGIN: "USER_LOGIN",
USER_REGISTER: "USER_REGISTER",
RESPONSE_USER_INFO: "RESPONSE_USER_INFO",
SET_MESSAGE: "SET_MESSAGE",
USER_AUTH:"USER_AUTH"
};
export const actions = {
get_login: function (username, password) {
return {
type: actionsTypes.USER_LOGIN,
username,
password
}
},
get_register: function (data) {
return {
type: actionsTypes.USER_REGISTER,
data
}
},
clear_msg: function () {
return {
type: actionsTypes.SET_MESSAGE,
msgType: 1,
msgContent: ''
}
},
user_auth:function () {
return{
type:actionsTypes.USER_AUTH
}
}
};
export function reducer(state = initialState, action) {
switch (action.type) {
case actionsTypes.FETCH_START:
return {
...state, isFetching: true
};
case actionsTypes.FETCH_END:
return {
...state, isFetching: false
};
case actionsTypes.SET_MESSAGE:
return {
...state,
isFetching: false,
msg: {
type: action.msgType,
content: action.msgContent
}
};
case actionsTypes.RESPONSE_USER_INFO:
return {
...state, userInfo: action.data
};
default:
return state
}
}複製代碼
class LoginFormCom extends Component {
constructor(props) {
super(props);
}
handleLogin = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.login(values.userName,values.password)
}
});
};
render() {
const {getFieldDecorator} = this.props.form;
return (
<Form onSubmit={this.handleLogin} className={style.formStyle}>
<FormItem>
{getFieldDecorator('userName', {
rules: [{required: true, message: '請輸入用戶名!'}],
})(
<Input prefix={<Icon type="user" style={{fontSize: 13}}/>} placeholder="Username"/>
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{required: true, message: '請輸入密碼!'}],
})(
<Input prefix={<Icon type="lock" style={{fontSize: 13}}/>} type="password"
placeholder="Password"/>
)}
</FormItem>
<FormItem>
<Button className={style.loginButton} type="primary" htmlType="submit">
登陸
</Button>
</FormItem>
</Form>
)
}
}
const LoginForm = Form.create()(LoginFormCom);
export default LoginForm複製代碼
如上代碼,在handleLogin中,咱們調用父組件傳進來的login方法。可能得說是爺爺組件吧。罷了,就是其容器組件。數據庫
而容器組件Home.js中的代碼以下:express
Home.defaultProps = {
userInfo:{}
};
Home.propsTypes = {
userInfo:PropTypes.object.isRequired
};
function mapStateToProps(state) {
return{
userInfo:state.globalState.userInfo
}
}
function mapDispatchToProps(dispatch) {
return{
login:bindActionCreators(actions.get_login,dispatch),
register:bindActionCreators(actions.get_register,dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Home);複製代碼
如上,咱們已經定義了login和register。分別爲登陸和註冊兩個方法。在登陸部分咱們如上寫。固然,註冊功能也是如上。json
由於登陸和註冊都是異步的,因此這裏咱們須要saga去監聽這個action的發起。而後對應的去處理。redux
export function* register (data) {
yield put({type:IndexActionTypes.FETCH_START});
try {
return yield call(post, '/user/register', data)
} catch (error) {
yield put({type:IndexActionTypes.SET_MESSAGE,msgContent:'註冊失敗',msgType:0});
} finally {
yield put({type: IndexActionTypes.FETCH_END});
}
}
export function* registerFlow () {
while(true){
let request = yield take(IndexActionTypes.USER_REGISTER);
let response = yield call(register, request.data);
if(response&&response.code === 0){
yield put({type:IndexActionTypes.SET_MESSAGE,msgContent:'註冊成功!',msgType:1});
yield put({type:IndexActionTypes.RESPONSE_USER_INFO,data:response.data})
}
}
}複製代碼
這裏咱們就舉例說下registerFlow吧,其實也就是監聽USER_REGISTER的action。而後調用register方法,發送請求開始action(界面出現Loading),而後請求結束action。接收到請求後,拿出數據,發送拿到數據後的action後端
基本思路如上,代碼如上,你們研究研究哈,不明白的地方,直接issue。
router.post('/register', (req, res) => {
let {userName, password, passwordRe} = req.body;
if (!userName) {
responseClient(res, 400, 2, '用戶名不可爲空');
return;
}
if (!password) {
responseClient(res, 400, 2, '密碼不可爲空');
return;
}
if (password !== passwordRe) {
responseClient(res, 400, 2, '兩次密碼不一致');
return;
}
//驗證用戶是否已經在數據庫中
User.findOne({username: userName})
.then(data => {
if (data) {
responseClient(res, 200, 1, '用戶名已存在');
return;
}
//保存到數據庫
let user = new User({
username: userName,
password: md5(password + MD5_SUFFIX),
type: 'user'
});
user.save()
.then(function () {
User.findOne({username: userName})
.then(userInfo=>{
let data = {};
data.username = userInfo.username;
data.userType = userInfo.type;
data.userId = userInfo._id;
responseClient(res, 200, 0, '註冊成功', data);
return;
});
})
}).catch(err => {
responseClient(res);
return;
});
});複製代碼
後端這邊其實都差很少,咱們拿註冊舉例子。簡單解釋下上面代碼
responseClient是封裝的一個方法。代碼以下:
module.exports = {
MD5_SUFFIX: 'eiowafnajkdlfjsdkfj大姐夫文姐到了困難額我積分那看到你@#¥%……&)(*&……)',
md5: function (pwd) {
let md5 = crypto.createHash('md5');
return md5.update(pwd).digest('hex')
},
responseClient(res,httpCode = 500, code = 3,message='服務端異常',data={}) {
let responseData = {};
responseData.code = code;
responseData.message = message;
responseData.data = data;
res.status(httpCode).json(responseData)
}
}複製代碼
讓你簡寫不少代碼。而後判斷用戶名、密碼是否爲空以及兩次密碼是否一致。(雖然這些部分在前端也應該作判斷,可是後端也儘可能保障一下)。
驗證用戶是否已經在數據庫中。若是不存在,則存儲下,而後將用戶信息返回。若是存在,則返回給客戶端相應的信息。
注意這裏咱們由於用的saga,因此只要是http請求三次握手成功的,咱們都是返回200.對於用戶名重複、別的錯誤,咱們統一在返回的數據中給個狀態碼標識。
存儲的時候咱們用md5加密,爲了防止md5解密,咱們在後面添加了一個隨機的字符串。在登陸的時候,拿到用戶的登陸密碼,而後加上隨機字符串,進行md5加密再與數據庫數據記性比較也就OK了。
後端實現基本思路就是這些,而後對於mongoose的基本操做這裏就不贅述,你們可自行查看文檔。
router.post('/login', (req, res) => {
let {username, password} = req.body;
if (!username) {
responseClient(res, 400, 2, '用戶名不可爲空');
return;
}
if (!password) {
responseClient(res, 400, 2, '密碼不可爲空');
return;
}
User.findOne({
username,
password: md5(password + MD5_SUFFIX)
}).then(userInfo => {
if (userInfo) {
//登陸成功
let data = {};
data.username = userInfo.username;
data.userType = userInfo.type;
data.userId = userInfo._id;
//登陸成功後設置session
req.session.userInfo = data;
responseClient(res, 200, 0, '登陸成功', data);
return;
}
responseClient(res, 400, 1, '用戶名密碼錯誤');
}).catch(err => {
responseClient(res);
})
});複製代碼
基本到這,就是實現了一個查和增的過程。也實現了先後端的基本交互。你們感覺下哈~
而後你們確定也是發現了,登陸了之後,貌似每次刷新咱們都要再從新登陸,這並非咱們想要的。固然,這部分功能,咱們將在下一篇博客中介紹。
實戰react技術棧+express先後端博客項目(17)-- 收工
假若有哪裏說的不是很明白,或者有什麼須要與我交流,歡迎各位提issue。或者加羣聯繫我~
掃碼關注個人我的微信公衆號,直接回復,必有迴應。分享更多原創文章。點擊交流學習加我微信、qq羣。一塊兒學習,一塊兒進步
歡迎兄弟們加入:
Node.js技術交流羣:209530601
React技術棧:398240621
前端技術雜談:604953717 (新建)