React
应用状态的管理
Redux
- Flux
- 结构思想
- 专门解决软件的架构问题,与MVC架构是同一类东西
- 更加简单与清晰
- 本身存在多种实现方式
- Facebook_Flux
- 用来构建客户端Web应用的应用架构
- 利用单向数据流的方式结合React中的视图组件
- 低学习成本
- 流程
- 客户端访问view
- View发出用户的Action
- Dispatcher收到Action,要求Store进行相应的更新
- Store更新后,发出一个change事件
- View收到change事件后,更新页面
- Redux
- 应用状态的管理(订阅发布模式),Redux的临时数据存储于内存中(刷新页面后就会销毁)
- 用一个单独的常量状态树(State对象)保存这一整个应用的状态,这个对象不能被直接改变
- 当一些数据变化了,一个新的对象就会被创建(使用actions和reduces),这样就可以进行数据追踪
- 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>
)
}
}
中间件
- 原理
- dispatch时会给每个redux-store的子组件发送数据,直到数据匹配上
- 原因
- 如果不是从正常的DOM进入页面(地址栏跳转),那么store跨组件不会产生缓存
- 解决方式
- 产生缓存的逻辑区块提取出来写成组件
代码示例:
// 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
- UI组件
- 仅负责UI呈现,不带有任何的业务逻辑
- 没有状态,所有数据都由参数提供
- 不使用任何Redux的API
- 容器组件
- 负责管理数据和业务逻辑,不负责UI呈现
- 带有内部状态
- 使用Redux的API
- HOC
- 不仅仅是一个方法,而是一个组件工厂
- 获取低阶组件,生成高阶组件
- 原理
- 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