Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Changing store on the fly" error when embedding <Provider> in a stateless component #259

Closed
Zacqary opened this issue Jan 20, 2016 · 10 comments

Comments

@Zacqary
Copy link

Zacqary commented Jan 20, 2016

I'm working on a large app where we're slowly integrating Redux piecemeal, essentially converting small components into "embedded Redux apps" that are the top-level of the Redux app, but not the top-level of the React app. This has worked for the most part, but we ran into this issue:

var ReduxRoot = props => (
    <Provider store={props}>
        <ReactReduxConnectedComponent/>
    </Provider>
)

It works fine, but any time the parent component re-renders, a <Provider> does not support changingstoreon the fly error pops up in the console.

This is fixed by adding a shouldComponentUpdate, and returning false if this.props and nextProps are equal. But that prevents us from taking advantage of the stateless component pipeline.

I know it's not the idealized use of Redux to embed it within a React hierarchy instead of at the top level, so I'm not sure if this issue is considered worth addressing. But maybe it is.

@gaearon
Copy link
Contributor

gaearon commented Jan 21, 2016

I know it's not the idealized use of Redux to embed it within a React hierarchy instead of at the top level, so I'm not sure if this issue is considered worth addressing. But maybe it is.

It's totally fine, we should support this.

It works fine, but any time the parent component re-renders, a does not support changingstoreon the fly error pops up in the console.

Please see the relevant check. It's comparing identities. This means that the store prop is different on every render. This is problematic. (You're effectively recreating store and destroying its state—why?)

This is also a bit weird:

var ReduxRoot = props => (
    <Provider store={props}>
        <ReactReduxConnectedComponent/>
    </Provider>
)

Shouldn't that be more like

var ReduxRoot = props => (
    <Provider store={props.store}>
        <ReactReduxConnectedComponent/>
    </Provider>
)

?

@Zacqary
Copy link
Author

Zacqary commented Jan 21, 2016

This is also a bit weird:

Aha, that was the problem. Since the initial state of the store was the only data I needed to pass to ReduxRoot, it was basically:

<ReduxRoot {...initialStore}/>

So that meant props, even though it always had the same contents, was actually a new object being passed to <Provider>.

I fixed the problem by using your correction, except with destructuring

var ReduxRoot = ({store}) => {

for an ever-so-satisfying elimination of six characters.

I guess one way to avoid this error is if <Provider> could check to see if the store it's being passed is actually identical to its previous store, but that might add some unnecessary overhead.

@gaearon
Copy link
Contributor

gaearon commented Jan 21, 2016

Glad you found the problem! Yeah, we only rely on object identity there, something more complicated seems unwarranted.

@gaearon gaearon closed this as completed Jan 21, 2016
foiseworth pushed a commit to foiseworth/react-redux that referenced this issue Jul 30, 2016
…ctions

Handling private actions is an anti-pattern. Enforce it. (Fixes reduxjs#186)
@beebase
Copy link

beebase commented Aug 22, 2016

Sorry for being a bit off topic but I'm looking for a code example that demonstrates switching between different stores in react-native. This thread seems to touch the subject.
Specifically, I want to switch between users (log in/out) where each user has its own store.
Could you point me to any articles/tutorials available online.
Thanks a lot!

@jpamarohorta
Copy link

Sorry too for being off topic but I'm having the same issue as @beebase. I want to change between stores in a login/logout logic in react-native, and I'm not sure how can that be done. Have you found a solution @beebase ? Thanks!

@jimbolla
Copy link
Contributor

jimbolla commented Dec 9, 2016

You can't change the store passed to <Provider>, but you could render a new <Provider>. Give it a key and change it when you change the store.

@bchenSyd
Copy link

interesting...

never encountered a case where I need to create 2 stores and switch between them..
can't we put the switch inside store, and read different part from store state? nevertheless, I believe you have your own ground to have 2 stores

@totymedli
Copy link

totymedli commented Jan 10, 2017

Throwing in an answer for those who searched for the Error message in the title: Don't call createStore in the provider attribute!

Don't do this:

ReactDOM.render(
 <Provider store={createStore(reducers)}>
   <App/>
 </Provider>,
 document.getElementById('root')
);

The correct way is this:

var store = createStore(reducers);
ReactDOM.render(
 <Provider store={store}>
   <App/>
 </Provider>,
 document.getElementById('root')
);

@carslan
Copy link

carslan commented Jun 19, 2017

I had the same problem as given in the title but after the advise of @totymedli, i solved my problem. I am using react-native my approach for the solution was as advised;

const store = createStore(reducers);

export default class App extends React.Component {
  render() {
    return (
      
        <Provider store={store}>
          <View style={{ flex: 1 }}>
            <Header title="Tech Stack" />
            <LibraryList />
          </View>
        </Provider>
      
    );
  }
}

@reduxjs reduxjs deleted a comment from MmtBkn Nov 29, 2017
@reduxjs reduxjs deleted a comment from Mridul11 Mar 22, 2018
@Verten
Copy link

Verten commented Sep 7, 2018

Here is my solution:

// index.tsx
import createStore from './redux/createStore'
const store = createStore({})

const renderApp = () => {
  ReactDOM.render(
    <MuiThemeProvider theme={theme}>
      <Provider store={store}>
        <Router>
          <App />
        </Router>
      </Provider>
    </MuiThemeProvider>,
    document.getElementById('root'),
  )
}
renderApp()

if (module.hot) {
  // only need replace entry point
  module.hot.accept('./App', () => {
    renderApp()
  })
}

The one important thing is that should not replace the file which create the redux store. In my above example is the index.tsx.

gleb-lobastov added a commit to gleb-lobastov/mine that referenced this issue Jun 1, 2019
appId used to indicate for provider that store is changed. Otherwise it look like dispatch from previous store is passed: next store isn't affected by actions

reduxjs/react-redux#259 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants