从零搭建React+TypeScript的后台项目(三)
本章主要讲解redux在React项目中的应用。Redux官方文档。
一、基本概念
Action
action简单理解就是对象字面量。功能上来说就是把数据从应用传到store的有效载荷,和Vue中Mutation提交载荷类似。
有action创造函数,是一个生成action的方法。下面就是一个简单的同步actin:
export function setGlobalState(data: {}) { return {type: 'SET_GLOBAL_STATE', data} }
action生成后,store并没有生成或者发生变化,只是说明我要更新store了。怎么更新需要通过reducer函数来处理。
Reducer
reducer本质上是纯函数,接受oldState、action两个参数,返回newState。reducer告诉开发系统是怎么更新store的。
function global(state: Global, action: any) { switch(action.type) { case 'SET_GLOBAL_STATE': return action.data default: return InitGlobalState } }
reducer支持组合和拆分,可以根据项目业务合理进行组织。
Store
store就是项目中的数据流,将action和reducer联系起来。Redux应用只有一个store,起职责如下:
- 维持应用的 state;
- 提供
getState()
方法获取 state; - 提供
dispatch(action)
方法更新 state; - 通过
subscribe(listener)
注册监听器; - 通过
subscribe(listener)
返回的函数注销监听器。
下面我们初始化一个store
import { createStore } from 'redux' // 初始化store const Store = createStore( rootReducer, )
二、React中使用Redux
redux是一种状态管理通用实现方案,可以应用在许多地方,并不受库的限制。react-redux正是两者搭配起来实现的通用库。
yarn add react-redux
Provider全局注入store
然后通过react-redux提供的Provider组件实现store注入。
import Store from './store' import { Provider } from 'react-redux' ReactDOM.render( <Provider store={Store}> <App/> </Provider>, document.getElementById('root') as HTMLElement //类型断言 );
这样项目中的所有组件都能访问到store中state,并且通过dispatch来更新state。总之,store在项目中是单独存在的一个整体,作为数据源存在。而项目外部可以获取state,改变store中state的唯一方法就是dispatch(action)。
组件中使用store
下面我们一todo.tsx组件为例,介绍组件中是如何获取、更新state的。
import { connect } from 'react-redux' const mapStateToProps = (state:InitState) => { return { todos: state.todos } } const mapDispatchToProps = (dispatch:Dis) => { return { onAddTodo(value: string) { dispatch(addTodo(value)) } } }
Provider组件将store注入到组件中的本质,还是将store通过组件的props关联到每个组件上。接下来我们将两个map函数绑定到组件的属性上。
interface Props { todos: [] onAddTodo:(value: string) => void } interface State { value: string } class Todo extends React.Component<Props, State> { constructor(props: Props) { super(props) this.state = { value: '' } } handleAddTodo = () => { this.props.onAddTodo(this.state.value) this.setState({ value: '' }) } handleInputChange = (e: any) => { const { value } = e.target this.setState({ value }) } render () { return( <div> <Input value={this.state.value} placeholder="请输入清单项" onChange={this.handleInputChange}/> <Button onClick={this.handleAddTodo}>添加</Button> <ul> {this.props.todos.map((todo: any) => ( <li>{todo.text}</li> ))} </ul> </div> ) } } export default connect( mapStateToProps, mapDispatchToProps )(Todo)
这样我们就实现了在组件内部获取state,并且通过界面交互可以动态新增待办清单,更新store,最后实时渲染页面的功能。
三、redux高级用法
组合拆分reducer
之前有讲到可以拆分reducer,做到单一功能模块对应一个reducer。类似于Vuex中将store分成多个子modules。
redux提供的combineReducers方法即可接受多个reducer。
import { createStore, combineReducers } from 'redux' import Todo from './todo/reducers' import Ware from './ware/reducers' const rootReducer = combineReducers( Object.assign({}, Todo, Ware) )
然后将rootReducer传入createStore中即可。
action中间件
中间件MiddleWare可以在我们进行disatch时,更加细粒化的跟踪store的变化,并且允许我们建立异步action。
下面我们来看react-thunk中间件的作用,使用middleWare后,action创建函数除了返回action对象外,还能返回函数。这个函数会被react-thunk执行,在函数内部能执行更多操作,执行异步请求、dispatch action。
yarn add react-thunk
下面我们来修改下store/index.tsx文件:
import thunkMiddleware from 'redux-thunk' import { createStore, applyMiddleware, combineReducers } from 'redux' const Store = createStore( rootReducer, applyMiddleware( thunkMiddleware, ) )
现在我们的action创造函数中就能像下面这样写:
export function addTodo(text: string) { return { type: 'ADD_TODO', text } } export function asyncAddTodo(text: string) { return (dispatch:Dis, getState: Get) => { if (checkStoreTodo(getState(),text)) { // 在 thunk 里 dispatch 另一个 thunk! return dispatch(addTodo(text)) } else { // 告诉调用代码不需要再等待。 return Promise.resolve() } } }
还有react-logger提供的中间件,在本地开发时在进行dispatch时,能打印改动日志。不仅如此,我们还可以自定义middleWare,用来实现自己想要的逻辑效果。
现在redux在react中一些基本用法都介绍完毕了,react中的一些第三方库基本和TypeScript结合的很好,可能有时候回调函数的参数不知道如何进行类型定义。有个d.ts文件则能在开发时进行智能提示,这个文件主要是在ts文件以js文件发布后,用来标记js文件里面对应的类型。
TypeScript官方文档有兴趣的朋友可以看看。
题外话,redux使用起来有点复杂。安利另外一个状态管理方案MobX,并且在react得到很好的实现,真正做到了开箱即用,代码写到哪里就在哪里使用!
这是一个系列文章:
从零搭建React+TypeScript的后台项目(一)--构建基础React+TypeScript项目
从零搭建React+TypeScript的后台项目(二)--后台router实现方案
从零搭建React+TypeScript的后台项目(三)--Redux基本配置