redux-saga 一个用于管理Redux应用异步操作的中间件,使副作用(数据获取、浏览器缓存获取)易于管理、运行、测试和失败处理
npm i redux-saga -S
-
redux-saga通过创建sagas将所有异步操作逻辑收集在一个地方集中处理,可用于取代redux-thunk中间件;-
redux-saga可以处理各种复杂的异步操作,而redux-thunk只适用于简单的异步场景; -
redux-saga使用generator解决异步问题,使用同步方式编写异步逻辑,解决了回调地狱问题; -
redux-thunk可以接收function类型的action,而redux-saga则是纯对象action
-
-
redux-saga的工作成员-
reducer负责action的state更新; -
sagas负责协调那些复杂或异步的操作。
-
基本用法
redux目录下:user.redux.js、sagas.js、index.js
-
reducer函数:user.redux.js// 初始化state const initialState = { isLogin: false } // reducer export default (state = initialState, action) => { switch(action.type) { case 'login': return { isLogin: true } default: return state } } // redux 生成action的函数 const login = function() { return { type: 'login_request' } } export { login } -
sagas.jsimport { call, put, takeEvery } from 'redux-saga/effects' const api = { login() { // 模拟登录的接口 //返回一个Promise return new Promise((resolve, reject) => { setTimeout(() => { //随机数的方式判断是否登录成功 if(Math.random() > 0.5) { resolve({ id: 1, name: 'Tim' }) } else { reject({ message: '用户名或密码错误!' }) } }, 2000) }) } } //工作的saga,ES6中的Generator生成器 function* login(action) { try { // call 调用异步方法,开始登录,并接收登录返回的信息 const res = yield call(api.login) // put 派发登录成功的 action,触发 reducer 函数 yield put({ type:'login', res }) } catch(err) { // put 派发登录失败的 action,触发 reducer 函数 yield put({ type: 'login_fail', message: err.message }) } } // 暴露出去的方法,执行工作的saga函数 login() function* mySaga() { // 事件监听:监听action yield takeEvery('login_request', login) } export default mySaga -
index.jsimport { createStore, applyMiddleware, combineReducers } from 'redux' import logger from 'redux-logger' import user from './user.redux' import createSagaMiddleware from 'redux-saga' import saga from './sagas' //1. 创建中间件 const mid = createSagaMiddleware() //2. 应用中间件 const store = createStore( combineReducers({ user }), // combineReducers 用于 reducer 模块化 applyMiddleware(mid, logger) // 使用两个中间件:saga、redux-logger ) //3. 执行saga,开始监听 mid.run(saga) export default store - 根组件中
import {connect, Provider} from 'react-redux' import store from './redux/index' //index可以省略 './redux' export default function App() { return ( <BrowserRouter> <Provider store={store}> <MainCompt></MainCompt> </Provider> <BrowserRouter> ) } - MainCompt.js
import { connect } from 'react-redux' import { login } from './redux/user.redux' //引入登录方法login() @connect( state=>({ isLogin: state.user.isLogin }) // 传递redux中的state ) class RouteGuard extends Component { //路由守卫 render() { const {component:Component, ...otherProps} = this.props return ( <Route {...otherProps} render={props => ( //根据redux中的state isLogin变量判断是否登录,没有登录则重定向到登录页 this.props.isLogin ? <Component {...props}></Component> : (<Redirect to={{ pathname:'/login', state:{from:props.location.pathname} }} />) )}></Route> ) } } @connect( state=>({isLogin:state.user.isLogin}), // 传递redux中的state { isLogin: true/false } { login } ) class Login extends Component { //登录组件 render() { const from = (this.props.location&&this.props.location.state&&this.props.location.state.from) || '/' if(this.props.isLogin) { return <Redirect to={from} /> // 已经登录了,重定向到守卫的路由,否则重定向到"/" } // 尚未登录 return ( <div> <h2>请先登录</h2> <button onClick={this.props.login}>登录</button> </div> ) } } export default class MainCompt extends Component { constructor(props) { super(props) } render() { return(<BrowserRouter> <div> <header> <NavLink to="/">首页</NavLink> <NavLink to="/news">新闻</NavLink> <NavLink to="/login">登录</NavLink> <NavLink to="/mine">我的</NavLink> </header> <Switch> <Route exact path="/" component={Home} /> <Route path="/news" component={News} /> <Route path="/login" component={Login} /> <RouteGuard path="/mine" component={Mine} /> </Switch> </div> </BrowserRouter>) } }