diff --git a/src/components/Provider.js b/src/components/Provider.js index 6b3e46ba2..3c5ec0ea7 100644 --- a/src/components/Provider.js +++ b/src/components/Provider.js @@ -1,62 +1,36 @@ -import React, { Component } from 'react' +import React, { useMemo, useEffect } from 'react' import PropTypes from 'prop-types' import { ReactReduxContext } from './Context' import Subscription from '../utils/Subscription' -class Provider extends Component { - constructor(props) { - super(props) - - const { store } = props - - this.notifySubscribers = this.notifySubscribers.bind(this) +function Provider({ store, context, children }) { + const contextValue = useMemo(() => { const subscription = new Subscription(store) - subscription.onStateChange = this.notifySubscribers - - this.state = { + subscription.onStateChange = subscription.notifyNestedSubs + return { store, subscription } + }, [store]) - this.previousState = store.getState() - } + const previousState = useMemo(() => store.getState(), [store]) - componentDidMount() { - this.state.subscription.trySubscribe() + useEffect(() => { + const { subscription } = contextValue + subscription.trySubscribe() - if (this.previousState !== this.props.store.getState()) { - this.state.subscription.notifyNestedSubs() + if (previousState !== store.getState()) { + subscription.notifyNestedSubs() } - } - - componentWillUnmount() { - if (this.unsubscribe) this.unsubscribe() - - this.state.subscription.tryUnsubscribe() - } - - componentDidUpdate(prevProps) { - if (this.props.store !== prevProps.store) { - this.state.subscription.tryUnsubscribe() - const subscription = new Subscription(this.props.store) - subscription.onStateChange = this.notifySubscribers - this.setState({ store: this.props.store, subscription }) + return () => { + subscription.tryUnsubscribe() + subscription.onStateChange = null } - } - - notifySubscribers() { - this.state.subscription.notifyNestedSubs() - } + }, [contextValue, previousState]) - render() { - const Context = this.props.context || ReactReduxContext + const Context = context || ReactReduxContext - return ( - - {this.props.children} - - ) - } + return {children} } Provider.propTypes = { diff --git a/test/components/Provider.spec.js b/test/components/Provider.spec.js index 1b95ce364..c8351f621 100644 --- a/test/components/Provider.spec.js +++ b/test/components/Provider.spec.js @@ -312,5 +312,43 @@ describe('React', () => { ReactDOM.unmountComponentAtNode(div) expect(spy).toHaveBeenCalledTimes(1) }) + + it('should handle store and children change in a the same render', () => { + const reducerA = (state = { nestedA: { value: 'expectedA' } }) => state + const reducerB = (state = { nestedB: { value: 'expectedB' } }) => state + + const storeA = createStore(reducerA) + const storeB = createStore(reducerB) + + @connect(state => ({ value: state.nestedA.value })) + class ComponentA extends Component { + render() { + return
{this.props.value}
+ } + } + + @connect(state => ({ value: state.nestedB.value })) + class ComponentB extends Component { + render() { + return
{this.props.value}
+ } + } + + const { getByTestId, rerender } = rtl.render( + + + + ) + + expect(getByTestId('value')).toHaveTextContent('expectedA') + + rerender( + + + + ) + + expect(getByTestId('value')).toHaveTextContent('expectedB') + }) }) })