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

Sync dispatch in componentDidMount() does not update component #208

Closed
roadhump opened this issue Jul 3, 2015 · 20 comments
Closed

Sync dispatch in componentDidMount() does not update component #208

roadhump opened this issue Jul 3, 2015 · 20 comments
Assignees
Labels

Comments

@roadhump
Copy link

roadhump commented Jul 3, 2015

I've tried v1.0.0-alpha and noticed that this small app https://gist.github.com/roadhump/f40483704e8527f11b07 will not work if I immediately dispatch action result from componentDidUpdate but it works if dispatch async (set const async to true). It worked both ways in version 0.12.

@taylorhakes
Copy link
Contributor

@gaearon I have confirmed this bug. In v1.0.0 the createConnector code

this.context.store.subscribe(::this.handleChange)

was moved from the constructor to the componentDidMount. This causes an issue with sync dispatches because the <Connector> mounts after the child components and the this.handleChange listener is not attached yet.

To fix this, we will have to move that code back to the constructor or earlier in the component lifecycle, so it will be called before the child components call dispatch.

@taylorhakes
Copy link
Contributor

Here is the related issue and pull request. #178 #179

@johanneslumpe
Copy link
Contributor

Wasn't this done to prevent setState etc when doing server-side rendering? @gaearon

Sent from my iPhone

On 06 Jul 2015, at 18:35, Taylor Hakes notifications@github.com wrote:

Here is the related issue and pull request. #178 #179


Reply to this email directly or view it on GitHub.

@taylorhakes
Copy link
Contributor

Yes, I linked to the pull request and issue

@johanneslumpe
Copy link
Contributor

@taylorhakes What i meant was that moving it back to the constructor or earlier in the lifecycle will possibly have negative implications for server-side rendering.

@taylorhakes
Copy link
Contributor

Correct. Another option is to document that componentDidMount dispatches need to be wrapped in setTimeout or another async timer

componentDidMount() {
  setTimeout(() => {
    this.props.dispatch(/* ... */);
  }, 0);
}

That approach feels very brittle though. Currently React's mounting is synchronous, but it might change in the future.

@clearjs
Copy link
Contributor

clearjs commented Jul 7, 2015

Can this behave differently depending on environment? E.g., using canUseDOM from react/lib/ExecutionEnvironment. Or am I missing something?

@johanneslumpe
Copy link
Contributor

@clearjs is that exported by default? If not, it should be treated as internal file and is off limits. If it's public though, it might work.

@clearjs
Copy link
Contributor

clearjs commented Jul 7, 2015

It's in the add-ons, so subject to more frequent changes. But the implementation is trivial:

var canUseDOM = !!(
  typeof window !== 'undefined' &&
  window.document &&
  window.document.createElement
); 

Obviously, this would only work in browser, not with react-native, etc.

@johanneslumpe
Copy link
Contributor

I think a solution which only works on the browser is not really acceptable though.

@clearjs
Copy link
Contributor

clearjs commented Jul 7, 2015

Sorry, my last comment was confusing. It does work on Node (returns false). Since for react-native server-side rendering isn't an option, this shouldn't be a problem. We'd also need to check whether the environment is react-native and behave like in browser if yes.

@clearjs
Copy link
Contributor

clearjs commented Jul 7, 2015

Here's a more robust alternative, and would work with react-native as expected: https://www.npmjs.com/package/detect-node. Would need to be memoized.

@johanneslumpe
Copy link
Contributor

@clearjs ok that detects node. But what about other environments like ruby? You can actually do server-side rendering there too. Or even with php. They will still execute in a JS context, but that will be V8 and not necessarily node I assume. In those cases this would still break I think.

@clearjs
Copy link
Contributor

clearjs commented Jul 7, 2015

Yep, you're right.

@taylorhakes
Copy link
Contributor

Another option. What about forcing the child components to mount after the Connector? Render the children only after componentDidMount on the Connector is called. No need for environment detection.

@taylorhakes
Copy link
Contributor

Forgot about server not calling componentDidMount, scratch that idea.

@gaearon
Copy link
Contributor

gaearon commented Jul 7, 2015

Can you please turn this into a failing test?

@gaearon
Copy link
Contributor

gaearon commented Jul 7, 2015

Also, can't we do something like

    componentDidMount() {      
      this.unsubscribe = this.context.redux.subscribe(::this.handleChange);

      const nextState = this.selectState(this.props, this.context);
      if (!this.isSliceEqual(this.state.slice, nextState.slice)) {
        this.setState(nextState);
      }
    }

@gaearon gaearon added this to the 1.0 milestone Jul 7, 2015
@gaearon gaearon self-assigned this Jul 7, 2015
@gaearon gaearon added the bug label Jul 7, 2015
@taylorhakes
Copy link
Contributor

I threw the example into a failing test. https://github.com/taylorhakes/redux/blob/failed-sync-dispatch/test/fullDispatch.spec.js

@gaearon I think your example will work, but I don't have time to test it right now.

@gaearon
Copy link
Contributor

gaearon commented Jul 9, 2015

Should be fixed in #195 via 406ca62.

@gaearon gaearon closed this as completed Jul 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants