React-Redux 实现伪代码
前言
React 相信大家都有过接触,React 一套MVC框架,根据 React 的框架诞生的一套使用方便的 处理数据状态的插件,redux。redux 也并不是就只是单纯使用于React , Vue 同样适用,它只是解决了一种处理数据的方式。
一、Redux
-
1.1 设计思想
Redux 将组价中的状态,数据存储到顶层 Store 里,通过 provider 从顶层注入。
组件通过 dispatch 触发 action, action 去覆盖之前的 reducer 中的 state 值。
此时 store 值做到了更替,所以传入组件中的 state 值修改,页面 View 更新。
-
1.2 三大原则
整个应用用只有一个store, 其内部state tree 存储整个应用 state。
修改数据通过 action 去派发。
单一数据源让 react 组件之间的 数据通信简单方便,更有利于管理。
- 1.3 createStore
- 1.3.1 store
import { create } from 'react';
function reducer() {}
let store = createStore(reducer)
通过 creatstore 创建一个 store, 创建的store 有以下几个方法:
* store.getState() // 获取最新state
* store.dispatch() // 触发 aciton
* store.subscribe() // 订阅 store 中 state 变化
- 1.3.2 reducer
reducer 是一个纯函数,作用只是接收数据,数据覆盖,其他什么也不操作,state 值替换作用。
const reducer = (state={count: 0}, action) => {
switch (action.type) {
case ADD:
return { count: state.count + 1 }
case MINUS:
return { count: state.count - 1 }
default:
return state;
}
}
- 1.3.3 getState、dispatch、subscribe
import React from 'react'
import store from '../store'
class Counter extends React.Component{
constructor(props){
super(props)
this.state = {
number: store.getState().count
}
}
render () {
return <div>
<p>{this.state.number}</p>
<button onClick={() => store.dispatch({type: 'ADD'})}>+</button>
<button onClick={() => store.dispatch({type: 'MINUS'})}>-</button>
</div>
}
}
export default Counter
```
可以看到,reducer 函数中已经接受到了 action, 此时 store 中的 state 已经发生了变化,而页面不更新的原因在于 Counter 没有订阅 store 中 state 的变化,可在代码中加入下面代码
```jsx
class Counter extends React.Component{
componentDidMount () {
this.unSubscribe = store.subscribe(() => {
this.setState({
number: store.getState().count
})
})
}
componentWillUnmount () {
this.unSubscribe && this.unSubscribe()
}
}
使用 store.subscribe 就可实现订阅,该方法接受一函数,当 store 中 state 中状态发生变化,就会执行传入的函数,同时 store.subscribe 方法返回一个函数,用于取消订阅。
至此,Counter组件已基本实现了。可能有些小伙伴发现应用首次加载后,控制台输出了
- 1.3.4 createStore 伪代码
function createStore(reducer){
let state
const listeners = []
// 返回最新的 state
function getState () {
return state
}
// 派发 action
function dispatch(action){
state = reducer(state, action)
listeners.forEach(listener => listener())
}
// 订阅,返回取消订阅函数
function subscribe(listener){
listeners.push(listener)
return function () {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}
// 获取state默认值
dispatch({type: "@@redux/INIT1.s.m.m.c.n"})
// 返回 store, 一个对象
return {
getState,
dispatch,
subscribe
}
}
export default createStore
- 1.4 combineReducers
-
1.4.1 原理及使用
当一个应用包含多个模块,将所以模块的 state 放在并不合理,更好的做法是按照模块进行划分,每个模块有各自的 reducer、action,最终通过 Redux 中的 combineReducers 合并成一个大的 reducer.
-
// src\store\reducers\index.js
import {combineReducers} from 'redux';
import counter1 from './counterReducer1';
import counter2 from './counterReducer2';
export default combineReducers({
x: counter1,
y: counter2
});
// src/store/reducers/counterReducer1.js
import * as types from '../action-types';
export default function (state= {count: 0},action){
switch(action.type){
case types.ADD1:
return state.count + 1;
case types.MINUS1:
return state.count - 1;
default:
return state;
}
}
// src/store/reducers/counterReducer2.js
import * as types from '../action-types';
export default function (state= {count: 0},action){
switch(action.type){
case types.ADD2:
return state.count + 1;
case types.MINUS2:
return state.count - 1;
default:
return state;
}
}
- 1.4.2 combineReducers 伪代码
function combineReducers(reducers){
// 返回合并之后的 reducer 函数
return function (state, action){
const nextState = {}
Object.keys(reducers).forEach(key => {
nextState[key] = reducers[key](state[key], action)
})
return nextState
}
}
- 1.5 总结
可以看出,在 React 组件中使用 store, 都需要手动去引入 store 文件, 手动订阅 store 中状态的变化,这是不合理的,接下来,我们看下 react-redux 是如何解决的
二、React-Redux
- 2.1 原理及使用
react-redux 提供一个 Provider 组件,通过 Provider 组件,可以向其子组件、孙组件传递 store, 而不需要每个组件都手动引入
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
在后代组件 Counter1 中,可使用 react-redux 提供 connect 函数,将 store 与 Counter1 组件的 props 进行关联
import React from 'react'
import { connect } from 'react-redux'
import action from '../store/actions/Counter1'
class Counter1 extends React.Component{
render () {
return <div>
<p>{ this.props.count }</p>
<button onClick={ this.props.add }>+</button>
<button onClick={ this.props.minus }>-</button>
</div>
}
}
const mapStateToProps = state => state
const mapDispatchToProps = {
...action
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter1)
从上面代码中,可以看出在 Counter1 组件内部,属性或方法都是通过 props 访问,我们完全可以将 Counter1 组件转换成函数组件(无状态组件),通过函数组件外部都是一个容器组件(有状态组件)进行包裹,所有 connect(mapStateToProps, mapDispatchToProps)(Counter1) 最终返回的就是一个容器组件,接下来我们看下如何手写一个 react-redux。
- 2.2 react-redux 伪代码
创建一个 ReactReduxContext 上下文对象
import React from 'react'
export const ReactReduxContext = React.createContext(null)
export default ReactReduxContext
在 Proveider 组件中,需要使用 ReactReduxContext 对象中提供的 Provider 组件
// src/react-redux/Provider.js
import React from 'react'
import {ReactReduxContext} from './Context'
class Provider extends React.Component{
constructor(props) {
super(props)
}
render () {
return <ReactReduxContext.Provider value={{ store: this.props.store }}>
{this.props.children}
</ReactReduxContext.Provider>
}
}
export default Provider
而 connect 方法,接收 mapStateToProps, mapDispatchToProps 两个参数,返回一个函数,返回的函数接收 自定义组件(例如 Counter1 ),函数执行后,返回最终的容器组件
// src/react-redux/connect.js
import React from 'react'
import {bindActionCreators} from 'redux'
import {ReactReduxContext} from './Context'
function connect(mapStateToProps, mapDispatchToProps) {
return function (WrappedComponent) {
// 返回最终的容器组件
return class extends React.Component{
static contextType = ReactReduxContext
constructor(props, context){
super(props)
this.state = mapStateToProps(context.store.getState())
}
shouldComponentUpdate() {
if (this.state === mapStateToProps(this.context.store.getState())) {
return false;
}
return true;
}
componentDidMount () {
this.unsubscribe = this.context.subscribe(() => {
this.setState(mapStateToProps(this.context.store.getState()))
})
}
componentWillUnmount (){
this.unsubscribe && this.unsubscribe()
}
render(){
const actions = bindActionCreators(
mapDispatchToProps,
this.context.store.dispatch
)
return <WrappedComponent {...this.state} {...this.props} {...actions}/>
}
}
}
}
export default connect
可以看出,connect 方法中,有 bindActionCreators 绑定 action 与 store.dispatch, 有订阅 store 中的 state 变化,这些都是我们只使用 redux ,需要在 react 组件中需要手动去写的,幸运的是,现在 react-redux 帮我们去干了.
总结
redux 主要应用在项目中的状态变化存储,组件之间的数据共享,当数据需要使用时,不存在层层传递到当前组件问题, 实现了类似于数据的传送门,哪里使用哪里取得。希望对您有所帮助吧。