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

react-redux api回顾

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


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

  1. ReactDOM.render(
  2. &lt;Provider store={store}&gt;
  3. &lt;MyRootComponent /&gt;
  4. &lt;/Provider&gt;,
  5. rootEl
  6. )
  7. ReactDOM.render(
  8. &lt;Provider store={store}&gt;
  9. &lt;Router history={history}&gt;
  10. &lt;Route path="/" component={App}&gt;
  11. &lt;Route path="foo" component={Foo}/&gt;
  12. &lt;Route path="bar" component={Bar}/&gt;
  13. &lt;/Route&gt;
  14. &lt;/Router&gt;
  15. &lt;/Provider&gt;,
  16. document.getElementById('root')
  17. )

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

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


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

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


  1. const mapStateToProps = (state, ownProps) =&gt; {
  2. return {
  3. active: ownProps.filter === state.visibilityFilter
  4. }
  5. }

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


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

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

eg:


  1. connect(mapStateToProps, {
  2. hideAdPanel,
  3. pushAdData,
  4. })(AdPanel)
  5. function mapDispatchToProps(dispatch) {
  6. return {
  7. todoActions: bindActionCreators(todoActionCreators, dispatch),
  8. counterActions: bindActionCreators(counterActionCreators, dispatch)
  9. }
  10. }

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

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

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


  1. import React, {Component} from 'react'
  2. class Welcome extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. username: ''
  7. }
  8. }
  9. componentWillMount() {
  10. let username = localStorage.getItem('username');
  11. this.setState({
  12. username: username
  13. })
  14. }
  15. render() {
  16. return (
  17. &lt;div&gt;welcome {this.state.username}&lt;/div&gt;
  18. )
  19. }
  20. }
  21. export default Welcome;
  22. import React, {Component} from 'react'
  23. class Goodbye extends Component {
  24. constructor(props) {
  25. super(props);
  26. this.state = {
  27. username: ''
  28. }
  29. }
  30. componentWillMount() {
  31. let username = localStorage.getItem('username');
  32. this.setState({
  33. username: username
  34. })
  35. }
  36. render() {
  37. return (
  38. &lt;div&gt;goodbye {this.state.username}&lt;/div&gt;
  39. )
  40. }
  41. }
  42. export default Goodbye;

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


  1. import React, {Component} from 'react'
  2. export default (WrappedComponent) =&gt; {
  3. class NewComponent extends Component {
  4. constructor() {
  5. super();
  6. this.state = {
  7. username: ''
  8. }
  9. }
  10. componentWillMount() {
  11. let username = localStorage.getItem('username');
  12. this.setState({
  13. username: username
  14. })
  15. }
  16. render() {
  17. return &lt;WrappedComponent username={this.state.username}/&gt;
  18. }
  19. }
  20. return NewComponent
  21. }
  22. 简化welcomegoodbye
  23. import React, {Component} from 'react';
  24. import wrapWithUsername from 'wrapWithUsername';
  25. class Welcome extends Component {
  26. render() {
  27. return (
  28. &lt;div&gt;welcome {this.props.username}&lt;/div&gt;
  29. )
  30. }
  31. }
  32. Welcome = wrapWithUsername(Welcome);
  33. export default Welcome;

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


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

provider实现


  1. import React from 'react'
  2. import ReactDOM from 'react-dom'
  3. import { createStore, applyMiddleware, compose} from 'redux'
  4. import thunk from 'redux-thunk'
  5. import { counter } from './index.redux'
  6. // import { Provider } from 'react-redux'
  7. // 换成自己的Provider实现
  8. import { Provider } from './self-react-redux'
  9. import App from './App'
  10. const store = createStore(counter, compose(
  11. applyMiddleware(thunk),
  12. window.devToolsExtension ? window.devToolsExtension() : f =&gt; f
  13. ))
  14. ReactDOM.render(
  15. (
  16. &lt;Provider store={store}&gt;
  17. &lt;App /&gt;
  18. &lt;/Provider&gt;
  19. ),
  20. document.getElementById('root'))

./self-react-redux


  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. export function connect(){
  4. }
  5. class Provider extends React.Component{
  6. static childContextTypes = {
  7. store: PropTypes.object
  8. }
  9. getChildContext() {
  10. return { store: this.store }
  11. }
  12. constructor(props, context) {
  13. super(props, context)
  14. this.store = props.store
  15. }
  16. render(){
  17. return this.props.children
  18. }
  19. }

connect实现

demo


  1. import React from 'react'
  2. // import { connect } from 'react-redux'
  3. import { connect } from './self-react-redux'
  4. import { addGun, removeGun, addGunAsync } from './index.redux'
  5. @connect(
  6. // 你要state什么属性放到props里
  7. state=&gt;({num:state.counter}),
  8. // 你要什么方法,放到props里,自动dispatch
  9. { addGun, removeGun, addGunAsync }
  10. )
  11. class App extends React.Component{
  12. render(){
  13. return (
  14. &lt;div&gt;
  15. &lt;h1&gt;现在有机枪{this.props.num}把&lt;/h1&gt;
  16. &lt;button onClick={this.props.addGun}&gt;申请武器&lt;/button&gt;
  17. &lt;button onClick={this.props.removeGun}&gt;上交武器&lt;/button&gt;
  18. &lt;button onClick={this.props.addGunAsync}&gt;拖两天再给&lt;/button&gt;
  19. &lt;/div&gt;
  20. )
  21. }
  22. }
  23. export default App

./self-react-redux.js

  1. // 高阶组件的写法
  2. export function connect(maoStateToProps, mapStateToProps) {
  3. return function(WrapComponent) {
  4. return class ConnectComponent extends React.Component{
  5. }
  6. }
  7. }
  8. import React from 'react'
  9. import PropTypes from 'prop-types'
  10. import { bindActionCreator } from './self-redux'
  11. // 使用简写形式
  12. // connect负责链接组件,给到redux里的数据放在组件的属性里
  13. // 1. 负责接收一个组件,把state里的一些数据放进去,返回一个组件
  14. // 2. 数据变化的时候,能通知组件
  15. export const connect = (
  16. mapStateToProps = state =&gt; state,
  17. mapDispatchToProps ={}
  18. ) =&gt; (WrapComponent) =&gt; {
  19. return class ConnectComponent extends React.Component {
  20. static contextTypes = {
  21. store: PropTypes.object
  22. }
  23. constructor(props, context){
  24. super(props, context)
  25. this.state = {
  26. props: {}
  27. }
  28. }
  29. // 2 实现了mapStateToProps
  30. componentDidMount() {
  31. const { store } = this.context
  32. store.subscribe(() =&gt; this.update())
  33. this.update()
  34. }
  35. update() {
  36. const { store } = this.context
  37. // store.getState()这就是为什么mapStateToProps函数里面能拿到state
  38. const stateProps = mapStateToProps(store.getState())
  39. // 方法不能直接给,因为需要dispatch
  40. /**
  41. function addGun() {
  42. return { type: ADD_GUN }
  43. }
  44. 直接执行addGun() 毫无意义
  45. 要 addGun = () =&gt; store.dispatch(addGun()) 才有意义,其实就是把actionCreator包了一层
  46. bindActionCreators在手写redux api实现了
  47. */
  48. const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
  49. // 注意state的顺序问题会覆盖
  50. this.setState({
  51. props: {
  52. ...this.state.props,
  53. ...stateProps,
  54. ...dispatchProps,
  55. }
  56. })
  57. }
  58. // 1
  59. render() {
  60. return &lt;WrapComponent {...this.state.props}&gt;&lt;/WrapComponent&gt;
  61. }
  62. }
  63. }

./self-redux.js


  1. // creators: {addGun, removeGun, addGunAsync}
  2. // creators[v]:addGun(参数)
  3. // 返回:(参数) =&gt; dispatch(addGun(参数))
  4. function bindActionCreator(creator, dispatch) {
  5. return (...args) =&gt; dispatch(creator(...args))
  6. }
  7. export function bindActionCreators(creators, dispatch) {
  8. let bound = {}
  9. Object.keys(creators).forEach( v =&gt; {
  10. let creator = creators[v]
  11. bound[v] = bindActionCreator(creator, dispatch)
  12. })
  13. return bound
  14. }
  15. // 简写
  16. export function bindActionCreators(creators, dispatch) {
  17. return Object.keys(creators).reduce((ret, item) =&gt; {
  18. ret[item] = bindActionCreator(creators[item], dispatch)
  19. return ret
  20. }, {})
  21. }

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

react深入 - 手写实现react-redux api的更多相关文章

  1. React深入 - 手写redux api

    简介: 手写实现redux基础api createStore( )和store相关方法 api回顾: createStore(reducer, [preloadedState], enhancer) ...

  2. react纯手写全选与取消全选

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 手写一个json格式化 api

    最近写的一个东西需要对json字符串进行格式化然后显示在网页上面. 我就想去网上找找有没有这样的api可以直接调用.百度 json api ,搜索结果都是那种只能在网页上进行校验的工具,没有api. ...

  4. 手写一个React-Redux,玩转React的Context API

    上一篇文章我们手写了一个Redux,但是单纯的Redux只是一个状态机,是没有UI呈现的,所以一般我们使用的时候都会配合一个UI库,比如在React中使用Redux就会用到React-Redux这个库 ...

  5. 放弃antd table,基于React手写一个虚拟滚动的表格

    缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...

  6. 手写React的Fiber架构,深入理解其原理

    熟悉React的朋友都知道,React支持jsx语法,我们可以直接将HTML代码写到JS中间,然后渲染到页面上,我们写的HTML如果有更新的话,React还有虚拟DOM的对比,只更新变化的部分,而不重 ...

  7. 手写系列-实现一个铂金段位的 React

    一.前言 本文基于 https://pomb.us/build-your-own-react/ 实现简单版 React. 本文学习思路来自 卡颂-b站-React源码,你在第几层. 模拟的版本为 Re ...

  8. 手写一个Redux,深入理解其原理

    Redux可是一个大名鼎鼎的库,很多地方都在用,我也用了几年了,今天这篇文章就是自己来实现一个Redux,以便于深入理解他的原理.我们还是老套路,从基本的用法入手,然后自己实现一个Redux来替代源码 ...

  9. 手写 redux 和 react-redux

    1.手写 redux redux.js /** * 手写 redux */ export function createStore(reducer) { // 当前状态 let currentStat ...

随机推荐

  1. 洛谷P2219 [HAOI2007]修筑绿化带(单调队列)

    传送门 啧……明明以前做到过这种类型的题结果全忘了…… 这种矩阵的,一般都是先枚举行,然后对列进行一遍单调队列,搞出右下角在每一行中合法位置时的最小权值 再枚举列,对行做一遍单调队列,用之前搞出来的最 ...

  2. Luogu P1850换教室【期望dp】By cellur925

    题目传送门 首先这个题我们一看它就是和概率期望有关,而大多数时候在OI中遇到他们时,都是与dp相关的. \(Vergil\)学长表示,作为\(NOIp2016\)的当事人,他们考前奶联赛一定不会考概率 ...

  3. split("\\.")是什么意思

    \\会转义成反斜杠,反斜杠本身就是转义符,所有就成了“\.”,在进行转义就是.,所以\\.实际上是“.”.在java.lang包中也有String.split()方法,与.net的类似,都是返回是一个 ...

  4. hbase-shell + hbase的java api

    本博文的主要内容有 .HBase的单机模式(1节点)安装 .HBase的单机模式(1节点)的启动 .HBase的伪分布模式(1节点)安装   .HBase的伪分布模式(1节点)的启动    .HBas ...

  5. C. Coconut(2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛)

    额,只是一道签到题,emmm直接代码: #include <cstdio> #include <cstring> #include <algorithm> usin ...

  6. SpringMVC + ajax

    1.ajax 返回汉字乱码 解决方法: http://blog.sina.com.cn/s/blog_5f39177b0101it7h.html //方案一 response.setCharacter ...

  7. DP HDOJ 5492 Find a path

    题目传送门 题意:从(1, 1)走到(n, m),每次往右或往下走,问(N+M−1)∑(Ai−Aavg)2 的最小值 分析:展开式子得到(N+M−1)∑(Ai2) - (∑(Ai))2的最小值.用普通 ...

  8. Vue 页面加载闪现代码问题

    CSS中 [v-cloak] { display: none; } HTML中 <div v-cloak> {{ message }} </div> 显示代码主要是{{}}这个 ...

  9. asp.net 微信登录实现方式

    之前我以为做微信登录跟微信公众号有关,后来发现是我想多了.原来微信还有一个叫开放平台的东西,见下图: 我的这个已经生成好了,没有的需要创建一个,https://open.weixin.qq.com/c ...

  10. AJPFX关于抽象类和接口的区别

    一.设计目的不同:接口体现的是一种规范,,类似于系统的总纲,它制定了系统的各模块应遵守的标准抽象类作为多个子类的共同父类,体现的是模式化的设计,抽象类可以认为是系统的中间产品,已经实现了部分功能,部分 ...