Skip to content

Commit 5199d9d

Browse files
casdevstimdorr
authored andcommitted
Ensure that component prop 'context' really contains a React context … (#1134)
* Ensure that component prop 'context' really contains a React context before using it after switching to react-redux 6.0.0, we've had a lot of errors stating Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. all over in our app. After digging deeper, we've discovered that we use a lot of (connected) components using a property named 'context' which conflicts with your new connectAdvanced code. So maybe you can improve the check on this.props.context a little bit to ensure it is used only if it really contains a valid React context. * Update connectAdvanced.js * Update connectAdvanced.js * improved check whether context given as a prop is a real ReactContext * added test for ignoring non-react-context values passed as a prop to the component * Use react-is checks * Just check Consumer. Good enough! * improved check for context.Consumer * added missing export 'isContextConsumer' in rollup config
1 parent e7661b3 commit 5199d9d

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

rollup.config.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import nodeResolve from 'rollup-plugin-node-resolve'
22
import babel from 'rollup-plugin-babel'
33
import replace from 'rollup-plugin-replace'
44
import commonjs from 'rollup-plugin-commonjs'
5-
import {uglify} from 'rollup-plugin-uglify'
5+
import { uglify } from 'rollup-plugin-uglify'
66
import pkg from './package.json'
77

88
const env = process.env.NODE_ENV
@@ -22,14 +22,17 @@ const config = {
2222
nodeResolve(),
2323
babel({
2424
exclude: '**/node_modules/**',
25-
runtimeHelpers: true,
25+
runtimeHelpers: true
2626
}),
2727
replace({
2828
'process.env.NODE_ENV': JSON.stringify(env)
2929
}),
3030
commonjs({
3131
namedExports: {
32-
'node_modules/react-is/index.js': ['isValidElementType'],
32+
'node_modules/react-is/index.js': [
33+
'isValidElementType',
34+
'isContextConsumer'
35+
]
3336
}
3437
})
3538
]

src/components/connectAdvanced.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import hoistStatics from 'hoist-non-react-statics'
22
import invariant from 'invariant'
33
import React, { Component, PureComponent } from 'react'
4-
import { isValidElementType } from 'react-is'
4+
import { isValidElementType, isContextConsumer } from 'react-is'
55

66
import { ReactReduxContext } from './Context'
77

@@ -213,7 +213,12 @@ export default function connectAdvanced(
213213
}
214214

215215
render() {
216-
const ContextToUse = this.props.context || Context
216+
const ContextToUse =
217+
this.props.context &&
218+
this.props.context.Consumer &&
219+
isContextConsumer(<this.props.context.Consumer />)
220+
? this.props.context
221+
: Context
217222

218223
return (
219224
<ContextToUse.Consumer>

test/components/connect.spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,36 @@ describe('React', () => {
15641564
expect(actualState).toEqual(expectedState)
15651565
})
15661566

1567+
it('should ignore non-react-context values that are passed as a prop to the component', () => {
1568+
class Container extends Component {
1569+
render() {
1570+
return <Passthrough />
1571+
}
1572+
}
1573+
1574+
const nonContext = { someProperty: {} }
1575+
1576+
let actualState
1577+
1578+
const expectedState = { foos: {} }
1579+
1580+
const decorator = connect(state => {
1581+
actualState = state
1582+
return {}
1583+
})
1584+
const Decorated = decorator(Container)
1585+
1586+
const store = createStore(() => expectedState)
1587+
1588+
rtl.render(
1589+
<ProviderMock store={store}>
1590+
<Decorated context={nonContext} />
1591+
</ProviderMock>
1592+
)
1593+
1594+
expect(actualState).toEqual(expectedState)
1595+
})
1596+
15671597
it('should throw an error if the store is not in the props or context', () => {
15681598
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
15691599

0 commit comments

Comments
 (0)