React

应用状态的管理

Redux

  1. Flux
    • 结构思想
    • 专门解决软件的架构问题,与MVC架构是同一类东西
    • 更加简单与清晰
    • 本身存在多种实现方式
  2. Facebook_Flux
    • 用来构建客户端Web应用的应用架构
    • 利用单向数据流的方式结合React中的视图组件
    • 低学习成本
  3. 流程
    • 客户端访问view
    • View发出用户的Action
    • Dispatcher收到Action,要求Store进行相应的更新
    • Store更新后,发出一个change事件
    • View收到change事件后,更新页面
  4. Redux
    • 应用状态的管理(订阅发布模式),Redux的临时数据存储于内存中(刷新页面后就会销毁)
    • 用一个单独的常量状态树(State对象)保存这一整个应用的状态,这个对象不能被直接改变
    • 当一些数据变化了,一个新的对象就会被创建(使用actions和reduces),这样就可以进行数据追踪
  5. Redux使用和设计的三大原则
    • state以单一对象存储在state中,state只读(每次都返回一个新的对象)
    • 使用纯函数reducer执行state更新,对外界没有副作用(变量、对象、状态等)
    • 同样的输入得到同样的输出(多次同样的调用结果都一样)

代码示例:

/* src/css/Tabber.module.css */

.myActive {
    color: #30363d;
}

.tabbar {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    background: azure;
    height: 50px;
    line-height: 50px;
    text-align: center;
}

.tabbar ul {
    display: flex;
    list-style: none;
}

.tabbar li {
    flex:1
}

.tabbar a {
    text-decoration: none;
}
// src/components/Tabber.jsx

import React from "react";
import {NavLink} from "react-router-dom";
import style from '../css/Tabber.module.css'

export default function Tabber() {
    return (
        <footer className={style.tabbar}>
            <ul>
                <li>
                    <NavLink to={'/films'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>首页
                    </NavLink>
                </li>
                <li>
                    <NavLink to={'/cinemas'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>内容
                    </NavLink>
                </li>
                <li>
                    <NavLink to={'/centers'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>我的
                    </NavLink>
                </li>
            </ul>
        </footer>
    )
}
// src/redux/actioncreator/TabbarActionCreator.js

function hidden() {
    return {
        type: 'Detail-hidden'
    }
}

function show() {
    return {
        type: 'Detail-show'
    }
}

export {hidden, show}  // 批量导出(对象)
// src/redux/reducers/CityReducer.js

const CityReducer = (prevState = {
    cityName: '幻想乡'
}, action) => {  // 默认值防止第一次运行老状态undefined
    let newState = {...prevState}

    switch (action.type) {
        case 'change-city':
            newState.cityName = action.payload
            return newState
        default:
            return newState
    }
}

export default CityReducer
// src/redux/reducers/TabbarReducer.js

const TabbarReducer = (prevState = {
    show: true
}, action) => {  // 默认值防止第一次运行老状态undefined
    let newState = {...prevState}

    switch (action.type) {
        case 'Detail-hidden':
            newState.show = false
            return newState
        case 'Detail-show':
            newState.show = true
            return newState
        default:
            return newState
    }
}

export default TabbarReducer
//src/redux/store.js

import {combineReducers, legacy_createStore as createStore} from 'redux'  // 原版createStore已被标记废弃,官方鼓励使用改进版的工具箱(简化Redux)
import CityReducer from "./reducers/CityReducer";
import TabbarReducer from "./reducers/TabbarReducer";

const reducer = combineReducers({  // 拆分reducer与对象简写
    CityReducer,
    TabbarReducer
})
const store = createStore(reducer)

export default store

function myCreateStore(reducer) {  // Redux最基本的原理
    let list = []
    let state = reducer(undefined, {})  // 防止初始值undefined

    function subscribe(callback) {
        list.push(callback)
    }

    function dispatch(action) {
        state = reducer(state, action)  // 每次调用都会覆盖旧的state
        for (let i in list) {
            list[i] && list[i]()
        }
    }

    function getState() {
        return state
    }

    return {  // 对象简写
        subscribe,
        dispatch,
        getState
    }
}

// let obj = {name: 'Foo'}
// function foo(obj) {
//     obj.name = 'Bar'
//     return obj
// }
// foo(obj)  // Bar(对外界有副作用的不属于纯函数)

// let obj = {name: 'Foo'}
// function foo(obj) {
//     let newObj = {...obj}
//     newObj.name = 'Bar'
//     return newObj
// }
// foo(obj)  // Foo
// src/router/index.jsx

/*
{
    path: '/city',
    element: LazyLoad('views/City')
}
*/
// src/views/Cinemas.jsx

import React, {useState} from "react";
import store from "../redux/store";
import {useNavigate} from "react-router-dom";

export default function Cinemas() {
    const [cityName] = useState(store.getState().CityReducer.cityName)
    const navigate = useNavigate()

    return (
        <div>
            <div onClick={() => {
                navigate(`/city`)
            }}>{cityName}
            </div>
        </div>
    )
}
// src/views/City.jsx

import React, {useState} from "react";
import store from "../redux/store";
import {useNavigate} from "react-router-dom";

export default function City() {
    const navigate = useNavigate()
    const [list] = useState(['红魔馆', '博丽神社', '白玉楼', '地灵殿'])

    return (
        <div>
            <ul>
                {
                    list.map(item =>
                        <li key={item} onClick={() => {
                            store.dispatch({
                                type: 'change-city',
                                payload: item
                            })
                            navigate(`/cinemas`)
                        }}>{item}
                        </li>
                    )
                }
            </ul>
        </div>
    )
}
// src/views/Detail.jsx

import React, {useEffect} from "react";
import store from "../redux/store";
import {hidden, show} from "../redux/actioncreator/TabbarActionCreator";

export default function Detail() {
    useEffect(() => {
        store.dispatch(hidden())

        return () => {
            store.dispatch(show())
        }
    }, [])

    return (
        <div>
            Detail
        </div>
    )
}
// src/App.jsx

import React, {Component} from "react";
import {BrowserRouter} from "react-router-dom";
import MRouter from "./router";
import Tabber from "./components/Tabber";
import './css/App.css'
import store from "./redux/store";

export default class App extends Component {
    state = {
        isShow: store.getState().TabbarReducer.show  // 拿到最新状态
    }

    componentDidMount() {
        store.subscribe(() => {
            this.setState({
                isShow: store.getState().TabbarReducer.show
            })
        })
    }

    render() {
        return (
            <BrowserRouter>
                <MRouter/>
                {this.state.isShow && <Tabber/>}
            </BrowserRouter>
        )
    }
}

中间件

  1. 原理
    • dispatch时会给每个redux-store的子组件发送数据,直到数据匹配上
  2. 原因
    • 如果不是从正常的DOM进入页面(地址栏跳转),那么store跨组件不会产生缓存
  3. 解决方式
    • 产生缓存的逻辑区块提取出来写成组件

代码示例:

// src/views/redux/actioncreator/getCinemaListAction.js

import axios from "axios";

// redux-thunk
// function getCinemaListAction() {
//     return (dispatch) => {  // dispatch会被中间件看准时机自动调用
//         axios.get(`/test.json`).then(res => {
//             console.log(res.data.films)
//
//             dispatch({  // 因为是异步的,所以return必须放在这里(被调用时还没来得及返回数据就会显示undefined)
//                 type: 'change-list',
//                 value: res.data.films
//             })
//         }).catch(err => console.log(err))
//     }
// }

// redux-promise
function getCinemaListAction() {
    return (
        axios.get(`/test.json`).then(res => {
            console.log(res.data.films)

            return {  // 返回的是promise对象
                type: 'change-list',
                value: res.data.films
            }
        }).catch(err => console.log(err))
    )
}

// async/await写法
// async function getCinemaListAction() {
//     return await axios.get(`/test.json`).then(res => {  // 等待axios返回
//         console.log(res.data.films)
//
//         return {  // 返回的是promise对象
//             type: 'change-list',
//             value: res.data.films
//         }
//     }).catch(err => console.log(err))
// }

export default getCinemaListAction
// src/redux/reducers/CityReducer.js

const CityReducer = (prevState = {
    cityName: '幻想乡'
}, action) => {  // 默认值防止第一次运行老状态undefined
    let newState = {...prevState}

    switch (action.type) {
        case 'change-city':
            newState.cityName = action.payload
            return newState
        default:
            return newState
    }
}

export default CityReducer
//src/redux/store.js

import {applyMiddleware, combineReducers, legacy_createStore as createStore} from 'redux'
import CityReducer from "./reducers/CityReducer";
import TabbarReducer from "./reducers/TabbarReducer";
import CinemaListReducer from "./reducers/CinemaListReducer";
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

const reducer = combineReducers({  // 拆分reducer与对象简写
    CityReducer,
    TabbarReducer,
    CinemaListReducer
})
const store = createStore(reducer, applyMiddleware(reduxThunk, reduxPromise))  // 分别对应同步和异步环境的处理方式

export default store
// src/router/index.jsx

/*
{
	path: '/city',
	element: LazyLoad('views/City')
},
{
	path: '/cinemas/search',
	element: LazyLoad('views/Search')
}
*/
// src/views/Cinemas.jsx

import React, {useEffect, useState} from "react";
import store from "../redux/store";
import {useNavigate} from "react-router-dom";
import getCinemaListAction from "../redux/actioncreator/getCinemaListAction";

export default function Cinemas() {
    const [cityName] = useState(store.getState().CityReducer.cityName)
    const navigate = useNavigate()
    const [cinemaList, setcinemaList] = useState(store.getState().CinemaListReducer.list)

    useEffect(() => {
        if (store.getState().CinemaListReducer.list.length === 0) {
            // 去后端取数据
            // 不要在组件中写入过多的独立请求(需要封装成一个组件)
            store.dispatch(getCinemaListAction())
        } else {
            console.log('数据已被缓存,无需重复向后端获取')
        }

        let unsubscribe = store.subscribe(() => {  // 此方法并不会随着组件的销毁而被销毁(且会引发重复订阅)
            setcinemaList(store.getState().CinemaListReducer.list)
        })

        return () => {
            // 销毁方法
            unsubscribe()
        }
    }, [])

    return (
        <div>
            <div style={{overflow: 'hidden'}}>
                <div onClick={() => {
                    navigate(`/city`)
                }} style={{float: 'left'}}>{cityName}
                </div>
                <div onClick={() => {
                    navigate(`/cinemas/search`)
                }} style={{float: 'right'}}>搜索
                </div>
            </div>
            {
                cinemaList.map(item =>
                    <dl key={item.filmId} style={{padding: '10px'}}>
                        <dt>{item.name}</dt>
                        <dd style={{fontSize: '12px', color: 'gray'}}>{item.synopsis}</dd>
                    </dl>
                )
            }
        </div>
    )
}
// src/views/City.jsx

import React, {useState} from "react";
import store from "../redux/store";
import {useNavigate} from "react-router-dom";

export default function City() {
    const navigate = useNavigate()
    const [list] = useState(['红魔馆', '博丽神社', '白玉楼', '地灵殿'])

    return (
        <div>
            <ul>
                {
                    list.map(item =>
                        <li key={item} onClick={() => {
                            store.dispatch({
                                type: 'change-city',
                                payload: item
                            })
                            navigate(`/cinemas`)
                        }}>{item}
                        </li>
                    )
                }
            </ul>
        </div>
    )
}
// src/views/Search.jsx

import React, {useEffect, useState, useMemo} from "react";
import store from "../redux/store";
import getCinemaListAction from "../redux/actioncreator/getCinemaListAction";

export default function Search() {
    const [myText, setMyText] = useState('')
    const [cinemaList, setcinemaList] = useState(store.getState().CinemaListReducer.list)

    useEffect(() => {
        if (store.getState().CinemaListReducer.list.length === 0) {
            store.dispatch(getCinemaListAction())
        } else {
            console.log('数据已被缓存,无需重复向后端获取')
        }

        let unsubscribe = store.subscribe(() => {
            setcinemaList(store.getState().CinemaListReducer.list)
        })

        return () => {
            unsubscribe()
        }
    }, [])

    const getCinemaList = useMemo(() => {
        return (
            cinemaList.filter(item => item.name.includes(myText) ||
                item.synopsis.includes(myText))
        )
    }, [cinemaList, myText])
    return (
        <div>
            <input value={myText} onChange={evt => {
                setMyText(evt.target.value)
            }}/>
            {
                getCinemaList.map(item =>
                    <dl key={item.filmId} style={{padding: '10px'}}>
                        <dt>{item.name}</dt>
                        <dd style={{fontSize: '12px', color: 'gray'}}>{item.synopsis}</dd>
                    </dl>
                )
            }
        </div>
    )
}

开发者工具

Redux DevTools Extension(浏览器插件)

代码示例:

//src/redux/store.js

import {applyMiddleware, combineReducers, legacy_createStore as createStore, compose} from 'redux'
import CityReducer from "./reducers/CityReducer";
import TabbarReducer from "./reducers/TabbarReducer";
import CinemaListReducer from "./reducers/CinemaListReducer";
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

const reducer = combineReducers({
    CityReducer,
    TabbarReducer,
    CinemaListReducer
})

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

const store = createStore(reducer,
    composeEnhancers(applyMiddleware(reduxThunk, reduxPromise))
)

export default store

React-Redux

  1. UI组件
    • 仅负责UI呈现,不带有任何的业务逻辑
    • 没有状态,所有数据都由参数提供
    • 不使用任何Redux的API
  2. 容器组件
    • 负责管理数据和业务逻辑,不负责UI呈现
    • 带有内部状态
    • 使用Redux的API
  3. HOC
    • 不仅仅是一个方法,而是一个组件工厂
    • 获取低阶组件,生成高阶组件
  4. 原理
    • connect是HOC
    • Provider组件可以让容器组件拿到state

代码示例:

// src/views/NotFound.jsx

import React, {useEffect} from "react";

function NotFound(props) {
    useEffect(() => {
        console.log(props)
    }, [props])

    return (
        <div>
            <h1>404</h1>
        </div>
    )
}

function myConnect(cb, obj) {
    let value = cb()

    return (MyComponent) => {
        return (props) => {
            console.log(props)

            return (
                <div style={{color: 'gray'}}>
                    <MyComponent {...value} {...props} {...obj}/>
                </div>
            )
        }
    }
}

export default myConnect(() => {
    return {
        a: 'a',
        b: 'b'
    }
}, {
    aa() {
    },
    bb() {
    }
})(NotFound)
// src/index.jsx

import React from "react";
import {createRoot} from "react-dom/client";
import App from "./App";
import {Provider} from "react-redux";
import store from "./redux/store";

createRoot(document.getElementById('root')).render(
    <Provider store={store}>  {/* 把store传给App组件(这样App与其子组件就不用引入store了) */}
        <App/>
    </Provider>
)
// src/App.jsx

import React, {Component} from "react";
import {BrowserRouter} from "react-router-dom";
import MRouter from "./router";
import Tabber from "./components/Tabber";
import './css/App.css'

export default class App extends Component {
    render() {
        return (
            <BrowserRouter>
                <MRouter/>
                <Tabber/>
                {/* 这里的Tabber组件必须不能引发render的刷新,否则会导致MRouter组件内的懒加载函数无限执行 */}
            </BrowserRouter>
        )
    }
}
// src/components/Tabber.jsx

import React from "react";
import {NavLink} from "react-router-dom";
import style from '../css/Tabber.module.css'
import {connect} from "react-redux";

function Tabber(props) {
    return (
        props.isShow && <footer className={style.tabbar}>
            <ul>
                <li>
                    <NavLink to={'/films'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>首页
                    </NavLink>
                </li>
                <li>
                    <NavLink to={'/cinemas'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>内容
                    </NavLink>
                </li>
                <li>
                    <NavLink to={'/centers'} className={({isActive}) =>
                        isActive ? style.myActive : ''
                    }>我的
                    </NavLink>
                </li>
            </ul>
        </footer>
    )
}

const mapStateToProps = (state) => {  // connect负责订阅
    return {  // 必须return给当前组件
        isShow: state.TabbarReducer.show  // 拿到公共状态
    }
}
export default connect(mapStateToProps)(Tabber)
// src/views/Detail.jsx

import React, {useEffect} from "react";
import {hidden, show} from "../redux/actioncreator/TabbarActionCreator";
import {connect} from "react-redux";

function Detail(props) {
    let {hidden, show} = props

    useEffect(() => {
        hidden()

        return () => {
            show()
        }
    }, [hidden, show])

    return (
        <div>
            Detail
        </div>
    )
}

const mapDispatchToProps = {
    show,
    hidden
}
export default connect(null, mapDispatchToProps)(Detail)  // 第一个实参:属性,第二个实参:回调函数
// src/views/City.jsx

import React, {useState} from "react";
import {useNavigate} from "react-router-dom";
import {connect} from "react-redux";

function City(props) {
    const navigate = useNavigate()
    const [list] = useState(['红魔馆', '博丽神社', '白玉楼', '地灵殿'])

    return (
        <div>
            <ul>
                {
                    list.map(item =>
                        <li key={item} onClick={() => {
                            props.change(item)
                            navigate(`/cinemas`)
                        }}>{item}
                        </li>
                    )
                }
            </ul>
        </div>
    )
}

const mapDispatchToProps = {
    change(item) {
        return {
            type: 'change-city',
            payload: item
        }
    }
}
export default connect(null, mapDispatchToProps)(City)
// src/views/Cinemas.jsx

import React, {useEffect} from "react";
import {useNavigate} from "react-router-dom";
import getCinemaListAction from "../redux/actioncreator/getCinemaListAction";
import {connect} from "react-redux";

function Cinemas(props) {
    const navigate = useNavigate()
    let {getCinemaListAction, list} = props

    useEffect(() => {
        if (list.length === 0) {
            getCinemaListAction()
        }  // 这个时候打印已缓存报告已经没有意义了,因为执行了两次
    }, [getCinemaListAction, list])

    return (
        <div>
            <div style={{overflow: 'hidden'}}>
                <div onClick={() => {
                    navigate(`/city`)
                }} style={{float: 'left'}}>{props.cityName}
                </div>
                <div onClick={() => {
                    navigate(`/cinemas/search`)
                }} style={{float: 'right'}}>搜索
                </div>
            </div>
            {
                props.list.map(item =>
                    <dl key={item.filmId} style={{padding: '10px'}}>
                        <dt>{item.name}</dt>
                        <dd style={{fontSize: '12px', color: 'gray'}}>{item.synopsis}</dd>
                    </dl>
                )
            }
        </div>
    )
}

const mapStateToProps = (state) => {
    return {
        list: state.CinemaListReducer.list,
        cityName: state.CityReducer.cityName
    }
}
const mapDispatchToProps = {
    getCinemaListAction
}
export default connect(mapStateToProps, mapDispatchToProps)(Cinemas)

持久化

代码示例:

//src/redux/store.js

import {applyMiddleware, combineReducers, legacy_createStore as createStore, compose} from 'redux'
import CityReducer from "./reducers/CityReducer";
import TabbarReducer from "./reducers/TabbarReducer";
import CinemaListReducer from "./reducers/CinemaListReducer";
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

import {persistStore, persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage'

const persistConfig = {
    key: 'root',
    storage,
    whitelist: ['CityReducer'],  // 持久化白名单(列表里的数据不会被持久化到本地存储空间,未列入列表的数据会被当作黑名单处理)
    blacklist: ['CinemaListReducer']  // 持久化黑名单(列表里的数据不会被持久化到本地存储空间)
}

const reducer = combineReducers({
    CityReducer,
    TabbarReducer,
    CinemaListReducer
})

const persistedReducer = persistReducer(persistConfig, reducer)

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

const store = createStore(persistedReducer,
    composeEnhancers(applyMiddleware(reduxThunk, reduxPromise))
)

let persistor = persistStore(store)

export {store, persistor}
// src/index.jsx

import React from "react";
import {createRoot} from "react-dom/client";
import App from "./App";
import {Provider} from "react-redux";
import {store, persistor} from "./redux/store";

import {PersistGate} from 'redux-persist/integration/react'  // 持久化网关

createRoot(document.getElementById('root')).render(
    <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
            <App/>
        </PersistGate>
    </Provider>
)
// src.views/Search.jsx

/*
import {store} from "../redux/store";
*/

redux-saga

异步管理

代码示例:

// src/redux-saga/Generator.js

// function* test() {  // *:生成器,可以用async/await替代
//     console.log(111)
//     let input01 = yield 'aaa'
//     console.log(222, input01)
//     let input02 = yield 'bbb'
//     console.log(333, input02)
//     let input03 = yield 'ccc'
//     console.log(444, input03)
// }
//
// let myTest = test()
//
// let res01 = myTest.next()  // 执行函数,走到下一个yield就会暂停(第一次执行时传过去的值是无效的)
// console.log(res01)
// let res02 = myTest.next('res02')
// console.log(res02)
// let res03 = myTest.next('res03')
// console.log(res03)
// let res04 = myTest.next('res04')  // 多出的next会返回undefined与true
// console.log(res04)

// async function a() {
//     let res1 = await featch()  // 从上往下传递
//     let res2 = await featch()
//     let res3 = await featch()
//     console.log(res3)
// }

// function* func() {
//     setTimeout(() => {
//         console.log('func01')
//         hFunc.next()
//     }, 1000)
//     yield
//     setTimeout(() => {
//         console.log('func02')
//         hFunc.next()
//     }, 1000)
//     yield
//     setTimeout(() => {
//         console.log('func03')
//     }, 1000)
//     yield
// }
//
// let hFunc = func()
//
// hFunc.next()

// 可执行生成器
function getData01() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('data01')
        }, 1000)
    })
}

function getData02() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('data02')
        }, 1000)
    })
}

function getData03() {
    return new Promise((resolve) => {  // 与then配合
        setTimeout(() => {
            resolve('data03')
        }, 1000)
    })
}

function* get() {
    let f1 = yield getData01()
    console.log(f1)
    let f2 = yield getData02(f1)
    console.log(f2)
    let f3 = yield getData03(f2)
    console.log(f3)
}

function run(fn) {
    let g = fn()

    function next(data) {
        let result = g.next(data)
        if (result.done) {
            return result.value
        }
        result.value.then(res => {
            next(res)
        })
    }

    next()
}

run(get)
// src/App.jsx

import React from "react";
import {createRoot} from "react-dom/client";
import App from "./redux-saga/App";

createRoot(document.getElementById('root')).render(<App/>)
// src/redux-saga/App.jsx

import React from "react";
// import "./Generator";
import store from "./redux/store";

export default function App() {
    return (
        <div>
            <button onClick={() => {
                if (store.getState().list.length === 0) {
                    store.dispatch({
                        type: 'get-list01',
                    })
                } else {
                    console.log('数据已被缓存:',store.getState().list)
                }
            }}>Click-ajax-异步缓存01
            </button>
            <button onClick={() => {
                if (store.getState().list02.length === 0) {
                    store.dispatch({
                        type: 'get-list02',
                    })
                } else {
                    console.log('数据已被缓存:',store.getState().list02)
                }
            }}>Click-ajax-异步缓存02
            </button>
        </div>
    )
}
// src/redux-saga/redux/store.js

import {applyMiddleware, legacy_createStore as createStore} from "redux";
import reducer from "./reducer";
import createSagaMiddleware from 'redux-saga'
import watchSaga from "./saga";

const SagaMiddleware = createSagaMiddleware()  // saga对象

const store = createStore(reducer, applyMiddleware(SagaMiddleware))

SagaMiddleware.run(watchSaga)  // saga任务

export default store
// src/redux-saga/redux/saga.js

import {takeEvery} from 'redux-saga/effects'
import {getList01} from "./saga/saga01";
import {getList02} from "./saga/saga02";

function* watchSaga() {  // 合并
    // yield all([watchSaga01(), watchSaga02()])
    yield takeEvery('get-list01', getList01)  // 第二种写法
    yield takeEvery('get-list02', getList02)
}

export default watchSaga
// src/redux-saga/redux/saga/saga01.js

import {call, put} from 'redux-saga/effects'

function getListAction01() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(['aaa', 'bbb', 'ccc'])
        }, 1000)
    })
}

function* getList01() {
    let res = yield call(getListAction01)
    yield put({
        type: 'change-list01',
        payload: res
    })
}

// function* watchSaga01() {
//     // while (true) {
//     //     yield take('get-list01')
//     //     yield fork(getList01)
//     // }
//     yield takeEvery('get-list01', getList01)
// }

// export default watchSaga01
export {getList01}
// src/redux-saga/redux/saga/saga02.js

import {takeEvery, call, put} from 'redux-saga/effects'

function getListAction02_1() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(['ddd', 'eee', 'fff'])
        }, 1000)
    })
}

function getListAction02_2(data) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve([...data, 'ggg', 'hhh', 'yyy'])
        }, 1000)
    })
}

function* getList02() {
    let res01 = yield call(getListAction02_1)  // 阻塞调用
    let res02 = yield call(getListAction02_2, res01)
    yield put({
        type: 'change-list02',
        payload: res02
    })
}

// function* watchSaga02() {
//     // while (true) {
//     //     yield take('get-list02')
//     //     yield fork(getList02)
//     // }
//     yield takeEvery('get-list02', getList02)
// }

// export default watchSaga02
export {getList02}
// src/redux-saga/redux/reducer.js

function reducer(prevState = {
    list: [],
    list02: []
},action={}){
    let newState = {...prevState}

    switch (action.type) {
        case 'change-list01':
            console.log(action)
            newState.list = action.payload
            return newState
        case 'change-list02':
            console.log(action)
            newState.list02 = action.payload
            return newState
        default:
            return prevState
    }
}

export default reducer