实现 React-redux(一) connect 和 mapStateToProps

1.结合 context 和 store

  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types'
  3. function createStore (reducer) {
  4. let state = null
  5. const listeners = []
  6. const subscribe = (listener) => listeners.push(listener)
  7. const getState = () => state
  8. const dispatch = (action) => {
  9. state = reducer(state, action)
  10. listeners.forEach((listener) => listener())
  11. }
  12. dispatch({}) // 初始化 state
  13. return { getState, dispatch, subscribe }
  14. }
  15. const themeReducer = (state, action) => {
  16. if (!state) return {
  17. themeColor: 'red'
  18. }
  19. switch (action.type) {
  20. case 'CHANGE_COLOR':
  21. return { ...state, themeColor: action.themeColor }
  22. default:
  23. return state
  24. }
  25. }
  26. const store = createStore(themeReducer)
  27. class Header extends Component {
  28. static contextTypes = {
  29. store: PropTypes.object
  30. }
  31. constructor () {
  32. super()
  33. this.state = { themeColor: '' }
  34. }
  35. componentWillMount () {
  36. const { store } = this.context
  37. this._updateThemeColor()
  38. store.subscribe(() => this._updateThemeColor())
  39. }
  40. _updateThemeColor () {
  41. const { store } = this.context
  42. const state = store.getState()
  43. this.setState({ themeColor: state.themeColor })
  44. }
  45. render () {
  46. return (
  47. <div>
  48. <h1 style={{ color: this.state.themeColor }}></h1>
  49. <ThemeSwitch />
  50. </div>
  51. )
  52. }
  53. }
  54. class ThemeSwitch extends Component {
  55. static contextTypes = {
  56. store: PropTypes.object
  57. }
  58. // dispatch action 去改变颜色
  59. handleSwitchColor (color) {
  60. const { store } = this.context
  61. store.dispatch({
  62. type: 'CHANGE_COLOR',
  63. themeColor: color
  64. })
  65. }
  66. render () {
  67. return (
  68. <div>
  69. <button onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button>
  70. <button onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button>
  71. </div>
  72. )
  73. }
  74. }
  75. class App extends Component {
  76. static childContextTypes = {
  77. store: PropTypes.object
  78. }
  79. getChildContext () {
  80. return { store }
  81. }
  82. render() {
  83. return (
  84. <div>
  85. <Header />
  86. </div>
  87. );
  88. }
  89. }
  90. export default App;

2.connect 和 mapStateToProps

App.js:

  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types'
  3. import Header from './Header'
  4. function createStore (reducer) {
  5. let state = null
  6. const listeners = []
  7. const subscribe = (listener) => listeners.push(listener)
  8. const getState = () => state
  9. const dispatch = (action) => {
  10. state = reducer(state, action)
  11. listeners.forEach((listener) => listener())
  12. }
  13. dispatch({}) // 初始化 state
  14. return { getState, dispatch, subscribe }
  15. }
  16. const themeReducer = (state, action) => {
  17. if (!state) return {
  18. themeColor: 'red'
  19. }
  20. switch (action.type) {
  21. case 'CHANGE_COLOR':
  22. return { ...state, themeColor: action.themeColor }
  23. default:
  24. return state
  25. }
  26. }
  27. const store = createStore(themeReducer)
  28. class App extends Component {
  29. static childContextTypes = {
  30. store: PropTypes.object
  31. }
  32. getChildContext () {
  33. return { store }
  34. }
  35. render() {
  36. return (
  37. <div>
  38. <Header />
  39. </div>
  40. );
  41. }
  42. }
  43. export default App;

Header.js:

  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types'
  3. import { connect } from './react-redux'
  4. import ThemeSwitch from './ThemeSwitch'
  5. class Header extends Component {
  6. static propTypes = {
  7. themeColor: PropTypes.string
  8. }
  9. render () {
  10. return (
  11. <div>
  12. <h1 style={{ color: this.props.themeColor }}></h1>
  13. <ThemeSwitch/>
  14. </div>
  15. )
  16. }
  17. }
  18. const mapStateToProps = (state) => {
  19. return {
  20. themeColor: state.themeColor
  21. }
  22. }
  23. Header = connect(mapStateToProps)(Header)
  24. export default Header

react-redux.js:

  1. import React, { Component } from 'react'
  2. import PropTypes from 'prop-types'
  3. export const connect = (mapStateToProps) => (WrappedComponent) => {
  4. class Connect extends Component {
  5. static contextTypes = {
  6. store: PropTypes.object
  7. }
  8. constructor () {
  9. super()
  10. this.state = { allProps: {} }
  11. }
  12. componentWillMount () {
  13. const { store } = this.context
  14. this._updateProps()
  15. store.subscribe(() => this._updateProps())
  16. }
  17. _updateProps () {
  18. const { store } = this.context
  19. let stateProps = mapStateToProps(store.getState(), this.props) // 额外传入 props,让获取数据更加灵活方便
  20. this.setState({
  21. allProps: { // 整合普通的 props 和从 state 生成的 props
  22. ...stateProps,
  23. ...this.props
  24. }
  25. })
  26. }
  27. render () {
  28. return <WrappedComponent {...this.state.allProps} />
  29. }
  30. }
  31. return Connect
  32. }

ThemeSwitch.js

  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types'
  3. class ThemeSwitch extends Component {
  4. static contextTypes = {
  5. store: PropTypes.object
  6. }
  7. // dispatch action 去改变颜色
  8. handleSwitchColor (color) {
  9. const { store } = this.context
  10. store.dispatch({
  11. type: 'CHANGE_COLOR',
  12. themeColor: color
  13. })
  14. }
  15. render () {
  16. return (
  17. <div>
  18. <button onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button>
  19. <button onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button>
  20. </div>
  21. )
  22. }
  23. }
  24. export default ThemeSwitch

 

 

 

 

 

 

posted @ 2024-03-18 09:39  mounter爱学习  阅读(10)  评论(0编辑  收藏  举报