Skip to content

Commit

Permalink
feat(wx-react-redux): wx-react-redux 基于react-redux重新实现
Browse files Browse the repository at this point in the history
  • Loading branch information
ykforerlang committed Jul 10, 2019
1 parent 22783e1 commit 7d55920
Show file tree
Hide file tree
Showing 13 changed files with 473 additions and 176 deletions.
3 changes: 3 additions & 0 deletions packages/wx-react-redux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## wx-react-redux

The code is a copy from [react-redux](https://github.com/reduxjs/react-redux). We made some adjustments for Wechat
25 changes: 0 additions & 25 deletions packages/wx-react-redux/miniprogram_dist/Provider.js

This file was deleted.

18 changes: 18 additions & 0 deletions packages/wx-react-redux/miniprogram_dist/components/Provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component } from '@areslabs/wx-react'

export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}

constructor(props, context) {
super(props, context)
this.store = props.store
}

render() {}
}

Provider.childContextTypes = {
store: {}
}
324 changes: 324 additions & 0 deletions packages/wx-react-redux/miniprogram_dist/components/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
import { HocComponent, createElement } from '@areslabs/wx-react'
import shallowEqual from '../utils/shallowEqual'
import wrapActionCreators from '../utils/wrapActionCreators'
import warning from '../utils/warning'
import isPlainObject from '../utils/isPlainObject'
import hoistStatics from '../utils/hoistNonReactStatics'

const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
...parentProps,
...stateProps,
...dispatchProps
})

function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

let errorObject = { value: null }
function tryCatch(fn, ctx) {
try {
return fn.apply(ctx)
} catch (e) {
errorObject.value = e
return errorObject
}
}

// Helps track hot reloading.
let nextVersion = 0

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
const shouldSubscribe = Boolean(mapStateToProps)
const mapState = mapStateToProps || defaultMapStateToProps

let mapDispatch
if (typeof mapDispatchToProps === 'function') {
mapDispatch = mapDispatchToProps
} else if (!mapDispatchToProps) {
mapDispatch = defaultMapDispatchToProps
} else {
mapDispatch = wrapActionCreators(mapDispatchToProps)
}

const finalMergeProps = mergeProps || defaultMergeProps
const { pure = true, withRef = false } = options
const checkMergedEquals = pure && finalMergeProps !== defaultMergeProps

return function wrapWithConnect(WrappedComponent) {
const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`

function checkStateShape(props, methodName) {
if (!isPlainObject(props)) {
warning(
`${methodName}() in ${connectDisplayName} must return a plain object. ` +
`Instead received ${props}.`
)
}
}

function computeMergedProps(stateProps, dispatchProps, parentProps) {
const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)
return mergedProps
}

class Connect extends HocComponent {
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}

constructor(props, context) {
super(props, context)
this.store = props.store || context.store

const storeState = this.store.getState()
this.state = { storeState }
this.clearCache()
}

computeStateProps(store, props) {
if (!this.finalMapStateToProps) {
return this.configureFinalMapState(store, props)
}

const state = store.getState()
const stateProps = this.doStatePropsDependOnOwnProps ?
this.finalMapStateToProps(state, props) :
this.finalMapStateToProps(state)

return stateProps
}

configureFinalMapState(store, props) {
const mappedState = mapState(store.getState(), props)
const isFactory = typeof mappedState === 'function'

this.finalMapStateToProps = isFactory ? mappedState : mapState
this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1

if (isFactory) {
return this.computeStateProps(store, props)
}

return mappedState
}

computeDispatchProps(store, props) {
if (!this.finalMapDispatchToProps) {
return this.configureFinalMapDispatch(store, props)
}

const { dispatch } = store
const dispatchProps = this.doDispatchPropsDependOnOwnProps ?
this.finalMapDispatchToProps(dispatch, props) :
this.finalMapDispatchToProps(dispatch)

return dispatchProps
}

configureFinalMapDispatch(store, props) {
const mappedDispatch = mapDispatch(store.dispatch, props)
const isFactory = typeof mappedDispatch === 'function'

this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch
this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1

if (isFactory) {
return this.computeDispatchProps(store, props)
}

return mappedDispatch
}

updateStatePropsIfNeeded() {
const nextStateProps = this.computeStateProps(this.store, this.props)
if (this.stateProps && shallowEqual(nextStateProps, this.stateProps)) {
return false
}

this.stateProps = nextStateProps
return true
}

updateDispatchPropsIfNeeded() {
const nextDispatchProps = this.computeDispatchProps(this.store, this.props)
if (this.dispatchProps && shallowEqual(nextDispatchProps, this.dispatchProps)) {
return false
}

this.dispatchProps = nextDispatchProps
return true
}

updateMergedPropsIfNeeded() {
const nextMergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props)
if (this.mergedProps && checkMergedEquals && shallowEqual(nextMergedProps, this.mergedProps)) {
return false
}

this.mergedProps = nextMergedProps
return true
}

isSubscribed() {
return typeof this.unsubscribe === 'function'
}

trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
this.handleChange()
}
}

tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
}
}

componentDidMount() {
this.trySubscribe()
}

componentWillReceiveProps(nextProps) {
if (!pure || !shallowEqual(nextProps, this.props)) {
this.haveOwnPropsChanged = true
}
}

componentWillUnmount() {
this.tryUnsubscribe()
this.clearCache()
}

clearCache() {
this.dispatchProps = null
this.stateProps = null
this.mergedProps = null
this.haveOwnPropsChanged = true
this.hasStoreStateChanged = true
this.haveStatePropsBeenPrecalculated = false
this.statePropsPrecalculationError = null
this.renderedElement = null
this.finalMapDispatchToProps = null
this.finalMapStateToProps = null
}

handleChange() {
if (!this.unsubscribe) {
return
}

const storeState = this.store.getState()
const prevStoreState = this.state.storeState
if (pure && prevStoreState === storeState) {
return
}

if (pure && !this.doStatePropsDependOnOwnProps) {
const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this)
if (!haveStatePropsChanged) {
return
}
if (haveStatePropsChanged === errorObject) {
this.statePropsPrecalculationError = errorObject.value
}
this.haveStatePropsBeenPrecalculated = true
}

this.hasStoreStateChanged = true
this.setState({ storeState })
}

getWrappedInstance() {
return this.wrappedInstance
}

render() {
const {
haveOwnPropsChanged,
hasStoreStateChanged,
haveStatePropsBeenPrecalculated,
statePropsPrecalculationError,
renderedElement
} = this

this.haveOwnPropsChanged = false
this.hasStoreStateChanged = false
this.haveStatePropsBeenPrecalculated = false
this.statePropsPrecalculationError = null

if (statePropsPrecalculationError) {
throw statePropsPrecalculationError
}

let shouldUpdateStateProps = true
let shouldUpdateDispatchProps = true
if (pure && renderedElement) {
shouldUpdateStateProps = hasStoreStateChanged || (
haveOwnPropsChanged && this.doStatePropsDependOnOwnProps
)
shouldUpdateDispatchProps =
haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps
}

let haveStatePropsChanged = false
let haveDispatchPropsChanged = false
if (haveStatePropsBeenPrecalculated) {
haveStatePropsChanged = true
} else if (shouldUpdateStateProps) {
haveStatePropsChanged = this.updateStatePropsIfNeeded()
}
if (shouldUpdateDispatchProps) {
haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded()
}

let haveMergedPropsChanged = true
if (
haveStatePropsChanged ||
haveDispatchPropsChanged ||
haveOwnPropsChanged
) {
haveMergedPropsChanged = this.updateMergedPropsIfNeeded()
} else {
haveMergedPropsChanged = false
}

if (!haveMergedPropsChanged && renderedElement) {
return renderedElement
}

if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
...this.hocProps,
ref: (wrappedInstance) => {
this.wrappedInstance = wrappedInstance
}
})
} else {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
...this.hocProps,
})
}



return this.renderedElement
}
}

Connect.displayName = connectDisplayName
Connect.WrappedComponent = WrappedComponent
Connect.contextTypes = {
store: {}
}

return hoistStatics(Connect, WrappedComponent)
}
}
Loading

0 comments on commit 7d55920

Please sign in to comment.