前言
相信很多人都在使用 redux 作为前端状态管理库进去项目开发,但仍然停留在“知道怎么用,但仍然不知道其核心原理”的阶段,接下来带大家分析一下 redux 和 react-redux 两个库的核心思想和 API。
redux
1.为什么要使用 redux?
随着互联网的高速发展,我们的应用变得越来越复杂,进行导致我们的组件之间的关系也变得日趋复杂,往往需要将状态父组件 -> 子组件 -> 子子组件 -> 又或者只是简单的 父组件与子组件之间的 props 传递也会导致我们的数据流变得难以维护,因为二次开发者不在熟悉项目的情况下无法第一时间确定数据来源是由谁发起的。使用 redux 之后,所有的状态都来自于 store 中的 state,并且 store 通过 react-redux 中的 Provider 组件可以传递到 Provider 组件下的所有组件,也就是说 store 中的 state 对于 Provider 下的组件来说就是全局的。
2.redux 的核心原理是什么?
1.将应用的状态统一放到 state 中,由 store 来管理 state。
2.reducer 的作用是返回一个新的 state 去更新 store 中对用的 state。
3.按 redux 的原则,UI 层每一次状态的改变都应通过 action 去触发,action 传入对应的 reducer 中,reducer 返回一个新的 state 更新 store 中存放的 state,这样就完成了一次状态的更新
4.subscribe 是为 store 订阅监听函数,这些订阅后的监听函数是在每一次 dipatch 发起后依次执行
5.可以添加中间件对提交的 dispatch 进行重写
3.redux 的 api 有哪些?
**1.**createStore 创建仓库,接受 reducer 作为参数
2.bindActionCreator 绑定 store.dispatch 和 action 的关系
3.combineReducers 合并多个 reducers
4.applyMiddleware 洋葱模型的中间件,介于 dispatch 和 action 之间,重写 dispatch
5.compose 整合多个中间件
接下来我们来依次实现 createStore、bindActionCreator、combineReducers、applyMiddleware、compose
createStore 的实现
function createStore (reducer, enhancer) {
if (enhancer) {
enhancer(createStore)(reducer)
}
let currentState = {} //这就是前面提到的store管理的state
let currentListener = [] //这是前面提到的为store添加监听函数
function subscribe (func) {
currentListener.push(func)
}
function getState () {
return JSON.parse(JSON.stringify(state));
}
funtion dispatch (action) {
currentState = reducer(currentState, action)
currentListener.map(listen => listen()) //每当发生依次dispatch后都会遍历监听函数并执行
return action
}
return {
subscribe,
getState,
dispatch
}
}
复制代码
注意: createStore 并没有直接返回 store 中存放的 state,而是返回一个函数 getState 来获取 state,当我们调用 getState 去获取 state 时,需要返回一个 state 的复制品,也就是需要返回一个深拷贝 state 之后对象,这样可以避免 state 值的非法篡改,因为如何直接返回 state 的话,只需通过 state[key] = xxxx 就能对 state 进行修改,违背了 redux 只能通过 dispatch(action)去更新 state
bindActionCreator 的实现
function bindActionCreator (creators, dispatch) {
let bound = {}
Object.keys(creators).map(key =>{
const creator = creators[key]
bound[key] = (..args) => dispach(creator(...args))
})
return bound
}
复制代码
bindActionCreator 是为 action 函数外面包一层 dispatch,这样在进行 action 发起时无需再手动 dispatch 了,
combineReducers 的实现
function combineReducers (reducers) {
const reducersKeys = Object.keys(reducers),
finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
if (typeof reducers[i] === 'function') {
finalReducers[i] = reducers[i]
}
}
const finalReducersKeys = Object.keys(finalReducers)
return function combination (state={}, action) {
let hasChanged = false,
nextState = {}
for (let i = 0; i < finalReducersKeys.length; i++) {
const key = finalReducersKeys[i],
reducer = finalReducers[key],
preStateKeys = state[key],
nextStateKeys = reducer(preState, action),
nextState[key] = nextStateKeys
hasChanged = hasChanged || preStateKeys !== nextStateKeys
}
return hasChanged ? nextState : state
}
}
复制代码
applyMiddleware 的实现
function applyMiddleware (...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let { getState, dispatch } = store
const middlewateApi = {
getState,
dispatch: (...args) => dispatch(...args)
}
const middlewareChain = middlewares.map(middleware => middlware(middlewareApi))
dispatch = compose(...middlewareChanin)(dispatch)
return {
store,
dispatch
}
}
}
复制代码
compose 的实现
function compose (...funcs) {
if(funcs.length === 0) {
return args => args
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce( (ret, item) => (...args) => ret(item(...args)) )
}
复制代码
compose 是整合多个中间件的情况,这里使用 reduce 对用传入的中间件进行累加执行
react-redux
1.为什么要使用 react-redux?
回答这个问题,只需要明确我们的最终目的:能在 react 组件中实现对 store 中的 state 进行获取、修改、监听,而从上述内容可以知道,createStore 会给我们返回 getState、dispatch、subscribe 来进行获取 state、修改 state、监听 state 变化,而我们现在要做的就是把这个三个函数传递给 react 组件就可以了,所以我们就需要 react-redux 来帮助我们
参考 React面试题详细解答
2.react-redux 的核心原理是什么?
1.将 Provider 高阶组件包裹在组件的最外层,并且将创建的 store 传入 Provider 高阶组件中,
然后再 Provider 高阶组件内部获取传入的 store 并将它添加到 Provider 高阶组件的 context 上下文中,context 是 react 组件特有的一种不用手动一层层传递 props 就能在组件树中传递数据的方式,这样就实现了 store 相对于 react 组件的全局化,所有组件都能对 store 进行修改,获取,监听了
2.虽然 Provider 下的组件都拥有可以操作 store 的能力了,但是由于倘若我们要在每一个组件都从 context 中去获取 store 会造成大量代码冗余,还有一点就是即使你能在 react 组件中能够操作 store 了,但是当你 dispatch 一个 action 之后,store 中的 state 虽然更新了,但是并不会触发组件中的 render 函数去渲染新的数据,所以我们就需要通过 react-redux 另一个高阶组件 connect
来做集中处理。connect 组件接受一个 component 组件返回一个新的 component 组件,在 connect 最终返回的组件中获取 store 并将 store 设置为当前组件的 state,并且在 connect 返回的组件中的 componentDidMount 周期函数中调用 subscribe 给 store 绑定监听函数,而这个监听函数就是负责当前 store 中的 state 发生改变时,通过 this.setState 来触发组件的 render 函数的调用,最终达到 store 中的 state 与 UI 中 state 同步的问题
3.react-redux 有哪些 API?
1.Provider 组件通过 context 的方式将 store 注入应用
2.Connect 高阶组件,获取并监听 store,然后根据 store state 和组件自身 props 计算得到新 props,注入该组件,并且可以通过监听 store,比较计算出的新 props 判断是否需要更新组件
Provider 的实现
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Provider extends Component {
constructor (props) {
super(props)
this.store = props.store
}
static propTypes = {
store: PropTypes.object
}
getChildContext () {
return { store: this.store }
}
render () {
return this.props.children
}
}
复制代码
若存在疑问请回顾上面 react-redux 的原理分析
connect 的实现
import React, { Component } from 'react'
import PropTyeps from 'prop-types'
import { bindActionCreator } from 'redux'
const connect = (mapStateToProps, dispatchStateToProps) => (WrapperComponent) => class Connect extends Component {
construtor (props, context) {
super(props, context)
this.store = context.store
this.state = {
props: {}
}
}
static contextTypes = {
store: PropTypes.object
}
componentDidMount () {
this.store.subscribe(this.update)
this.update()
}
update = () => {
const { getState, dispatch } = this.store
const stateProps = mapStateToProps(getState())
const dispatchProps = bindActionCreator(dispatchStateToProps, dispatch)
this.setState({
...this.state.props,
...stateProps,
...dispatchProps,
})
}
render () {
const { props } = this.state
return <WrapperComponent {...props} />
}
}
复制代码
若存在疑问请回顾上面 react-redux 的原理分析
评论