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

Connect runs after componentWillUnmount #351

Closed
2j2e opened this issue Apr 13, 2016 · 11 comments
Closed

Connect runs after componentWillUnmount #351

2j2e opened this issue Apr 13, 2016 · 11 comments

Comments

@2j2e
Copy link

2j2e commented Apr 13, 2016

4.4.4 has a change that breaks my current solution.
I'm cleaning the state dispatching the action in the componentWillUnmount. It never caused Connect method invocation after unmount happend.

Next is how I'm using connect:

export default connect(state => ({
    state1: state.state1
    state2: state.state2
  }
}))(Component);

Tested on 4.4.1, 4.4.2, 4.4.3, 4.4.4

@gaearon
Copy link
Contributor

gaearon commented Apr 13, 2016

Ah, good point, thanks.

@gaearon
Copy link
Contributor

gaearon commented Apr 13, 2016

Hmm, no, I can’t reproduce it.

I added a test confirming setState() doesn’t get called when you dispatch from componentWillUnmount: 85982f6.

Do you mind providing a reproducible test case showing otherwise?

@2j2e
Copy link
Author

2j2e commented Apr 13, 2016

The issue can be reproduced when you have a child component which fetched by parent.
Initial state: data = null
Fetched state: data: { profile: {...} }
Clean state: data = null
Child connects to data.profile
I feel it's not a good way to use nullable object properties. But this was missed during code writing. With 4.4.4 this issue appeared. So do we have an issue on both sides? Or only my side?

@connect(null)
export default class Parent extends React.Component {
  componentWillMount() {
    this.props.dispatch(fetch());
  }

  componentWillUnmount() {
    this.props.dispatch(clean())
  }

  render() {
    return <Child />
  };
}

@connect(state => ({ profile: state.test.data.profile }))
class Child extends React.Component {
  render() {
    return (
      <div className="container">
        Hello {this.props.profile.test1} {this.props.profile.test2}
      </div>
    );
  };
}

Simple actions:

export function fetch() { return { type: 'fetch' } }
export function clean() { return { type: 'clean'} }

Reducers:

function initializeState() {
  return {
    data: null
  }
}

export default function test(state = initializeState(), action = {}) {
  switch (action.type) {
    case 'fetch':
      return Object.assign({}, state, {data: {profile: {test1: 'John', test2: 'Little'}}});
    case 'clean':
      return Object.assign({}, state, {data: null});
    default:
      return state;
  }
}

@gaearon
Copy link
Contributor

gaearon commented Apr 13, 2016

I’d really appreciate if you could fold this into a standalone project on GitHub. Thanks!

@gaearon
Copy link
Contributor

gaearon commented Apr 14, 2016

I tried adding this test based on your sample:

    it.only('whatever', () => {
      function reducer(state = { data: null }, action) {
        switch (action.type) {
          case 'fetch':
            return { data: { something: 42 } }
          case 'clean':
            return { data: null }
          default:
            return state
        }
      }

      @connect(null)
      class Parent extends React.Component {
        componentWillMount() {
          this.props.dispatch({ type: 'fetch '})
        }

        componentWillUnmount() {
          this.props.dispatch({ type: 'clean' })
        }

        render() {
          return <Child />
        }
      }

      @connect(state => ({ something: state.data.something }))
      class Child extends React.Component {
        render() {
          return null
        }
      }

      const store = createStore(reducer)
      const div = document.createElement('div')
      ReactDOM.render(
        <ProviderMock store={store}>
          <Parent />
        </ProviderMock>,
        div
      )
      ReactDOM.unmountComponentAtNode(div)
    })

For me, it fails consistently in 4.4.4, 4.4.3, and 4.4.0.

@gaearon gaearon added the bug label Apr 14, 2016
@gaearon
Copy link
Contributor

gaearon commented Apr 14, 2016

Able to reproduce with a slightly different test case. Looking into it.

@gaearon gaearon removed the bug label Apr 14, 2016
@gaearon
Copy link
Contributor

gaearon commented Apr 14, 2016

Should be fixed in 4.4.5.
Thank you for reporting.

@2j2e
Copy link
Author

2j2e commented Apr 14, 2016

Thank for the quick fix. Sorry that I was not able to make a test projects in time and you did it by yourself.

@gaearon
Copy link
Contributor

gaearon commented Apr 14, 2016

No problem.
I almost gave up but then figured out how to do it without giving up perf improvements in 4.4.4. 😄

@DmitryOlkhovoi
Copy link

DmitryOlkhovoi commented Jun 10, 2016

I don't understand what was changed but 4.4.4 and 4.4.5 breaks my current solution :)
The render function is calling and locale is fine when i dispatch setLocale, but FormattedMessage doesn't change the message. Looks like FormattedMessage doesn't get new props or something. It works on previous releases, and i did double check that issue reproduces only with react-redux 4.4.4 and 4.4.5
I will investigate the bug late, but for now here is my component

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { setLocale } from '../../actions/app';
import CSSModules from 'react-css-modules';
import { FormattedMessage, injectIntl } from 'react-intl';
import styles from './styles/index.local.css';

@CSSModules(styles, {
  allowMultiple: true
})
class SettingsPage extends Component {
  render() {
    const { locale, setLocale,
      intl: { formatMessage }
    } = this.props;

    return (
      <div styleName="wrapper">
        <div styleName="display-1 page-header">
          <FormattedMessage id="pages.settings.title"></FormattedMessage>
        </div>
        <form className="form-horizontal">
          <div className="form-group">
            <lable className="col-sm-1 control-label">
              <FormattedMessage id="pages.settings.language"></FormattedMessage>
            </lable>
            <div className="col-sm-2">
              <select ref="select" className="form-control"
                onChange={ () => setLocale(this.refs.select.value) }
                value={ locale }
              >
                <option value="en">English</option>
                <option value="ru">Русский</option>
              </select>
            </div>
          </div>
          <div className="form-group">
            <lable className="col-sm-1 control-label">
              <FormattedMessage id="pages.settings.theme"></FormattedMessage>
            </lable>
            <div className="col-sm-2">
              <select className="form-control">
                <option>
                  { formatMessage({id: 'pages.settings.theme.dark'}) }
                </option>
                <option>
                  { formatMessage({id: 'pages.settings.theme.light'}) }
                </option>
              </select>
            </div>
          </div>
        </form>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    locale: state.getIn(['app', 'locale']),
  }
}

export default connect(mapStateToProps, {
  setLocale,
})(injectIntl(SettingsPage));

@gaearon thx

@DmitryOlkhovoi
Copy link

DmitryOlkhovoi commented Jun 10, 2016

const mapStateToProps = (state, own) => {
  console.log(state.getIn(['app', 'locale']))
  return {
    locale: state.getIn(['app', 'locale']),
  }
}

and

render() {
    const { locale, setLocale,
      intl: { formatMessage }
    } = this.props;
console.log('locale', locale)

in both locale is 'ru', i'm changing 'en' to 'ru'

@reduxjs reduxjs deleted a comment from saxenanickk Apr 1, 2020
@reduxjs reduxjs locked as resolved and limited conversation to collaborators Apr 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants