Time: 2019-11-27
之前在比赛中写前端工程时,使用了如下几个关键的技术点:
- redux,负责应用状态管理
- reux-thunk,负责中间件管理
- 容器组件
- 展示组件
- reducer
- action
首先从redux讲起。
redux的设计原则是用单个字典来存储应用的状态树store,通过action来传递修改状态的信息包,具体的修改是通过定义的reducers函数来执行。
也即我们需要将actions --> reducers --> store三者串联起来。
这三者是为了搭建起来一个自动响应的框架,搭建完是不动的,需要外界dispatch
某个action
来触发,相当于现在定义了一个状态机,外界的输入进来才会触发响应。
store
会在应用的index.js
中绑定到全局。
import { store } from './_store'
const rootElement = document.getElementById("root")
console.log("store状态:", store.getState()) // 可以拿到状态
// store已经绑定到了整个App
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, rootElement
)
一般我们会定义一个store.js
用于表示状态,我们会
关于组件,我们抽象为两层组件:
- 容器组件
- 展示组件
容器组件负责将状态和要触发的动作绑定到展示组件,展示组件只需要关注自己如何通过HTML + CSS元素表达想要展示的内容,容器组件会把要用到的数据和要dispatch
的动作注入到展示组件的props
。
举个例子:
import { Blacklist } from '../../_pages/company'
import { connect } from 'react-redux'
import { listBlacklistAsync } from '../../_actions/company.actions'
// 容器负责注入状态显示和行为定义,作为显示组件的props
// store状态映射到组件
// store已经在index.js中绑定到应用,这里直接取用即可
// blacklist是用于props的键
const mapStateToProps = state => ({
blacklist: state.company.blacklist,
fetchStatus: state.company.fetchStatus,
})
// 注入到展示组件的props中的回调方法
const mapDispatchToProps = dispatch => ({
listBlacklistAsync: () => dispatch(listBlacklistAsync())
})
// 连接到展示组件
export default connect(
mapStateToProps,
mapDispatchToProps
)(Blacklist)
容器组件和展示组件通过connect
方法绑定到一起。背后store
状态机已经搭建好了。
展示组件调用容器组件准备好的属性和方法:
componentDidMount() {
this.props.listBlacklistAsync() // 先分发查询修改状态树
}
render() {
// this.createData(`0xc83b2cf766d3165acc2fc9164641380088defd1b`, "xx于xx年xx月xx日未还款金额xx元"),
let rows = []
// 需要根据状态判定是否已经结束获取
console.log("组件内显示黑名单:", this.props.blacklist)
if (this.props.blacklist !== undefined) {
// this.props.blacklist.map((item, i) => {
// rows.push(this.createData(item.weid, item.record))
// })
rows = this.props.blacklist
}
const { classes } = this.props
if (this.props.fetchStatus === FETCH_STATUS.FETCH_BEGIN) {
return (
<div align="center">
<br />
<CircularProgress />
</div>
)
}
....
关于action的定义:
程序需要调用的外部数据,我们用fetch
获取,获取到的数据可以用到action
定义中,外部数据的获取是为了修改组件的状态,而组件的状态现在是由store接管。
看一个action定义的例子:
// dispatch action
const listLoanRequestRecords = (json) => {
return {
type: types.LOAN_REQUEST_RECORDS,
payload: json.data
}
}
// 定义行为,行为会在容器组件中设定给props,具体显示组件中择时触发
const listLoanRequestRecordsAsync = () => {
return dispatch => {
dispatch(fetchBegin())
// 通过服务拿到数据,最后作为action的payload,action的payload用于修改store,修改行为由reducer执行
companyServices.listLoanRequestRecords("WeBank").then(
json => {
if (json.status === 200) {
dispatch(fetchSuccess())
}
dispatch(listLoanRequestRecords(json))
}
)
}
}
action是在reducers
中使用。所以我们看下reducers的定义:
// 获取用户请求列表
export function loanRequestRecordsReducer(state = [], action) {
switch (action.type) {
case types.LOAN_REQUEST_RECORDS:
return [
...state,
...action.payload
]
default:
return state
}
}
reducer是纯函数,接收当前状态的值然后根据action带来的动作类型和数据包修改store。
不要看参数state=[]
就认为store是空,这是默认参数,拿到的是当前的数据。
另外,reducer指定的键名就是store字典树的键名。
比如在store中:
// 会根据定义的键值生成对应的以第一层键指定的store
const rootReducer = combineReducers({
company: companyReducer,
government: governmentReducer,
user: userReducer,
common: commonReducer
})
一级键就是这四个值:company
, government
, user
, common
。
这个之前写过一个笔记:
在combineReducers用的是对象,按照key来标记对应的是哪个reducer,键是state的属性名!
这太重要了,我太喜欢这个了,解决了我的困惑。
下一个困惑是,state给空,每次调用都是在干嘛?
是根据分离的reducer对应的属性直接抽出来一段数据过来当参数吗?
如果修改的话就在容器组件中dispatch修改方法
如果只是读取,就在容器组件中getState传到值属性里。
Q: fetch返回的Promise如何触发状态更新?
A: fetch(url).then(response => response.json()).then(json => console.log(json))
这样就串起来应用状态和数据读取的全貌了。
有点乱,后续有时间再梳理。
网友评论