Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitadc805a

Browse files
committed
Merge pull request#279 from tgriesser/mapStateToFactory
Overloading connect with factory
2 parents89645d6 +1da657e commitadc805a

File tree

2 files changed

+210
-44
lines changed

2 files changed

+210
-44
lines changed

‎src/components/connect.js‎

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,51 +18,33 @@ function getDisplayName(WrappedComponent) {
1818
returnWrappedComponent.displayName||WrappedComponent.name||'Component'
1919
}
2020

21+
functioncheckStateShape(stateProps,dispatch){
22+
invariant(
23+
isPlainObject(stateProps),
24+
'`%sToProps` must return an object. Instead received %s.',
25+
dispatch ?'mapDispatch' :'mapState',
26+
stateProps
27+
)
28+
returnstateProps
29+
}
30+
2131
// Helps track hot reloading.
2232
letnextVersion=0
2333

2434
exportdefaultfunctionconnect(mapStateToProps,mapDispatchToProps,mergeProps,options={}){
2535
constshouldSubscribe=Boolean(mapStateToProps)
26-
constfinalMapStateToProps=mapStateToProps||defaultMapStateToProps
27-
constfinalMapDispatchToProps=isPlainObject(mapDispatchToProps) ?
36+
constmapState=mapStateToProps||defaultMapStateToProps
37+
constmapDispatch=isPlainObject(mapDispatchToProps) ?
2838
wrapActionCreators(mapDispatchToProps) :
2939
mapDispatchToProps||defaultMapDispatchToProps
40+
3041
constfinalMergeProps=mergeProps||defaultMergeProps
31-
constdoStatePropsDependOnOwnProps=finalMapStateToProps.length!==1
32-
constdoDispatchPropsDependOnOwnProps=finalMapDispatchToProps.length!==1
42+
constcheckMergedEquals=finalMergeProps!==defaultMergeProps
3343
const{ pure=true, withRef=false}=options
3444

3545
// Helps track hot reloading.
3646
constversion=nextVersion++
3747

38-
functioncomputeStateProps(store,props){
39-
conststate=store.getState()
40-
conststateProps=doStatePropsDependOnOwnProps ?
41-
finalMapStateToProps(state,props) :
42-
finalMapStateToProps(state)
43-
44-
invariant(
45-
isPlainObject(stateProps),
46-
'`mapStateToProps` must return an object. Instead received %s.',
47-
stateProps
48-
)
49-
returnstateProps
50-
}
51-
52-
functioncomputeDispatchProps(store,props){
53-
const{ dispatch}=store
54-
constdispatchProps=doDispatchPropsDependOnOwnProps ?
55-
finalMapDispatchToProps(dispatch,props) :
56-
finalMapDispatchToProps(dispatch)
57-
58-
invariant(
59-
isPlainObject(dispatchProps),
60-
'`mapDispatchToProps` must return an object. Instead received %s.',
61-
dispatchProps
62-
)
63-
returndispatchProps
64-
}
65-
6648
functioncomputeMergedProps(stateProps,dispatchProps,parentProps){
6749
constmergedProps=finalMergeProps(stateProps,dispatchProps,parentProps)
6850
invariant(
@@ -96,8 +78,58 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
9678
this.clearCache()
9779
}
9880

81+
computeStateProps(store,props){
82+
if(!this.finalMapStateToProps){
83+
returnthis.configureFinalMapState(store,props)
84+
}
85+
86+
conststate=store.getState()
87+
conststateProps=this.doStatePropsDependOnOwnProps ?
88+
this.finalMapStateToProps(state,props) :
89+
this.finalMapStateToProps(state)
90+
91+
returncheckStateShape(stateProps)
92+
}
93+
94+
configureFinalMapState(store,props){
95+
constmappedState=mapState(store.getState(),props)
96+
constisFactory=typeofmappedState==='function'
97+
98+
this.finalMapStateToProps=isFactory ?mappedState :mapState
99+
this.doStatePropsDependOnOwnProps=this.finalMapStateToProps.length!==1
100+
101+
returnisFactory ?
102+
this.computeStateProps(store,props) :
103+
checkStateShape(mappedState)
104+
}
105+
106+
computeDispatchProps(store,props){
107+
if(!this.finalMapDispatchToProps){
108+
returnthis.configureFinalMapDispatch(store,props)
109+
}
110+
111+
const{ dispatch}=store
112+
constdispatchProps=this.doDispatchPropsDependOnOwnProps ?
113+
this.finalMapDispatchToProps(dispatch,props) :
114+
this.finalMapDispatchToProps(dispatch)
115+
116+
returncheckStateShape(dispatchProps,true)
117+
}
118+
119+
configureFinalMapDispatch(store,props){
120+
constmappedDispatch=mapDispatch(store.dispatch,props)
121+
constisFactory=typeofmappedDispatch==='function'
122+
123+
this.finalMapDispatchToProps=isFactory ?mappedDispatch :mapDispatch
124+
this.doDispatchPropsDependOnOwnProps=this.finalMapDispatchToProps.length!==1
125+
126+
returnisFactory ?
127+
this.computeDispatchProps(store,props) :
128+
checkStateShape(mappedDispatch,true)
129+
}
130+
99131
updateStatePropsIfNeeded(){
100-
constnextStateProps=computeStateProps(this.store,this.props)
132+
constnextStateProps=this.computeStateProps(this.store,this.props)
101133
if(this.stateProps&&shallowEqual(nextStateProps,this.stateProps)){
102134
returnfalse
103135
}
@@ -107,7 +139,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
107139
}
108140

109141
updateDispatchPropsIfNeeded(){
110-
constnextDispatchProps=computeDispatchProps(this.store,this.props)
142+
constnextDispatchProps=this.computeDispatchProps(this.store,this.props)
111143
if(this.dispatchProps&&shallowEqual(nextDispatchProps,this.dispatchProps)){
112144
returnfalse
113145
}
@@ -116,12 +148,14 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
116148
returntrue
117149
}
118150

119-
updateMergedProps(){
120-
this.mergedProps=computeMergedProps(
121-
this.stateProps,
122-
this.dispatchProps,
123-
this.props
124-
)
151+
updateMergedPropsIfNeeded(){
152+
constnextMergedProps=computeMergedProps(this.stateProps,this.dispatchProps,this.props)
153+
if(this.mergedProps&&checkMergedEquals&&shallowEqual(nextMergedProps,this.mergedProps)){
154+
returnfalse
155+
}
156+
157+
this.mergedProps=nextMergedProps
158+
returntrue
125159
}
126160

127161
isSubscribed(){
@@ -164,6 +198,8 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
164198
this.haveOwnPropsChanged=true
165199
this.hasStoreStateChanged=true
166200
this.renderedElement=null
201+
this.finalMapDispatchToProps=null
202+
this.finalMapStateToProps=null
167203
}
168204

169205
handleChange(){
@@ -203,10 +239,10 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
203239
letshouldUpdateDispatchProps=true
204240
if(pure&&renderedElement){
205241
shouldUpdateStateProps=hasStoreStateChanged||(
206-
haveOwnPropsChanged&&doStatePropsDependOnOwnProps
242+
haveOwnPropsChanged&&this.doStatePropsDependOnOwnProps
207243
)
208244
shouldUpdateDispatchProps=
209-
haveOwnPropsChanged&&doDispatchPropsDependOnOwnProps
245+
haveOwnPropsChanged&&this.doDispatchPropsDependOnOwnProps
210246
}
211247

212248
lethaveStatePropsChanged=false
@@ -224,7 +260,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
224260
haveDispatchPropsChanged||
225261
haveOwnPropsChanged
226262
){
227-
this.updateMergedProps()
263+
haveMergedPropsChanged=this.updateMergedPropsIfNeeded()
228264
}else{
229265
haveMergedPropsChanged=false
230266
}

‎test/components/connect.spec.js‎

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('React', () => {
3939
@connect()
4040
classContainerextendsComponent{
4141
render(){
42-
return<div{...this.props}/>
42+
return<Passthrough{...this.props}/>
4343
}
4444
}
4545

@@ -1509,5 +1509,135 @@ describe('React', () => {
15091509
// But render is not because it did not make any actual changes
15101510
expect(renderCalls).toBe(1)
15111511
})
1512+
1513+
it('should allow providing a factory function to mapStateToProps',()=>{
1514+
letupdatedCount=0
1515+
letmemoizedReturnCount=0
1516+
conststore=createStore(()=>({value:1}))
1517+
1518+
constmapStateFactory=()=>{
1519+
letlastProp,lastVal,lastResult
1520+
return(state,props)=>{
1521+
if(props.name===lastProp&&lastVal===state.value){
1522+
memoizedReturnCount++
1523+
returnlastResult
1524+
}
1525+
lastProp=props.name
1526+
lastVal=state.value
1527+
returnlastResult={someObject:{prop:props.name,stateVal:state.value}}
1528+
}
1529+
}
1530+
1531+
@connect(mapStateFactory)
1532+
classContainerextendsComponent{
1533+
componentWillUpdate(){
1534+
updatedCount++
1535+
}
1536+
render(){
1537+
return<Passthrough{...this.props}/>
1538+
}
1539+
}
1540+
1541+
TestUtils.renderIntoDocument(
1542+
<ProviderMockstore={store}>
1543+
<div>
1544+
<Containername="a"/>
1545+
<Containername="b"/>
1546+
</div>
1547+
</ProviderMock>
1548+
)
1549+
1550+
store.dispatch({type:'test'})
1551+
expect(updatedCount).toBe(0)
1552+
expect(memoizedReturnCount).toBe(2)
1553+
})
1554+
1555+
it('should allow providing a factory function to mapDispatchToProps',()=>{
1556+
letupdatedCount=0
1557+
letmemoizedReturnCount=0
1558+
conststore=createStore(()=>({value:1}))
1559+
1560+
constmapDispatchFactory=()=>{
1561+
letlastProp,lastResult
1562+
return(dispatch,props)=>{
1563+
if(props.name===lastProp){
1564+
memoizedReturnCount++
1565+
returnlastResult
1566+
}
1567+
lastProp=props.name
1568+
returnlastResult={someObject:{dispatchFn:dispatch}}
1569+
}
1570+
}
1571+
functionmergeParentDispatch(stateProps,dispatchProps,parentProps){
1572+
return{ ...stateProps, ...dispatchProps,name:parentProps.name}
1573+
}
1574+
1575+
@connect(null,mapDispatchFactory,mergeParentDispatch)
1576+
classPassthroughextendsComponent{
1577+
componentWillUpdate(){
1578+
updatedCount++
1579+
}
1580+
render(){
1581+
return<div{...this.props}/>
1582+
}
1583+
}
1584+
1585+
classContainerextendsComponent{
1586+
constructor(props){
1587+
super(props)
1588+
this.state={count:0}
1589+
}
1590+
componentDidMount(){
1591+
this.setState({count:1})
1592+
}
1593+
render(){
1594+
const{ count}=this.state
1595+
return(
1596+
<div>
1597+
<Passthroughcount={count}name="a"/>
1598+
<Passthroughcount={count}name="b"/>
1599+
</div>
1600+
)
1601+
}
1602+
}
1603+
1604+
TestUtils.renderIntoDocument(
1605+
<ProviderMockstore={store}>
1606+
<Container/>
1607+
</ProviderMock>
1608+
)
1609+
1610+
store.dispatch({type:'test'})
1611+
expect(updatedCount).toBe(0)
1612+
expect(memoizedReturnCount).toBe(2)
1613+
})
1614+
1615+
it('should not call update if mergeProps return value has not changed',()=>{
1616+
letmapStateCalls=0
1617+
letrenderCalls=0
1618+
conststore=createStore(stringBuilder)
1619+
1620+
@connect(()=>({a:++mapStateCalls}),null,()=>({changed:false}))
1621+
classContainerextendsComponent{
1622+
render(){
1623+
renderCalls++
1624+
return<Passthrough{...this.props}/>
1625+
}
1626+
}
1627+
1628+
TestUtils.renderIntoDocument(
1629+
<ProviderMockstore={store}>
1630+
<Container/>
1631+
</ProviderMock>
1632+
)
1633+
1634+
expect(renderCalls).toBe(1)
1635+
expect(mapStateCalls).toBe(1)
1636+
1637+
store.dispatch({type:'APPEND',body:'a'})
1638+
1639+
expect(mapStateCalls).toBe(2)
1640+
expect(renderCalls).toBe(1)
1641+
})
15121642
})
15131643
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp