redux基本使用及connect实现

229 阅读1分钟

基础使用

const { legacy_createStore } = require('redux')
const createStore = legacy_createStore
const initialState = {
  count: 0
}
// reducer 必须是一个纯函数
const reducer = function (state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, ...{ count: state.count + 1 } }
      break;
    case 'ADD_NUMBER':
      return { ...state, ...{ count: state.count + action.num } }
      break;
    default:
      return state
      break;
  }
}
// 创建store
const store = createStore(reducer)

// 订阅store的修改
store.subscribe(() => {
  console.log(store.getState())
})

const action = { type: 'INCREMENT' }
const action1 = { type: 'ADD_NUMBER', num: 9 }
// 派发action
store.dispatch(action)
store.dispatch(action1)

自定义connect函数(返回一个高阶组件)

import { PureComponent } from "react"
import store from '../store';

export function connect(mapStateToProps, mapDispatchToProps) {
  // 返回一个高阶组件
  return function enhanceHOC(WrapperComponent) {
    return class extends PureComponent {
      constructor(props) {
        super(props)
        this.state = {
          storeState: mapStateToProps(store.getState())
        }
      }

      componentDidMount() {
        // 订阅state改变
        this.unsubscribe = store.subscribe(() => {
          this.setState({
            storeState: mapStateToProps(store.getState())
          })
        })
      }
      
      componentWillUnmount() {
        // 取消订阅
        this.unsubscribe()
      }

      render() {
        return (
          <WrapperComponent
            {...this.props}
            {...mapStateToProps(store.getState())}
            {...mapDispatchToProps(store.dispatch)}
          />
        )
      }
    }
  }
}

使用

class App extends Component {
  render() {
    const { count, addNum } = this.props
    return (
      <div>
        <span>count: { count }</span>
        <button onClick={() => addNum(8)}>+8</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  count: state.count
})
const mapDispatchToProps = (dispatch) => ({
  addNum: (num) => {
    dispatch(addAction(num))
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(App)

context

上面封装的connect函数还有对store的依赖,这其实是不应该的,因为我们想要把connect作为一个库文件进行发布,所以我们应该想办法取消这个依赖

做法是使用context

// context.js
import { createContext } from "react";

export const StoreContext = createContext()
// connect.js store换成context的值,我们应该让使用者给我们传入
import React, { PureComponent } from "react"
import { StoreContext } from './context'

export function connect(mapStateToProps, mapDispatchToProps) {
  // 返回一个高阶组件
  return function enhanceHOC(WrapperComponent) {
    class EnhanceComponent extends PureComponent {
      constructor(props, context) {
        super(props)
        this.state = {
          // 此处不能直接this.context 因为还没有这个值,可以用constructor第二个参数
          storeState: mapStateToProps(context.getState())
        }
      }
      
      componentDidMount() {
        // 订阅state改变
        this.unsubscribe = this.context.subscribe(() => {
          this.setState({
            storeState: mapStateToProps(this.context.getState())
          })
        })
      }
      
      componentWillUnmount() {
        // 取消订阅
        this.unsubscribe()
      }
      
      render() {
        return (
          <WrapperComponent
            {...this.props}
            {...mapStateToProps(this.context.getState())}
            {...mapDispatchToProps(this.context.dispatch)}
            />
        )
      }
    }
    EnhanceComponent.contextType = StoreContext
    return EnhanceComponent
  }
}
// 用户传入 项目入口文件 index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './app.jsx';
import { StoreContext } from './utils/context';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  {/* 此处传入store */}
  <StoreContext.Provider value={store}>
    <App />
  </StoreContext.Provider>
);