实战:使用 Redux-Saga 实现复杂的登录与注销、take函数的用法

新建user.php

<?php
header('Access-Control-Allow-Origin:*');  
header('Access-Control-Allow-Methods:GET,POST');  
header('Access-Control-Allow-Headers:x-requested-with,content-type'); 
sleep(1);
$result          = new stdClass();
$result->message = "error username or password";
$result->status  = "error";
$result->token   = "none";
if (isset($_POST["user_name"]) && isset($_POST["user_pass"])) {
    $get_userName = $_POST["user_name"];
    $get_userPass = $_POST["user_pass"];
    if ($get_userName == "shenyi" && $get_userPass == "123") {
        $result->message = "login success";
        $result->status  = "success";
        $result->token   = "0ab7f9aa-e246-4359-aee9-8b67fd5dc3f3"
    }
}
header("content-type:application/json");
exit(json_encode($result));
?>

新建userlevel.php

<?php
header('Access-Control-Allow-Origin:*');  
header('Access-Control-Allow-Methods:GET,POST');  
header('Access-Control-Allow-Headers:x-requested-with,content-type'); 
sleep(1);
$result = new stdClass();
$result->level = "none";
if (isset($_POST["token"]) && $_POST["token"] == "0ab7f9aa-e246-4359-aee9-8b67fd5dc3f3") {
    $result->level = "黄金VIP";
}
header("content-type:application/json");
exit(json_encode($result));

新建UserSaga.js

import { call, put, takeEvery, select, take, fork, cancel, cancelled } from 'redux-saga/effects'
import axios from 'axios'
import qs from 'qs'

class UserAPI {
    static userLogin (user_name, user_pass) {
        return axios.post('http://localhost:8080/user.php', qs.stringify({ user_name: user_name, user_pass: user_pass })).then((res) => res.data)
    }
    static getUserLevel (token) {
        return axios.post('http://localhost:8080/userlevel.php', qs.stringify({ token: token })).then((res) => res.data)
    }
}

function* login () {
    try {
        // 获取state
        const { userName, userPass } = yield select()
        // ajax登录
        const { token }  = yield call(UserAPI.userLogin, userName, userPass)
        // 代表用户登录成功
        if (token && token != 'none') {
            // 通知修改 isLogin 标识
            yield put({type: 'LOGIN_SUCCESS'})
            // ajax获取用户等级
            const { level } = yield call(UserAPI.getUserLevel, token)
            // 用户等级获取成功
            if (level && level != 'none') {
                yield put({type: 'UPDATE_USERLEVEL', level})
            // 用户等级获取失败
            } else {
                yield put({type: 'UPDATE_USERLEVEL', level: '获取等级失败'})
            }
        // 代表登录失败
        } else {
            yield put({type: 'LOGIN_ERROR'})
        }
    } catch (e) {
        // ...   
    } finally {
        if (yield cancelled()) {
             // ... 这里可以进行一些业务逻辑
            console.log('任务被取消')
        }
    }
}

export function* UserSaga () {
    // 如果没有while(true) 那么只能执行一次。这是生成器函数的特性导致的
    while (true) {
        // 定义【登录】任务,等待被调用。一旦被调用,就会执行下面的代码
        yield take('USER_LOGIN')
        // 按钮不可用
        yield put({type: 'ACTIVE_CHANGE', btnDisabled: true})
        // fork:不阻塞任务
        const task_001 = yield fork(login)

        // ............................................................

        // 定义【注释】任务,等待被调用
        yield take('LOGIN_OUT')
        // 如果任务存在,那么取消任务
        if (task_001) {
            // 尽可能的取消login的执行,并且会触发cancelled()
            yield cancel(task_001)
        }
        // 退出登录!
        yield put({type: 'LOGIN_OUT_DONE'})  
    }
}

新建UserReduce.js

export default (state = { btnDisabled: false, isLogin: false, mylevel: '' }, action) => {
    switch (action.type) {
        case 'UPDATE_USERFORM':
            return Object.assign({}, state, action.Form)
        case 'ACTIVE_CHANGE':
            return Object.assign({}, state, { btnDisabled: action.btnDisabled })
        case 'LOGIN_SUCCESS':
            return Object.assign({}, state, { btnDisabled: false, isLogin: true })
        case 'LOGIN_ERROR':
            return Object.assign({}, state, { btnDisabled: false, isLogin: false, mylevel: '' })
        case 'LOGIN_OUT_DONE':
            return Object.assign({}, state, { btnDisabled: false, isLogin: false, mylevel: '' })
        case 'UPDATE_USERLEVEL':
            return Object.assign({}, state, { mylevel: action.level })
        default:
            return state
    }
}

修改main.js

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore ,applyMiddleware } from 'redux'
import UserReduce from './redux/UserReduce'
import { UserSaga } from './redux/UserSaga'
import createSaga from 'redux-saga'

let saga = createSaga()
let store = createStore(UserReduce,applyMiddleware(saga))
saga.run(UserSaga)

class UserLogin extends React.Component {
    constructor (props) {
        super(props)
        this.S = this.props.Store
    }
    componentWillMount () {
        this.S.subscribe(() => this.forceUpdate())
    }
    textChange (e, key) {
        this.S.dispatch({type: 'UPDATE_USERFORM', Form:{[key]: e.target.value}})
    }
    userSubmit () {
        this.S.dispatch({type: 'USER_LOGIN'})
    }
    render () {
        return <div>
            <h2>用户登录</h2>
            <div><span>用 户 名:</span><input type = 'text'    onChange = { e => {this.textChange(e, 'userName')}}/></div>
            <div><span>密    码:</span><input type = 'passwod' onChange = { e => {this.textChange(e, 'userPass')}}/></div>
            <div><span>状    态:</span><span> { this.S.getState().isLogin ? '已经登录' : '未登录' }</span></div>
            <div><span>用户等级:</span><span> { this.S.getState().mylevel }</span></div>
            <div><button onClick = { this.userSubmit.bind(this) } disabled = { this.S.getState().btnDisabled } > 登录 </button></div>
            <div><button onClick = { () => { this.S.dispatch({type: 'LOGIN_OUT'})} }> 注销 </button></div>
        </div>
    }
}

ReactDOM.render(
    <UserLogin Store = { store }/>,
    document.getElementById('root')
)

results matching ""

    No results matching ""