react深入 - 手写实现react-redux api

2023-06-25,,

简介:简单实现react-redux基础api

react-redux api回顾

<Provider store>
把store放在context里,所有子组件可以直接拿到store数据


使组件层级中的 connect() 方法都能够获得 Redux store
根组件应该嵌套在 &lt;Provider&gt; 中

ReactDOM.render(
&lt;Provider store={store}&gt;
&lt;MyRootComponent /&gt;
&lt;/Provider&gt;,
rootEl
) ReactDOM.render(
&lt;Provider store={store}&gt;
&lt;Router history={history}&gt;
&lt;Route path="/" component={App}&gt;
&lt;Route path="foo" component={Foo}/&gt;
&lt;Route path="bar" component={Bar}/&gt;
&lt;/Route&gt;
&lt;/Router&gt;
&lt;/Provider&gt;,
document.getElementById('root')
)

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
链接组件和数据,把redux中的数据放到组件的属性中

[mapStateToProps(state, [ownProps]): stateProps] (Function)


如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并

如果你省略了这个参数,你的组件将不会监听 Redux store
ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用,被重新计算
mapStateToProps 函数的第一个参数是整个Redux store的state,它返回一个要作为 props 传递的对象。它通常被称作 selector (选择器)。 可以使用reselect去有效地组合选择器和计算衍生数据.
注意:如果定义一个包含强制性参数函数(这个函数的长度为 1)时,ownProps 不会传到 mapStateToProps


const mapStateToProps = (state, ownProps) =&gt; {
return {
active: ownProps.filter === state.visibilityFilter
}
}

[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function)


Object: 它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数(action creator),会被当作 Action creator ,返回的 Action 会由 Redux 自动发出, Function: 会得到dispatch和ownProps(容器组件的props对象)两个参数(此时可能用到Redux 的辅助函数 bindActionCreators())

省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中,你可以this.props.dispatch调用
指定了该回调函数中第二个参数 ownProps,该参数的值为传递到组件的 props,而且只要组件接收到新 props,mapDispatchToProps 也会被调用

eg:


connect(mapStateToProps, {
hideAdPanel,
pushAdData,
})(AdPanel) function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch)
}
}

知识点补充 - React高阶组件(Higher-Order Components)

高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件
高阶组件就是一个没有副作用的纯函数

使用场景:两个组件大部分代码都是重复的+且更好的封闭性,不需要关注数据的获取


import React, {Component} from 'react' class Welcome extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
} componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
} render() {
return (
&lt;div&gt;welcome {this.state.username}&lt;/div&gt;
)
}
} export default Welcome; import React, {Component} from 'react' class Goodbye extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
} componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
} render() {
return (
&lt;div&gt;goodbye {this.state.username}&lt;/div&gt;
)
}
} export default Goodbye;

welcome和goodbye组件相似,只能获取的数据不一样,用高阶组件,提取公共部分


import React, {Component} from 'react' export default (WrappedComponent) =&gt; {
class NewComponent extends Component {
constructor() {
super();
this.state = {
username: ''
}
} componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
} render() {
return &lt;WrappedComponent username={this.state.username}/&gt;
}
} return NewComponent
} 简化welcome和goodbye import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername'; class Welcome extends Component { render() {
return (
&lt;div&gt;welcome {this.props.username}&lt;/div&gt;
)
}
} Welcome = wrapWithUsername(Welcome); export default Welcome;

此时,理解react-redux 的connect就好理解了


ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component); // connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component);

provider实现


import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose} from 'redux'
import thunk from 'redux-thunk'
import { counter } from './index.redux'
// import { Provider } from 'react-redux'
// 换成自己的Provider实现
import { Provider } from './self-react-redux'
import App from './App' const store = createStore(counter, compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f =&gt; f
))
ReactDOM.render(
(
&lt;Provider store={store}&gt;
&lt;App /&gt;
&lt;/Provider&gt;
),
document.getElementById('root'))

./self-react-redux


import React from 'react'
import PropTypes from 'prop-types' export function connect(){
} class Provider extends React.Component{
static childContextTypes = {
store: PropTypes.object
}
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render(){
return this.props.children
}
}

connect实现

demo


import React from 'react'
// import { connect } from 'react-redux'
import { connect } from './self-react-redux'
import { addGun, removeGun, addGunAsync } from './index.redux' @connect(
// 你要state什么属性放到props里
state=&gt;({num:state.counter}),
// 你要什么方法,放到props里,自动dispatch
{ addGun, removeGun, addGunAsync }
)
class App extends React.Component{
render(){
return (
&lt;div&gt;
&lt;h1&gt;现在有机枪{this.props.num}把&lt;/h1&gt;
&lt;button onClick={this.props.addGun}&gt;申请武器&lt;/button&gt;
&lt;button onClick={this.props.removeGun}&gt;上交武器&lt;/button&gt;
&lt;button onClick={this.props.addGunAsync}&gt;拖两天再给&lt;/button&gt;
&lt;/div&gt;
)
}
} export default App

./self-react-redux.js



// 高阶组件的写法
export function connect(maoStateToProps, mapStateToProps) {
return function(WrapComponent) {
return class ConnectComponent extends React.Component{ }
}
} import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreator } from './self-redux'
// 使用简写形式
// connect负责链接组件,给到redux里的数据放在组件的属性里
// 1. 负责接收一个组件,把state里的一些数据放进去,返回一个组件
// 2. 数据变化的时候,能通知组件
export const connect = (
mapStateToProps = state =&gt; state,
mapDispatchToProps ={}
) =&gt; (WrapComponent) =&gt; {
return class ConnectComponent extends React.Component {
static contextTypes = {
store: PropTypes.object
}
constructor(props, context){
super(props, context)
this.state = {
props: {}
}
}
// 2 实现了mapStateToProps
componentDidMount() {
const { store } = this.context
store.subscribe(() =&gt; this.update())
this.update()
}
update() {
const { store } = this.context
// store.getState()这就是为什么mapStateToProps函数里面能拿到state
const stateProps = mapStateToProps(store.getState())
// 方法不能直接给,因为需要dispatch
/**
function addGun() {
return { type: ADD_GUN }
}
直接执行addGun() 毫无意义
要 addGun = () =&gt; store.dispatch(addGun()) 才有意义,其实就是把actionCreator包了一层
bindActionCreators在手写redux api实现了
*/
const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
// 注意state的顺序问题会覆盖
this.setState({
props: {
...this.state.props,
...stateProps,
...dispatchProps,
}
})
}
// 1
render() {
return &lt;WrapComponent {...this.state.props}&gt;&lt;/WrapComponent&gt;
}
}
}

./self-redux.js


// creators: {addGun, removeGun, addGunAsync}
// creators[v]:addGun(参数)
// 返回:(参数) =&gt; dispatch(addGun(参数))
function bindActionCreator(creator, dispatch) {
return (...args) =&gt; dispatch(creator(...args))
} export function bindActionCreators(creators, dispatch) {
let bound = {}
Object.keys(creators).forEach( v =&gt; {
let creator = creators[v]
bound[v] = bindActionCreator(creator, dispatch)
})
return bound
}
// 简写
export function bindActionCreators(creators, dispatch) {
return Object.keys(creators).reduce((ret, item) =&gt; {
ret[item] = bindActionCreator(creators[item], dispatch)
return ret
}, {})
}

原文地址:https://segmentfault.com/a/1190000016759675

react深入 - 手写实现react-redux api的相关教程结束。

《react深入 - 手写实现react-redux api.doc》

下载本文的Word格式文档,以方便收藏与打印。