4、一个简单的todolist案列(包含数据持久化)
4.0、redux&react-redux 项目结构
store index.js 创建一个store容器 views Lists——>这个名是看文件,语义化就好了 redux - Actions.js 返回一个或多个动作对象,一般为`{type:'',data:''}` - ActionsTypes.js 返回一个或多个常量,用做Actions和Reducer的type值 - Reducer.js 返回一个纯函数,用于处理动作对象 index.js 将actions和reducer函数集中导出(方便管理) index.jsx 容器组件和UI组件的结合体
数据持久化看src/store/index.js 、 src/router.js
4.1 src/store/index.js
此文件主要就是创建一个仓库
将需要用到的reducer文件引入
从本地存储中取值,放进createStore的第二个参数,初始值的作用
import { createStore, combineReducers,applyMiddleware } from 'redux'; import thunk = 'react-thunk' //支持异步 import { reducer as lists } from '../views/Lists/_index.js' const reducer = combineReducers({ lists }) let initList = JSON.parse(localStorage.getItem("LIST")) || []; export default createStore(reducer,initList,applyMiddleware(thunk));
4.2 src/router.js
此文件主要就是做一个根组件
通过provider向下传递store
通过subscrbe方法订阅store
getState方法获取store的值存入本地
import React from 'react'; import App from './App'; import { Provider } from 'react-redux'; import Store from './store' const StoreToken = Store.subscribe(() => { // Store.getState() // console.log(Store.getState()) window.localStorage.setItem("LIST", JSON.stringify(Store.getState())) }) //StoreToken() 这样可以取消订阅 const Router = () => { return ( <div> <Provider store={Store}> <App /> </Provider> </div> ) } export default Router
4.3 src/List/redux/actions.js
此文件主要是返回一个action动作对象
action动作对象必须要有一个type属性
import * as actiontypes from './actionTypes'; export const addList = (data) => ({ type: actiontypes.ADDList, data }) export const delList = (data) => ({ type: actiontypes.DELLIST, data })
4.4 src/List/redux/actionTypes.js
此文件主要是返回一个常量,目的是防止出现字母错误(默认是大写)
export const ADDList = "ADDLIST"; export const DELLIST = "DELLIST";
4.5 src/List/redux/reducer.js
此文件返回一个纯函数,用于接收dispatch传来的action,进行处理之后返回store
默认注入两个参数(preState,action)
preState需要设置初始值
reducer被第一次调用时,是store自动触发的,传递的preState是undefined
必须要有默认返回值
import * as actionTypes from './actionTypes'; const reducer = (preState = [], action) => { const { type, data } = action; switch (type) { case actionTypes.ADDList: const result = [ data, ...preState ] console.log(result) return result case actionTypes.DELLIST: preState.splice(data, 1) return [...preState] default: return preState } } export default reducer
4.6 src/List/_index.js
汇总action和reducer方便管理
actions文件只有一个
reducer文件或许会有多个
import * as actions from './redux/actions'; import reducer from './redux/reducer'; export { actions, reducer }
4.7-1 src/List/_index.js (类组件写法)
类组件中获取cannect中函数映射的值和函数都是通过props拿到的
import React,{Component} from 'react'; import { connect } from 'react-redux'; import * as actions from './redux/actions'; class View extends Component{ // 渲染函数 rLsits = () => { let { lists } = this.props; return lists.map((value, index) => ( <li key={index}>{value} <button onClick={()=>{this.delfn(index)}}>删除</button></li> )) } // 添加数据 send = () => { let val = this.inp.value; this.props.addLists(val) this.inp.value="" } // 删除数据 delfn = (index) => { this.props.delLists(index) } render() { return ( <div> <h2>这是一个class类组件操作数组数据的例子</h2> <input ref={value=>this.inp=value} type="text" /> <button onClick={()=>{this.send()}}>发送</button> {this.rLsits()} </div> ) } } // 写法一 mapDispatchToProps是一个对象 export default connect( state => ({ lists: state.lists }), { addLists: actions.addList, delLists: actions.delList } )(View) // 写法二 mapDispatchToProps是一个函数 // export default connect( // state => ({ lists: state.lists }), // (dispatch, ownProps) => ({ // addLists: data => { dispatch(actions.addList(data)) }, // delLists: data => { dispatch(actions.delList(data)) } // }) // )(View) // 写法三 mapDispatchToProps为null // 需要在UI组件内通过this.props.dispath传递action动作对象 // export default connect( // state => ({ lists: state.lists }), // null // )(View)
4.7-2 src/List/_index.js (函数组件写法)
函数组件的值是通过useSelector拿到的
函数组件的dispatch是通过useDispatch 拿到的
import React,{useRef} from 'react'; import { useSelector, useDispatch } from 'react-redux'; import * as actions from './redux/actions' const View = () => { let inp = useRef(); const state = useSelector(state=>state) const dispath = useDispatch() const rLsits = () => { console.log('我执行了') return state.lists.map((value, index) => ( <li key={index}>{value} <button onClick={()=>{delLists(index)}}>删除</button></li> )) } // 添加数据 const send = () => { let val = inp.current.value; dispath(actions.addList(val)) inp.current.value="" } // 删除数据 const delLists = (index) => { dispath(actions.delList(index)) } return ( <div> <h2>这是一个函数组件操作数组数据的例子</h2> <input ref={inp} type="text" /> <button onClick={send}>发送</button> {rLsits()} </div> ) } export default View;
【补】纯函数
一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)
必须遵守以下一些约束
不得改写数据
不会产生任何副作用,例如网络请求,输入和输出设备
不能调用Date.now()或者Math.random()等不纯的方法