Skip to content

Commit 2677705

Browse files
authored
Pass non-Redux-store values through the store prop (#1447)
* Allow non-Redux-store values as a prop named `store` * Formatting
1 parent b832f83 commit 2677705

File tree

5 files changed

+37
-18
lines changed

5 files changed

+37
-18
lines changed

docs/api/connect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ export default connect(
571571
572572
The number of declared function parameters of `mapStateToProps` and `mapDispatchToProps` determines whether they receive `ownProps`
573573
574-
> Note: `ownProps` is not passed to `mapStateToProps` and `mapDispatchToProps` if the formal definition of the function contains one mandatory parameter (function has length 1). For example, functions defined like below won't receive `ownProps` as the second argument. If the incoming value of `ownProps` is `undefined`, the default argument value will be used.
574+
> Note: `ownProps` is not passed to `mapStateToProps` and `mapDispatchToProps` if the formal definition of the function contains one mandatory parameter (function has length 1). For example, functions defined like below won't receive `ownProps` as the second argument. If the incoming value of `ownProps` is `undefined`, the default argument value will be used.
575575
576576
```js
577577
function mapStateToProps(state) {

docs/api/hooks.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ React Redux now offers a set of hook APIs as an alternative to the existing `con
1313

1414
These hooks were first added in v7.1.0.
1515

16-
1716
## Using Hooks in a React Redux App
1817

1918
As with `connect()`, you should start by wrapping your entire application in a `<Provider>` component to make the store available throughout the component tree:
@@ -212,10 +211,6 @@ export const App = () => {
212211

213212
## Removed: `useActions()`
214213

215-
216-
217-
218-
219214
## `useDispatch()`
220215

221216
```js
@@ -295,7 +290,6 @@ export const CounterComponent = ({ value }) => {
295290
}
296291
```
297292

298-
299293
## Custom context
300294

301295
The `<Provider>` component allows you to specify an alternate context via the `context` prop. This is useful if you're building a complex reusable component, and you don't want your store to collide with any Redux store your consumers' applications might use.
@@ -395,7 +389,7 @@ This hook was in our original alpha release, but removed in `v7.1.0-alpha.4`, ba
395389
That suggestion was based on "binding action creators" not being as useful in a hooks-based use case, and causing too
396390
much conceptual overhead and syntactic complexity.
397391

398-
You should probably prefer to call the [`useDispatch`](#usedispatch) hook in your components to retrieve a reference to `dispatch`,
392+
You should probably prefer to call the [`useDispatch`](#usedispatch) hook in your components to retrieve a reference to `dispatch`,
399393
and manually call `dispatch(someActionCreator())` in callbacks and effects as needed. You may also use the Redux
400394
[`bindActionCreators`](https://redux.js.org/api/bindactioncreators) function in your own code to bind action creators,
401395
or "manually" bind them like `const boundAddTodo = (text) => dispatch(addTodo(text))`.
@@ -410,12 +404,15 @@ import { useMemo } from 'react'
410404

411405
export function useActions(actions, deps) {
412406
const dispatch = useDispatch()
413-
return useMemo(() => {
414-
if (Array.isArray(actions)) {
415-
return actions.map(a => bindActionCreators(a, dispatch))
416-
}
417-
return bindActionCreators(actions, dispatch)
418-
}, deps ? [dispatch, ...deps] : [dispatch])
407+
return useMemo(
408+
() => {
409+
if (Array.isArray(actions)) {
410+
return actions.map(a => bindActionCreators(a, dispatch))
411+
}
412+
return bindActionCreators(actions, dispatch)
413+
},
414+
deps ? [dispatch, ...deps] : [dispatch]
415+
)
419416
}
420417
```
421418

docs/troubleshooting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,4 @@ Or by setting it globally:
118118
}
119119
```
120120

121-
See https://github.com/facebook/react/issues/14927#issuecomment-490426131
121+
See https://github.com/facebook/react/issues/14927#issuecomment-490426131

src/components/connectAdvanced.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,14 @@ export default function connectAdvanced(
163163
// Retrieve the store and ancestor subscription via context, if available
164164
const contextValue = useContext(ContextToUse)
165165

166-
// The store _must_ exist as either a prop or in context
167-
const didStoreComeFromProps = Boolean(props.store)
166+
// The store _must_ exist as either a prop or in context.
167+
// We'll check to see if it _looks_ like a Redux store first.
168+
// This allows us to pass through a `store` prop that is just a plain value.
169+
console.log('Store from props: ', props.store)
170+
const didStoreComeFromProps =
171+
Boolean(props.store) &&
172+
Boolean(props.store.getState) &&
173+
Boolean(props.store.dispatch)
168174
const didStoreComeFromContext =
169175
Boolean(contextValue) && Boolean(contextValue.store)
170176

@@ -176,7 +182,8 @@ export default function connectAdvanced(
176182
`React context consumer to ${displayName} in connect options.`
177183
)
178184

179-
const store = props.store || contextValue.store
185+
// Based on the previous check, one of these must be true
186+
const store = didStoreComeFromProps ? props.store : contextValue.store
180187

181188
const childPropsSelector = useMemo(() => {
182189
// The child props selector needs the store reference as an input.

test/components/connect.spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,21 @@ describe('React', () => {
20902090
expect(actualState).toEqual(expectedState)
20912091
})
20922092

2093+
it('should pass through a store prop that is not actually a Redux store', () => {
2094+
const notActuallyAStore = 42
2095+
2096+
const store = createStore(() => 123)
2097+
const Decorated = connect(state => ({ state }))(Passthrough)
2098+
2099+
const rendered = rtl.render(
2100+
<ProviderMock store={store}>
2101+
<Decorated store={notActuallyAStore} />
2102+
</ProviderMock>
2103+
)
2104+
2105+
expect(rendered.getByTestId('store')).toHaveTextContent('42')
2106+
})
2107+
20932108
it('should pass through ancestor subscription when store is given as a prop', () => {
20942109
const c3Spy = jest.fn()
20952110
const c2Spy = jest.fn()

0 commit comments

Comments
 (0)